Call Swift completion handler in objective c - ios

I am trying to call a swift method, which is implemented like this:-
#objc class DataAPI: NSObject {
func makeGet(place:NSString , completionHandler: (String! , Bool!) -> Void)
{
var str:String = ""
let manager = AFHTTPSessionManager()
manager.GET("https://api.com", parameters: nil, success:
{ (operation, responseObject) -> Void in
str = "JSON: \(responseObject!.description)"
print(str)
completionHandler(str,false) //str as response json, false as error value
},
failure: { (operation,error: NSError!) in
str = "Error: \(error.localizedDescription)"
completionHandler("Error",true)
})
}}
Now when I am trying to call it in my Objective C class, it is throwing an error "No Visible interface for DataAPI declares selector makeGet:completionHandler"
This is how I am calling the method in my Objective C class:-
[[DataAPI new] makeGet:#"" completionHandler:^{
}];

Try to clean and Rebuild to generate the "YourModule-Swift.h" again with all your changes.
Then it should be something like this:
[[DataAPI new] makeGet:#"" withCompletionHandler:^(NSString* string, BOOl b){
// your code here
}];
If you still getting that error, your "YourModule-Swift.h" file hasn't been generated correctly. Check it!

I see that in Swift the completion handler has two arguments: String and Bool whereas in your Objective-C call you pass a block without any arguments. I think it may be the cause of the error.
Try:
[[DataAPI new] makeGet:#"" completionHandler:^(NSString* string, BOOl b){
}];

You shouldn't use !(ImplicitUnwrappedOptional) keyword in closure. That is not allow bridging to ObjC code. just remove ! from closure.
func makeGet(place:NSString , completionHandler: (String! , Bool!) -> Void)
to
func makeGet(place:NSString , completionHandler: (String , Bool) -> Void)

Related

In Swift, how to set a function's return value in a closure?

I am now making a UITextField with AutoComplete that users will get a place name and zip code list after they have tapped one or some letters.
I got an Autocomplelt (https://github.com/cjcoax/Autocomplete) and there is a delegate function:
func autoCompleteItemsForSearchTerm(term: String) -> [AutocompletableOption]
I have to send a http request with term to server and wait for a json response as return.
For network connenction, I used Moya lib and its Method like:
CredentialProvider.request(.Autocomplete(term, 10)) { (result) -> () in }
Now, my question is: How can I make a return value after getting the response from server?
Thanks
Declare a function with completion block:
class func authenticateUser(userName:String?,password:String?,completionHandler: (response:NSDictionary?) -> (Void)) ->Void
{
completionHandler(response: nil(or)dict)
}
Call a function :
authenticateUser(emailId, password: password, completionHandler: { (response) in
print(response)
})

completion handler's error in swift 3 and Xcode 8

I have working project in Xcode 7.3 with swift 2.2 version. Now I have updated Xcode 8 and migrated to swift 3. Now my project contains errors specially for blocks like success block of afnetworking.
Which gives error as
Cannot convert value of type '() -> ()' to expected argument type '((URLSessionDataTask, Any?) -> Void)?'
I don't understand how to solve this to work as per swift 3.
And there is also same like error in Facebook login.
Which gives error as
Cannot convert value of type '(FBSDKLoginManagerLoginResult!, NSError!) -> Void' to expected argument type 'FBSDKLoginManagerRequestTokenHandler!'
and
Cannot convert value of type '(_, _, NSError!) -> Void' to expected argument type 'FBSDKGraphRequestHandler!'
This all errors are related to handler blocks in swift 3. I don't understand the errors and so that can't able to solve. Any help will be appreciated. Thanks in advance.
For facebook - the problem is in new Swift rules about converting objective-c function parameters into Swift.
Previously, if parameters in objective-c code did not have nullability attributes(like nonnull or nullable), Swift converts it with ! making them non optional(forced unwrapping). Now it convert it with ? making them optional. That why you are getting an error. Before you were putting as a callback for login:
(FBSDKLoginManagerLoginResult!, NSError!) -> Void
Now you need to put:
(FBSDKLoginManagerLoginResult?, Error?) -> Void
Also, as you see, now you will not see NSError class. Instead of that Swift will put Error.This is also new rule. Now all "NS" prefixed in class names is removed in Swift(NSObject -> Object; NSError -> Error).
Example of working code for facebook login in Swift 3.0:
let manager = FBSDKLoginManager()
manager.logIn(withReadPermissions: ["public_profile"], from: self.controller) {
(loginResult: FBSDKLoginManagerLoginResult?, error: Error?) in
}
Example of working code for facebook request in Swift 3.0:
let request = FBSDKGraphRequest()
request.start {
(connection: FBSDKGraphRequestConnection?, result: Any?, error: Error?) in
}
As you see, now it is using Any type instead of objective-c id. In Swift 2.2 it was using AnyObject. It is also new Swift converting rule.
You do not need to specify callback parameters type. I did that in code for highlighting their real types. So you can just write code without them:
let manager = FBSDKLoginManager()
manager.logIn(withReadPermissions: ["public_profile"], from: self.controller) { (loginResult, error) in }
let request = FBSDKGraphRequest()
request.start { (connection, result, error) in }
But you need to remember that they are optional now.
In conclusion some converting rules that may affect you callback code:
Closure parameters are optional if in objective-c are not specified nullability attributes
All "NS" prefixes is removed for objective-c classes in Swift
If objective-c function had id parameter, in Swift 3.0 it will have type Any instead of AnyObject
Though I didn't know the error before that what Xcode want to inform me about the error, but I have removed type specification with object and it worked.
As
manager.post(methodname, parameters: param, progress: nil, success:{ (dataTask, responseObj) in
if let dict : NSDictionary = responseObj as? NSDictionary {
print("Response of \(methodname) : \(dict)")
if dict.object(forKey: "response") as? String == "success" {
CompletionHandler(true, dict)
} else {
CompletionHandler(false, dict)
}
}
})
Here with respect to question error is given at dataTask and responseObj which are with type specified. After removing type it worked fine.
Same as with facebook login
#IBAction func fbLoginClicked(_ sender: AnyObject) {
let app = UIApplication.shared.delegate as! AppDelegate
app.fbLoginManager = FBSDKLoginManager()
app.fbLoginManager.logOut()
app.fbLoginManager.loginBehavior = FBSDKLoginBehavior.native
app.fbLoginManager.logIn(withReadPermissions: ["email"], from: self, handler: { (result, error) -> Void in
if error != nil {
print(error?.localizedDescription)
} else {
if (result! as FBSDKLoginManagerLoginResult).isCancelled == true {
} else {
self.fetchFacebookUserDetail()
}
}
})
}
Here also I have removed type specification of result and error and problem solved. And followed this in whole app and it worked. I can run the project without error and also it is working. Thanks.

