NSURLSession and NSURLConnection Compatibility - ios

NSURLSession is a refactoring of NSURLConnection and introduces some new features. I've been working on an issue with an app that uses both, and I'm wondering if this is OK. Or is it necessary to abandon NSURLConnection methods completely when adding NSURLSession?
Specifically, what's happening is that, seemingly randomly, a request is made and nothing is returned in response. If the webView is asked [webView isLoading], the answer is YES, however, neither connectionDidFinishLoading: nor URLSession:didBecomeIvalidWithError: nor webView:didFailLoadWithError: get called. Additionally, asking the web view to stop loading using [webView stopLoading] does not seem to work, since [webView isLoading] seems to continue to be YES.
If any of this sounds familiar, perhaps someone could give me suggestions of something to try. I'm running out of ideas. If NSURLConnection cannot safely be used while NSURLSession is being used, I'll just have to refactor to no longer use NSURLConnection, but I'd rather not if I don't have to or if doing so doesn't solve the problem.

Sounds to me like you are having threading issues. The difference with NSURLSession is the delegates fire on a background thread, so any direct UI updates from those methods won't work, you are expected to transfer back to the main thread first. Unless you are doing some heavy uploading or downloading you might not need that feature. To regain the original main thread design of NSURLConnection you can create a NSURLSession config with the main operation queue (means main thread):
[NSURLSession sessionWithConfiguration:nil delegate:nil delegateQueue:[NSOperationQueue mainQueue]];
Now all your delegates will be main thread. You can verify this with [NSThread isMainThread] then you are good to update the UI as you like.

Related

How do I cancel a download with AFHTTPSessionManager?

