almel
almel

Reputation: 7968

How to use completionHandler Closure with return in Swift?

I am trying to us a RESTful API that returns some json data. I want to encapsulate the code that creates the HTTP Request and sets the headers in its own method so I can call it by entering a url String and then have the method return a JSON object.

In the following snippet of code, I have already created the request object and set the headers, and I call that variable "req". I have not declared any objects named data, response, or error. I have the following code that correctly prints out a JSON object

let sesh = NSURLSession.sharedSession()
    let dataTask = sesh.dataTaskWithRequest(req, completionHandler: {(data, response, error) in
        var jsonError : NSError?
        let jsonBlob = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableLeaves, error: &jsonError)
        println(jsonBlob)
        });

    dataTask.resume()

So here's my question. How do I make it so that this completionHandler block is able to return the jsonBlob, which is of type "AnyObject!"? If I modify the code slightly to be the following:

let sesh = NSURLSession.sharedSession()
    let dataTask = sesh.dataTaskWithRequest(req, completionHandler: {(data, response, error) -> AnyObject! in
        var jsonError : NSError?
        let jsonBlob : AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableLeaves, error: &jsonError)
        return jsonBlob
        });

    dataTask.resume()

then the program will not compile as the call to dataTaskWithRequest:completionHandler gives a compiler warning saying:

 Could not find an overload for 'dataTaskWithRequest' that accepts the supplied arguments

I don't understand this. I'm using the correct syntax for returning closures, as is given in this page of the Swift Docs:

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html

Upvotes: 23

Views: 35437

Answers (6)

Pavnesh Singh
Pavnesh Singh

Reputation: 67

completionHandler Closure with return in Swift

func checkPassword(pass:String,com:@escaping(Bool)-> String){
    if pass.count < 5{
        let pass = com(false)
        print("Pleas check your password :\(pass)")
    }else{
        let pass = com(true)
        print("You can use this password : \(pass)")
    }
}

let password = "test@1"

checkPassword(pass:password ) { (status) in
    if status{
        print("Strong Password")
    }else{
        print("weak Password")
    }
    return password
}

Output ###

Strong Password

You can use this password : test@1

Upvotes: 2

Pavnesh Singh
Pavnesh Singh

Reputation: 67

func checkPassword(pass:String,com:@escaping(Bool)-> Void){
    if pass.count < 5{
        com(false)
    }else{
        com(true)
    }
}

let password = "test@1"

checkPassword(pass:password ) { (status) in
    if status{
        print("Strong Password")
    }else{
        print("weak Password")
    }
}

Output###

Strong Password

Upvotes: 0

Ben Carroll
Ben Carroll

Reputation: 138

I came across a similar issue when trying to update info in a central DB based on content was pulling from CloudKit. The patterns for pulling CK data all have these asynchronous completion handlers which allow no return value.

Since I wanted to be able to re-use similar code in different contexts, I separated the CK calls out into their own class and defined a delegate protocol that I made the core class conform to. Within the completion handlers I sent data retrieved from the CK calls back to where I want them stored via delegate methods.

Easy peasy.

Upvotes: 0

gotnull
gotnull

Reputation: 27224

func getSomething(callback: (Array<AnyObject>) -> ()) {
    var dataTask = NSURLSessionDataTask()
    dataTask = session.dataTaskWithRequest(request) { (data, response, error) in
        if (error == nil) {
            var callbackArray = Array<MyObject>()
            let responseDict = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: nil) as NSDictionary
            let response = responseDict.objectForKey("response_key") as NSDictionary
            let array = response.objectForKey("array_key") as NSArray

            for item: AnyObject in array {
                var arrayItem = MyObject(dict: item as NSDictionary)
                callbackArray.append(arrayItem)
            }

            callback(callbackArray)
        } else {
            // handle an error
        }
    }
    dataTask.resume()
}

Then you could do something like:

getSomething() { (response) in
    if let responseArray = response as? Array<MyObject> {
        self.somethings = responseArray
    }
}

Upvotes: 34

holex
holex

Reputation: 24041

as you see here, the dataTaskWithRequest:completionHandler: has a completion handler with no expected return value.

completion handler's interface

that means the NSURLSession instance do not expect any value from you to proceed after calling this method; with other words: you completion closure (or block, if you like) ends the procedure here.

there is not clear why you'd like sending back anything to the caller via the completion handler.

Upvotes: 3

Skyte
Skyte

Reputation: 529

The completion handler can't return anything because the closure you have to supply has to be of return type Void and not AnyObject!.

func dataTaskWithRequest(_ request: NSURLRequest!,
   completionHandler completionHandler: ((NSData!,
                              NSURLResponse!,
                              NSError!) -> Void)!) -> NSURLSessionDataTask!

Upvotes: 2

Related Questions