Conforming Swift Callback With An Objective-C NSDictionary

Our app uses a Push Plugin: https://github.com/phonegap/phonegap-plugin-push
One thing the Push Plugin does is swizzle the AppDelegate.m file. Since our AppDelegate file is a .swift, I need to import the core functionality instead.
However, I am running into a problem converting a Swift function to go inside of the NSDictionary Object it expects.
Here is the code I am converting:
void (^safeHandler)(UIBackgroundFetchResult) = ^(UIBackgroundFetchResult result){
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(result);
});
};
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithCapacity:2];
[params setObject:safeHandler forKey:#"handler"];
PushPlugin *pushHandler = [self getCommandInstance:#"PushNotification"];
pushHandler.handlerObj = params;
So the first thing I did was convert the (^safeHandler) to Swift as such:
let safeHandler: (UIBackgroundFetchResult) -> () =
{
result in dispatch_async(dispatch_get_main_queue())
{
completionHandler(result)
}
}
Next I need create the NSMutableDictionary object that gets set as the handlerObj on pushHandler.
Putting safeHandler directly into the dictionary throws the error
(UIBackgroundFetchResult) -> () does not confirm to AnyObject
protocol.
So I tried changing the dictionary type to be [NSObject : closure]() by doing the following:
typealias closure = (UIBackgroundFetchResult) -> Void
var params:Dictionary<String, closure> = Dictionary<String, closure>()
params["handler"] = safeHandler
But when I try to assign the object to the pushHandler
pushHandler.handlerObj = params
I get the error again
Cannot assign value of type 'Dictionary' (aka
'Dictionary ()>') to type
'[NSObject : AnyObject]!'
So back to square one. It seems in Objective-C passing in functions is OK but not so with Swift?
Do you know how I can workaround this issue and get the function into the [NSObject: AnyObject] the Objective-C code is expecting?

Code formatting in Swift to use the UIAccessibilityRequestGuidedAccessSession function

I'm new to Swift code formatting and I'm trying to figure out how to use the UIAccessibilityRequestGuidedAccess function. Here's how the function is declared in Swift:
func UIAccessibilityRequestGuidedAccessSession(_ enable: Bool,
_ completionHandler: ((Bool) -> Void)!)
In Objective-C I would have used it as follows:
UIAccessibilityRequestGuidedAccessSession(YES, ^(BOOL didSucceed) {
if (didSucceed ) {
...
} else {
...
}
Any help with the syntax is appreciated.
You do it like this:
UIAccessibilityRequestGuidedAccessSession(true){
success in
println("Request guided access success \(success)")
}
YES is not valid in swift (use true/false), and if the last parameter of a function is a closure, you can use trailing closure syntax as shown above. success is the argument passed into the completion block.

Swift Error - extra argument 'option' in call for SimpleAuth

Hi I have been battling with a swift error as I cannot match the arguments of an objective-c method
SimpleAuth.authorize("instagram",
options: ["scope" : "likes"], completion: {
(responseObject : NSDictionary!, error : NSError!) -> Void in
self.accessToken = responseObject["credentials"]["token"]
......
})
//error Extra argument 'options' in call
Declared as:
+ (void)authorize:(NSString *)provider options:(NSDictionary *)options completion:(SimpleAuthRequestHandler)completion;
Code completion is :
SimpleAuth.authorize(<#provider: String!#>, options: <#[NSObject : AnyObject]!#>, completion: <#SimpleAuthRequestHandler!##(AnyObject!, NSError!) -> Void#>)
I have tried type casting, down casting, declaring as a separate variables, but still cant get it to work.
Any ideas, will be much appreciated
It's your definition of your completion block. SimpleAuthRequestHandler is defined as:
typedef void (^SimpleAuthRequestHandler) (id responseObject, NSError *error);
But your completion block/closure is defined as:
(responseObject : NSDictionary!, error : NSError!) -> Void in
You can't just change the type from id (AnyObject in Swift) to NSDictionary! without explicitly casting it. Your call should look something like this:
SimpleAuth.authorize("instagram", options: ["scope" : "likes"], completion: {
(responseObject : AnyObject!, error : NSError!) -> Void in
/* ... */
})
You can then make responseObject an NSDictionary with a cast:
var response = responseObject as NSDictionary

Resources