My app downloads information using a subclass of AFHTTPSessionManager and GET. The user may opt out of the download and I am trying to figure out how to cancel the download.
I came across this code:
- (void) cancel {
[self.operationQueue cancelAllOperations];
}
But that didn't seem to work, since the download completed.
I then tried:
- (void) cancel {
for (NSURLSessionTask *task in self.tasks) {
[task cancel];
}
}
This did seem to interrupt the download, but I am not sure if this is the proper way to do this. The docs are not very helpful for this (or I haven't found it yet).
So, is the above the correct way to cancel my download? Do I need to do more cleanup?
You certainly do not want to use cancelAllOperations. That is fine with operation-based AFHTTPRequestOperationManager requests, but not the task-based AFHTTPSessionManager requests. Sadly, AFHTTPSessionManager does not use operations. I understand why (wrapping the tasks in operations is surprisingly complicated given the fact that NSURLSessionTask delegates are specified at the session level (!), it's incompatible with background sessions, etc.), but it's too bad, as it significantly diminishes the value of AFHTTPSessionManager, IMHO. Mattt once said that he was going to remedy this, but it's never been released.
Your second approach of canceling the individual tasks is fine. You could also call invalidateSessionCancelingTasks, but you'd presumably have to re-instantiate the session (or the entire AFHTTPSessionManager) when you want to initiate more requests.

iOS - Perform synchronous HTTP request with AFNetworking

I'm currently migrating my app from using ASIHTTPRequest to AFNetworking. I know synchronous requests are to be avoided if possible, but in my app I have just 1 place where I use one. In the app delegate applicationDidEnterBackground() method I need to run a HTTP request to query data to set the application badge number when the app goes to background. When run asynchronously this rarely works because the method just drops out and the application is suspended before the HTTP request completes. With ASIHTTPRequest I used the [request startSynchronous] method and this meant the HTTP request completed 100% every time and the badge was updated correctly. I see with AFNetworking there is no obvious synchronous option, so can anybody suggest a way it can be used synchronously, or an alternative solution?
Have found the way to do this - using one of the AFHTTPRequestOperation derivatives there is the method - setShouldExecuteAsBackgroundTaskWithExpirationHandler: which gives the operation extra time to complete before the OS suspends the app. So my code is :
[operation setShouldExecuteAsBackgroundTaskWithExpirationHandler:nil];
[operation start];
This was just what I needed and has solved my problem.
Jonathan
UPDATE: As Bob has pointed out using this method does not actually perform the operation synchronously, but was the ideal solution in my case in the absence of a synchronous method.

NSURLConnection synchronous request from a thread vs asynchronous request

What is the differance between adding a operation which make a synchronous NSURLConnection request in NSOperationQueue ( or synchronous request from a thread ( not main thread)) AND making a asynchronous request from the main thread ?
Both will not block main thread so UI will remain responsive but is there any advantage of using one over other? I know in later method i can track request progress etc but assume that progress and other HTTP stuff is not important here.
They are very similar. The biggest problem with synchronous requests is that they can't easily be cancelled. Depending on your application, that could be a problem. Imagine you are downloading a big document and the user moves to another screen so you no longer need that information. In our case, I actually chose doing asynchronous NSURLConnections on a secondary NSThread, which may be overkill for some apps. It is more complicated, but it gives us the ability to both cancel requests and to decode the JSON/XML/image data on secondary threads so they don't impact main thread user interactivity.
Asynchronous requests are scheduled on the run loop and setup as a run loop source, triggering the code automatically only when there is data received from the network (as any socket source).
Synchronous requests running on a NSThread monopolizes a thread to monitor the incoming data, which is in general quite overkill.
You can always cancel an NSURLConnection even if it has been executed asynchronously, using the cancel method.
I bet using the new API that allows to send an asynchronous request on an NSOperationQueue (+sendAsynchronousRequest:queue:completionHandler:) uses GCD under the hood and dispatch_source_create, or something similar, so that it behave the same way as when an NSURLConnection is scheduled on the run loop, avoiding using an additional thread (watch the WWDC'12 videos that explains why threads are evil and their usage should be minimized), the difference only being that allows you to use a block to be informed upon completion instead of using the delegate mechanism.
Some years ago I created a class that embedded NSURLConnection asynchronous calls and delegate management into a nice block API (see OHURLLoader on my github) that makes it easier to use (feel free to take a look). I bet the new API that uses NSOperationQueues uses the same principle, still doing asynchronous requests on the runloop but allowing you to use blocks instead of having to implement a delegate.
The historical position was that there's an advantage in power consumption, and therefore battery life, in asynchronous requests — presumably including both the older delegate approach and the new block-based approach.

How do asynchronous NSURL requests fit with grand central dispatch / Operation queues ?

Can someone explain the relationship between asynchronous NSURL requests and GCD and NSOperationQueues?
I am not sure when to use each.
Right now, I have been "getting away" with asynchronous NSURL requests when I need to fetch/upload data to the server. But it has been suggested that I should use GCD. My problem is I do not know in what real life examples GCD would be better. Does anyone have any common use cases for me? And if I use GCD to store a queue of 10 asynchronous NSURL GET requests, for example, how would this benefit me? Does it even make sense to have an asynchronous NSURL request inside grand central dispatch queue or an NSOperationQueue?
Thank you!
What is particularly confusing in your case is that we're mixing an HTTP request queue (where requests would be sent one after the other) and an operation queue (where random computational work is executed one task after the other).
A standard NSURLConnection instance calls its delegate on the main thread, which is OK if you're not doing complex work on the data or on your UI at that time. But say you need to download a big file and write it chunk by chunk as a file on disk, all while scrolling down a table view. Now your scrolling might get choppy when you write data on the disk, blocking the main thread.
That's where GCD or its higher level abstraction NSOperationQueue comes into play. To solve this issue, you need to offload your data write calls off of the main thread. You can do that by specifying an NSOperationQueue instance on your NSURLConnection via setDelegateQueue:. This will ensure your delegate, and therefore your write calls, will be called in a background thread. You can also leave the delegate calls on the main thread, and wrap your expensive write calls inside a block which you will then execute off the main thread with dispatch_async. Now NSOperationQueue basically wraps a dispatch queue, and you're avoiding an extra thread switch by using it over a raw dispatch queue so I would recommend the NSOperationQueue solution (which also happens to looks simpler).
AFNetworking is a great library, and it solves this issue in a third way: it fires off an NSThread which is dedicated to NSURLConnection delegate calls. That's the pre-GCD way of offloading work off the main thread. Although it works, GCD offers a more efficient and good-citizen way of presenting your background work to the system.
Last, if you're looking for an HTTP request queue, Cocoa doesn't provide it. You will have to either build a scheduler yourself, or as you already figured it out use AFNetworking which again is a great choice.
If you're interested in this topic, GCD has a lot more to offer than just hosting NSURLConnection delegate calls, and I would recommend you read Apple's great Concurrency Programming Guide or watch the excellent WWDC 2011 videos on GCD (Blocks and Grand Central Dispatch in Practice and Mastering Grand Central Dispatch).
This is off the topic but if you use AFNetworking Lib you dont have to do anything you mentioned above.
"AFNetworking is a delightful networking library for iOS and Mac OS X. It's built on top of NSURLConnection, NSOperation, and other familiar Foundation technologies. It has a modular architecture with well-designed, feature-rich APIs that are a joy to use. "
Answer to your question, please read this
http://www.cocoaintheshell.com/2011/04/nsurlconnection-synchronous-asynchronous/

NSNotification-based iOS App

I am currently thinking about the data-model for my iOS App. The app does only receive Information from a server and therefore, the "model" itself generally does all the web-requests, ...
However, these network requests have to be performed in the background - I mean another task, not the iOS background state - and after the request has finished, the information in the Application has to be updated.
Would it make more sense to write a delegate to inform the controller or could I also use NSNotificationCenter? I think an NSNotification-based solution could make the model more universal, e.g. in a desktop application.
And maybe, I should add: the model (since it saves some session Information) is a singleton, so a regular delegate based method would not work...
I don't think that it would be a good idea to use a separate thread to handle the communication. Apart from being complex, it is not necessary since NSURLConnection/NSURLRequest do allow you to handle communication asynchronously, i.e., without blocking.
In detail, you can create an NSURLRequest executing:
NSURLRequest* yourReq = [NSURLRequest requestWithURL:yourURL];
then create an NSURLConnection with:
NSURLConnection* yourConnection = [NSURLConnection connectionWithRequest:yourReq delegate:yourDelegate];
and start it with:
[yourConnection start];
When the data is ready, one of the methods for your delegate (connectionDidFinishLoading:, or connection:didFailWithError:) will be called so that you can update your UI.
All this without blocking.
An even better alternative to using NSURLConnection/NSURLRequest, is using ASIHTTPRequest, which seems to be more resilient as to memory management and also offers a great caching mechanism.
EDIT: if your concern is that being your model a singleton, you cannot have a delegate, let me suggest that you look further into the matter.
Your model may be a singleton and the delegate to your request may have nothing to do with the model, apart from knowing how to access it (which is pretty trivial being the model a singleton).
This is possible by several mechanisms with NSURLConnection, but if you use ASIHTTPRequest, it will become really easy, because each ASIHTTRequest can have its own delegate.
A delegate solution does work and is recommended. It looks something like:
[[DataLayer sharedInstance] fetchDataWithDelegate:self];
That method can spawn a background thread and respond to the delegate on the main thread.

Resources