Kodeco Forums

Video Tutorial: Networking With NSURLSession Part 4: Delegate vs. Closures

Learn how the NSURLSession system uses either delegate methods or closures to communicate progress and completion.


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/3759-networking-with-nsurlsession/lessons/5

I would absolutely LOVE to see this series taken in a different direction, which is the study of completion handlers/closures and blocks. Not only how to work with existing ones from the iOS APIs but creating your own as you do for the photo fetching app!

I understand the focus here is NSURLSession but the inevitable use of closures and blocks generates a natural digression. :slight_smile:

Why cant background tasks use completion handlers? Because the code isnt running if the app is in the background?

Thanks for the feedback.

I think the main reason completion handlers can’t be used for background tasks is the architecture that Apple uses for those tasks. It uses a separate system process to run the background tasks and then calls back to the delegate on completion.

Hi Jerry, quick question regarding the demo app FlickrFeed

  1. In the NetworkClient class, method getURL, why don’t you check the status code of the response before calling parseJSON ?

  2. Why do you bridge your JSON to an NSDictionary in your parseJSON ? You could have cast it with a swift dictionary

I usually parse ODATA JSON response like that (for a response that contains an array)

let array = json[“d”]?[“results”] as? [[String: AnyObject]]

thanks

Hi Jerry,
i’m interested in the scenario where the first closure gets a JSON which in turn has an image’s URL. I’d then call a second DownloadDataTask inside this closure and get the result in another closure. Would this nesting of DataTasks and closures the recommended pattern is this situation?

Cheers

In didCompleteWithError: why do we get the completion handler back from the dictionary? Couldn’t we just call completion(nil,error)? Is it because in some different scenario we might have different completion handlers passed in from different callers?

  1. Yeah, you probably should check the status code of the response first and most APIs will handle authentication errors too.

  2. I don’t recall, but I ran into some issue when I was trying to cast to a swift dictionary. I seem to remember that it felt like a beta bug at the time, but I’m not seeing it now.

1 Like

Sure, no problem nesting the calls, but you probably want to keep the knowledge of the structure of the data separate from the object that gets and parses the data. Here, NetworkClient is responsible for the transfer and parsing of data and managing the session settings. Then Photo+Services is the object that knows more about the structure of the data and how to create Photo objects out of it. In your case, you could have a NetworkClient pass the parsed data back to an Image class, which knows where in the returned data to find the URL and kick off the second request. The other option would be to wrap the session tasks in NSOperation subclasses so you could manage the dependencies and queues more easily. See the Concurrency video series for an example of managing dependencies this way.

In the cases where we weren’t using the delegate methods, you could call completion directly because it was passed in as a parameter - even in the parseJSON(_:completion:) method, we passed along the completion block to call. Because background transfers use the delegate callbacks, you have to store the completion blocks that you want to call - they can’t be passed in to the delegate callback.

Oh right, because the delegate callback is a predefined f(x). Sorry, I made a mess of my project and confused it with the getImageInBackground() where we do use a completion block:

func getImageInBackground(url: NSURL, completion: ImageResult?) -> NSURLSessionDownloadTask {
completionHandlers[url] = completion
let request = NSURLRequest(URL: url)
let task = backgroundSession.downloadTaskWithRequest(request) task.resume()
return task
}

but thats because here we store it into the dictionary. We actually call it in didCompleteWithError:

Thanks