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

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. Commented Jul 9, 2014 at 7:15
  • what purpose do you like to send anything back to the caller for? your completion handler ends the procedure here. Commented Jul 9, 2014 at 9:59
  • @jtbandes 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. Commented Jul 9, 2014 at 18:23
  • The block is executed when the response is returned, that's the point. Why don't you just do the work inside the block? Commented Jul 9, 2014 at 18:26
  • 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. Commented Jul 9, 2014 at 18:47

6 Answers 6

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
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

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 comments
@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.
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.

Comments

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!

Comments

2

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

Comments

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.

Comments

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.