Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

Snapshot for error in afnetworking post method

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.

Facebook Login error

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.

like image 607
Max Avatar asked Sep 15 '16 10:09

Max


2 Answers

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:

  1. Closure parameters are optional if in objective-c are not specified nullability attributes
  2. All "NS" prefixes is removed for objective-c classes in Swift
  3. If objective-c function had id parameter, in Swift 3.0 it will have type Any instead of AnyObject
like image 151
Vasyl Khmil Avatar answered Nov 09 '22 14:11

Vasyl Khmil


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.

like image 13
Max Avatar answered Nov 09 '22 12:11

Max