How to use completionHandler Closure with return in Swift?
Asked Answered
C

6

23

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

Cotonou answered 9/7, 2014 at 7:12 Comment(7)
Why do you want to return the data? Where should it return to? It's unclear what you're trying to do — the completion handler block probably isn't supposed to return anything.Anthropocentric
what purpose do you like to send anything back to the caller for? your completion handler ends the procedure here.Mastat
@Anthropocentric I need to make a GET request using Coinbase's API, and then extract data from the JSON object that gets returned. Right now, the completion block runs and returns the JSON, but the response data is lost once the block ends. I need to create a String object using some of the data from the JSON response object and have it persist beyond the block's life. From the URL Session docs I think that I have to implement some NSURLSessionDataDelegate protocols which fire once the response is returned. Is this correct? Though I'd rather just use a simple completion block to return things.Cotonou
The block is executed when the response is returned, that's the point. Why don't you just do the work inside the block?Anthropocentric
Becuase that's poor code separation. I want this function to create a request, add the correct headers, and then send the request off, and have it work for multiple different URL's that I use. If I do the work I need to do in the completion block, then the block will be huge as I'll have to include a lot of parsing for what type of response I received from the different requests. I guess my concept of how to send a get request is fundamentally wrong, because right now it seems obvious to me that I could get the response data as a variable, and have it persist outside of the completion block.Cotonou
You can certainly put the code in a separate function which you call from inside the completion block. The point is that the completion block is the designated (and only) way to get the response data if you don't want to use the delegate based APIs.Anthropocentric
@Cotonou Did you ever figure out how to start executing code within the function after the completion block finishes running without requiring the completion block to return a closure back to the function?Kermanshah
C
34
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
    }
}
Cristen answered 10/7, 2014 at 4:16 Comment(2)
I'm confused shouldn't you be feeding request to the getSomething otherwise this function will always have the same request because it's not taking it in as a parameter...Also can you explain what the issue is, possibly add some commentsTorras
@Honey with the callback you're only really interested in the response that's returned from the service. If you want the request for whatever reason then just return the request object.Cristen
M
3

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.

Mastat answered 9/7, 2014 at 9:55 Comment(0)
D
2

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!
Danseur answered 9/7, 2014 at 9:1 Comment(0)
R
1

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

Rosenquist answered 26/11, 2021 at 13:4 Comment(0)
B
0

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.

Basanite answered 28/11, 2015 at 4:58 Comment(0)
R
0
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

Rosenquist answered 26/11, 2021 at 12:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.