I've built an app around AFNetworking 2.0's AFHTTPSessionManager and its nice HTTP convenience methods. I now need to ensure that all of this networking functionality can run in the background, and I'm rather concerned.
Reading Apple's documentation, I can see that data tasks are not supported for background sessions. After looking briefly at AFHTTPSessionManager's implementation of GET, POST, PUT et al, it seems to use NSURLSessionDataTask across the board.
Is there something I'm missing, or do I have some redesign and rework to do?
In the event that I'm right (and I suspect I am), and that this codepath won't allow me to background uploads and downloads, is there any reason I couldn't wrap AFURLSessionManager's other methods that use non-data tasks in the same way the current HTTP methods wrap "dataTaskWithRequest:completionHandler"? For example for a POST I could perhaps use "uploadTaskWithRequest:fromData:progress:completionHandler"?
I'm asking, since I'm wondering if this is a viable route, why the AFNetworking devs didn't use it, so that AFHTTPSessionManager's convenience methods would allow for background transfers.
AFNetworking allows you to perform background requests (though be careful to not use any of the task-specific completion blocks and make sure you implement the appropriate app delegate methods; see AFNetworking 2.0 and background transfers). You probably also can use requestWithMethod of AFHTTPRequestSerializer to simplify the process of building the request, too (though, IIRC, you cannot use HTTPBody of the request with background upload requests, so you might have to save the body of the request to a file and then issue your background upload request using that file).
But you're absolutely correct that you cannot use the AFHTTPSessionManager methods to initiate NSURLSessionDataTask tasks when using background session. Regarding why they implemented it that way, that's a question better suited for their issues forum.
Related
If both initWithRequest and sendAsyncRequest are Asynchronous ways of connections then whats the major difference ?
Other than completion Handler and Queue concept in sendAsyncReq what else ?
Which 1 is more advantageous ??
The sendAsynchronousRequest is simpler and easier to use, saving you from implementing NSURLConnectionDataDelegate and NSURLConnectionDelegate methods. But if you need richness of delegate approach (e.g. challenge-based authentication, need cancelable requests, etc.), then sendAsynchronousRequest is not up to the job.
If targeting iOS 7 and later, consider NSURLSession, instead, too. You can enjoy simplicity of block-based networking and still enjoy delegate methods if and when needed. Also requests are always cancelable. It also opens new opportunities (e.g. background sessions that continue operating even if your app is no longer active).
this is an open question aiming at understanding what is the best practice or the most common solution for a problem that I think might be common.
Let's say I have a list of URLs to download; the list is itself hosted on a server, so I start a NSURLConnection that downloads it. The code in connectionDidFinishLoading will use the list of URLs to instantiate one new NSURLConnection, asynchronously, per each URL; this in turn will trigger even more NSURLConnections, and so on - until there are no more URLs. See it as a tree of connections.
What is the best way to detect when all connections have finished?
I'm aiming the question to iOS7, but comments about other versions are welcome.
A couple of thoughts:
In terms of triggering the subsequent downloads after you retrieve the list from the server, just put the logic to perform those subsequent downloads inside the completion handler block (or completion delegate method) of the first request.
In terms of downloading a bunch of files, if targeting iOS 7 and later, you might consider using NSURLSession instead of NSURLConnection.
First, the downloading of files with a nice modest memory footprint is enabled by initiating "download" tasks (rather than "data" tasks).
Second, you can do the downloads using a background NSURLSessionConfiguration, which will let the downloads continue even if the user leaves the app. See the Downloading Content in the Background section of the App Programming Guide for iOS. There are a lot of i's that need dotting and t's that need crossing if you do this, but it's a great feature to consider implementing.
See WWDC 2013 What's New in Foundation Networking for an introduction to NSURLSession. Or see the relevent chapter of the URL Loading System Programming Guide.
In terms of keeping track of whether you're done, as Wain suggests, you can just keep track of the number of requests issued and the number of requests completed/failed, and in your "task completion" logic, just compare these two numbers, and initiate the "all done" logic if the number of completions matches the number of requests. There are a bunch of ways of doing this, somewhat dependent upon the details of your implementation, but hopefully this illustrates the basic idea.
Instead of using GCD you should consider using NSOperationQueue. You should also limit the number of concurrent operations, certainly on mobile devices, to perhaps 4 so you don't flood the network with requests.
Now, the number of operations on the queue is the remaining count. You can add a block to the end of each operation to check the queue count and execute any completion logic.
As Rob says in his answer you might want to consider NSURLSession rather than doing this yourself. It has a number of advantages.
Other options are building your own download manager class, or using a ready-made third party framework like AFNetworking. I've only worked with AFNetworking a little bit but from what I've seen its elegant, powerful, and easy to use.
Our company wrote an async download manager class based on NSURLConnection for a project that predates both AFNetworking and NSURLSession. It's not that hard, but it isn't as flexible as either NSURLSession or AFNetworking.
I have subclassed AFHTTPSessionManager and added some methods specific to my implementation. I have a couple questions around using my subclass as a singleton and concurrency:
Is creating a singleton of AFHTTPSessionManager still a best practice? (AFNetworking 2.0, iOS7)
If I make requests via a singleton of the subclass using the [self GET/POST method does this support concurrent
operations? E.g., I have a slow sync running and then do an search. Will the search begin immediately or wait for the sync to complete? Or, asked another way, are these operations on independent operation queues?
You asked:
Is creating a singleton of AFHTTPSessionManager still a best practice? (AFNetworking 2.0, iOS7)
I'm not sure if it ever was best practice. Singletons are frequently derided (see What's wrong with singleton?, which has links to lots of arguments against singletons). They are convenient, but for most apps, a singleton for the session manager is unnecessary. My general counsel is that you shouldn't use a singleton unless you have some compelling need to do so.
This is a subject of opinion and debate (which is explicitly frowned upon on Stack Overflow), so I would not suggest we pursue that question further. Follow the links in the above Stack Overflow question, and you'll see many opinions expressed there.
If I make requests ... does this support concurrent operations?
Yes, the network requests run asynchronously and support concurrent operations.
Are these operations on independent operation queues?
At the time I write this, the requests generated via AFHTTPSessionManager are not run on operation queues at all. The session's manager NSURLSession manages the tasks itself.
On the other hand, the NSURLConnection-based AFHTTPRequestOperationManager will run GET and POST requests on a single, concurrent operation queue. If you manually create your own AFHTTPRequestOperation, you can add them to your own queues if you want.
But all of this is academic. I think your real question is whether GET and POST requests run asynchronously, and the answer is "yes". And if the question is whether they run concurrently with respect to each other, again, the answer is "yes".
In a job interview, I was asked why I should use blocks and GCD instead of NSURLConnection in order to download files asyncronously. After some research I haven't found a good reason to do that. I have multiple apps where I use just NSURLConnection just fine for multiple simultaneous downloads. Is their question attempting to ascertain whether I'm conforming to whatever is trendy (GCD, blocks) or is there any actual, substantial advantage to doing async fetches in this way?
Thanks.
In iOS 7, you generally should not use block-based methods to download files asynchronously. In order to support background transfers, you must use NSURLSession with delegate methods, and cannot use the block-based methods. Beyond that, I'm not sure what is meant here by "instead of NSURLConnection" in any case.
If they meant sendAsynchronousRequest:queue:completionHandler: (which is NSURLConnection), it's convenient, but much less flexible and powerful than the delegate-based NSURLConnection, so the only answer I would have is "because sometimes it's more convenient, and keeps the code closer together, when you don't need much flexibility."
Unless what they actually mean is the part of GCD that really does this: Dispatch I/O. There are reasons to use that directly (particularly if you're using non-HTTP protocols, or if you're managing an HTTP server rather than a client), but they're rare, and not usually for "downloading files asynchronously." The higher level APIs are preferred in most cases.
If you were doing many, many connections, transferring a ton of data over a very fast network connection, I can perhaps see how NSURLConnection's use the runloop for I/O handling and callbacks might become problematic, if you're scheduling these NSURLConnections on the main runloop. That said, you could just as easily spool up a lower priority background thread with its own runloop to keep those operations off the main thread.
If you didn't need all the extra machinery of NSURLConnection (caching, authentication, etc), dispatch_io* is almost certainly a lower overhead mechanism for handling raw network I/O, but you would really be giving up quite a bit of functionality for what I expect, practically speaking, to be a very marginal performance improvement.
I'm not sure. Mainly because NSURLConnection's sendAsynchronousRequest method has a completion handler built into it which uses a block.
Maybe a trick question? To me, it seems like the interviewer just wanted to see if you could conclude that they both can serve the same function.
Blocks and GCD aren't specifically for downloads, but they can make downloading easier. You would have to use them in conjunction with something that did the downloading (like NSURLConnection).
The advantage of using GCD with an NSURLConnection is that you can package it together nicely and don't have to rely on spread-out delegate methods. You can limit the number of connections easily too as well as pausing and stopping connections.
My "go to" set up for complex networking is to use an NSOperationQueue and NSOperation subclasses to do the work.
Each operation then uses a NSURLConnection and its delegate methods to download the data and then process it.
In a way this is already using GCD through the NSOperationQueue but I can't see a reason to use any other method combining blocks and GCD etc...
Did they give you a "correct" answer?
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.