I am trying to call a Swift function that contains a completion handler in an objective C class, but I am not sure how to implement it.
This is my Swift Code
#objc class textToSpeech:NSObject{
func toSpeech(word: NSString, sucess:()->Void) -> NSURL {
let tempDirectory = NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
let tempFile = tempDirectory.URLByAppendingPathComponent((word as String) + ".wav")
let tts = TextToSpeech(username: "xxxxxx", password: "xxxxxx")
tts.synthesize(word as String,
voice: SynthesisVoice.GB_Kate,
audioFormat: AudioFormat.WAV,
failure: { error in
print("error was generated \(error)")
}) { data in
data.writeToURL(tempFile, atomically: true)
print("createdURL")
print(tempFile)
sucess();
}
return tempFile
}
How would I write the function call in objective c. I have already completed setting up the project so that I can call swift functions from objective c.
For example you have this code:
#objc class PDTextToSpeech: NSObject{
func toSpeech(word: NSString, success: () -> Void) -> NSURL {
// ...
return NSURL()
}
}
So you could easily bridge you Swift code in obj-c with #import "<ModuleName>-Swift.h"
where you project name.
Then you can call:
[[PDTextToSpeech new] toSpeech:#"String" success:^{
NSLog(#"Success");
}];
I was using PDTextToSpeech as class name, because it's preferable to call classes in obj-c with uniq prefix. If you project called TestProject - you can use TP prefix.
I guess it should look like this:
textToSpeech* text = [[textToSpeech alloc] init];
[text word:#"some text" sucess:^{
NSLog(#"success");
}];
Related
I have a Swift app, with some Objective-C code mixed in. It was working yesterday, but this morning I updated XCode and now everything had gone to hell!
Firstly, after updating, I clicked the XCode popup box to allow it to upgrade my app to Swift4. This is where the problems started.
I have a Swift class called RestClient with the following 4 functions (among others):
class func getInstance() -> RestClient {
if (gRestClient == nil) {
let prefs:UserDefaults = UserDefaults.standard
return RestClient(username: prefs.string(forKey: "username")!, password: prefs.string(forKey: "password")!)
}
return gRestClient!
}
class func getUsername() -> String {
if (gUsername == nil) {
let prefs:UserDefaults = UserDefaults.standard
gUsername = prefs.string(forKey: "username")!
}
return gUsername!
}
class func getPassword() -> String {
if (gPassword == nil) {
let prefs:UserDefaults = UserDefaults.standard
gPassword = prefs.string(forKey: "password")!
}
return gPassword!
}
public func getServer() -> String {
return MAIN_SERVER;
}
Then in my /Objective-C/ folder, I have some more files, once of which is called RestClientObj.m. In here, I have this lines of code:
NSString* url = [NSString stringWithFormat:#"%#/receipt/email/%#/%#/", [[RestClient getInstance] getServer], rrn, emailAddress];
NSString *authStr = [NSString stringWithFormat:#"%#:%#", [RestClient getUsername], [RestClient getPassword]];
So as you can see, I'm calling the RestClient.swift from here. The RestClientObj.h is as follows:
#ifndef RestClientObj_h
#define RestClientObj_h
#endif /* ResClientObj_h */
#interface RestClientObj : NSObject {
}
+(BOOL) sendSMS:(NSString *)rrn mn:(NSString *)mobileNumber;
+(BOOL) sendEmail:(NSString *)rrn mn:(NSString *)emailAddress;
#end
This whole upgrade is causing other problems too. I have another class with the following error:
No visible #interface for 'AppDelegate' declares the selector 'managedObjectContext'
on the line:
appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext]; <-ERROR
Can anyone shed any light on this?
EDIT: Here's some code from the AppDelegate class:
lazy var managedObjectContext: NSManagedObjectContext? = {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
if coordinator == nil {
return nil
}
var managedObjectContext = NSManagedObjectContext()
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
Just to close this off.
The issue was missing the #obj identifier before the variable declaration to make it visible to my objective-c code, in combination with the XCode Swift Upgrade wizard renaming some functions.
I must present this completion handler in Swift as variable:
-test:(void(^)(id result, NSError* error))completion;
What must this look like in Swift? Is in this case id == AnyObject and NSError == Error?
id is imported into Swift as Any, NSError * becomes Error? in Swift 3 so your completion handler should read like this in Swift:
func completionHandler(result: Any, error: Error?) {
// Do something
}
obj.test(completionHandler)
Or if you want to define it inline:
let handler = { (result: Any, error: Error?) -> Void in
// Do something
}
obj.test(handler)
1) I've managed to communicate from apple watch to iOS app using a complete swift project (iOS app in swift and Apple Watch target in swift)
Code:
In InterfaceController.swift
#IBAction func buttonPressed() {
let watchMessage = ["SiteName" : "Tech-recipes"]
WKInterfaceController.openParentApplication(watchMessage, reply: { (reply:[NSObject : AnyObject]!, error: NSError!) -> Void in
if let message = reply["Message"] as? String{
println(message)
}
})
}
in AppDelegate.swift
func application(application: UIApplication!, handleWatchKitExtensionRequest userInfo: [NSObject : AnyObject]!, reply: (([NSObject : AnyObject]!) -> Void)!) {
if let siteName = userInfo["SiteName"] as? String{
reply(["Message":"Hello from \(siteName)"])
}
}
Output: "Hello from Tech-recipes"
2) However, when I want to integrate the apple watch target in swift to the exiting project in obj-c, it will crash and give me this error: "fatal error: unexpectedly found nil while unwrapping an Optional value"
In InterfaceController.swift
#IBAction func buttonPressed() {
let watchMessage = ["SiteName" : "Tech-recipes"]
WKInterfaceController.openParentApplication(watchMessage, reply: { (reply:[NSObject : AnyObject]!, error: NSError!) -> Void in
if let message = reply["Message"] as? String{ //CRASH HERE!!!!
println(message)
}
})
}
iPhoneAppDelegate.m
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply{
// Performs task on phone here
// Sending back a reply
if ([[userInfo valueForKey:#"SiteName"] length]>0) {
//NSMutableDictionary *reply = [[NSMutableDictionary alloc] init];
[reply setValue:[NSString stringWithFormat:#"Hello from %#", [userInfo valueForKey:#"SiteName"]] forKey:#"Message"];
}
}
Update:
--> Noticed that in handleWatchKitExtensionRequest: method the type for userInfo is different in swift and obj-c, in [NSObject : AnyObject]! and NSDictionary respectively. How to solve this?
--> Got an error in error: NSError!: [0] (null) #"NSLocalizedDescription" : #"The UIApplicationDelegate in the iPhone App never called reply() in -[UIApplicationDelegate application:handleWatchKitExtensionRequest:reply:]"
Solved it!
Missed out the reply(aNsdictionary) in the app delegate
I'm having trouble converting this objective C to swift blocks
you can see here Code on Github, but i don't have any background in objective C, so i don't really understand this block, here is the code
- (LinkedStringTapHandler)exampleHandlerWithTitle:(NSString *)title
{
LinkedStringTapHandler exampleHandler = ^(NSString *linkedString) {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:title
message:[NSString stringWithFormat:#"Handle tap in linked string '%#'",linkedString]
delegate:nil
cancelButtonTitle:#"Dismiss"
otherButtonTitles:nil, nil];
[alert show];
};
return exampleHandler;
}
you can see this code on github https://github.com/evilBird/HBVLinkedTextView/blob/master/HBVLinkedTextViewExample/HBVLinkedTextViewExample/ViewController.m , take a look at that code, Im trying to use it in swift using bridging header. Everything i converted but this line i dont understand.
LinkedStringTapHandler exampleHandler = ^(NSString *linkedString)
this is what i have done so far
func exampleHandlerWithTitler(title:NSString)->LinkedStringTapHandler {
exampleHandler: LinkedStringTapHandler = (linkedString:NSString) -> () {
// alert view code here
}
return exampleHandler (ERROR here Use of unresolved identifier exampleHandler)
}
Where Objective-C has blocks, Swift has functions. So it's just a matter of knowing how to read Objective-C block syntax - not easy, I know:
^(NSString *linkedString)
That means: "This is a block taking an NSString parameter (and returning nothing)." So you want to supply here a Swift function that takes an NSString parameter (and returns nothing).
In other words, the definition of LinkedStringTapHandler is equivalent to saying this in Swift:
typealias LinkedStringTapHandler = (linkedString:NSString) -> ()
So in your code you would form a function of that type and return it:
func f(linkedString:NSString) -> () {
// do stuff
}
return f
You could do the same thing with an anonymous function but that's neither here nor there, really.
finally this is working
func exampleHandlerWithTitler(title:NSString)->LinkedStringTapHandler {
let exampleHandler: LinkedStringTapHandler = {(linkedString:String) in {
// alert view code here
}
return exampleHandler
}
I am trying to use a library which is written in objective c in my swift application.
I tried to translate a snippet from the readme to swift code - But I get a type error I don't understand.
Obj.C code from readme:
[self.client logonWithUsername:self.username.text password:self.password.text responseCallback:^(NSDictionary *response, NSError *error) {
if (error) {
[self handleFailedAuth:error];
return;
}
[self handleSuccessfulAuth];
}];
My translation to swift:
var username = NSUserDefaults.standardUserDefaults().stringForKey("username")
var password = NSUserDefaults.standardUserDefaults().stringForKey("password")
client.logonWithUsername(username, password: password, responseCallback: {
(response: NSDictionary, error: NSError) in
if(error){
handleFailedAuth(error)
return;
}
handleSuccessfulAuth()
}
)
I Get [NSObject: AnyObject]! is not a subtype of NSDictionary on the line where the parameters of the closure are defined. How is that possible? I am using the same types as in the example.
Your Swift should probably read the following:
var username = NSUserDefaults.standardUserDefaults().stringForKey("username")
var password = NSUserDefaults.standardUserDefaults().stringForKey("password")
client.logonWithUsername(username, password: password, responseCallback: {
(response: NSDictionary?, error: NSError?) in
if(error){
handleFailedAuth(error!)
return;
}
handleSuccessfulAuth()
}
)
This is because Swift optionals in some ways replace the way you used to pass nil in objective-c. So because the NSDictionary might be nil and the NSError might be nil, you put a ?-mark after them, then conditionally unwrap w/ a !-mark inside the block when you need to access the value of that
You're explicitly specifying the block/closure parameter types in Swift, and the Swift compiler does not have enough information about the NSDictionary. This is because the Swift Dictionary is more strongly typed than the Objective-C NSDictionary.
The error message says (admittedly, pretty cryptically) that the exact type Swift is expecting is a Dictionary<NSObject, AnyObject>!.
There are a couple of ways to solve this. One is to be more explicit about your NSDictionary parameter in the Swift closure definition:-
client.logonWithUsername(username, password: password, responseCallback: {
(response: Dictionary<NSObject, AnyObject>!, error: NSError) in
// ... handler body
}
)
A somewhat easier way is to not try to tell Swift about the types at all and let the compiler infer what it needs:-
client.logonWithUsername(username, password: password, responseCallback: {
response, error in
// ... handler body
}
)