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.
Related
I'm using NSURLSession to perform an HTTP Post NSMutableURLRequest, using the dataTaskWithRequest:completionHandler: method. When I perform a request for the first time, things take a reasonable amount of time to complete and show some feedback in the completion handler. On the next time that same request is fired, it happens almost instantaneously with very little time in between, which leads me to believe that the system is caching the contents of this data task.
As I don't need to view the returned data, is NSURLSession the best way to do this? It needs to work well with WatchKit, which NSURLSession does, which is why I chose it in the fist place. I would preferably like to find a way to just clear the cache after each request. If need be, I could switch to NSURLConnection, but this would be best. Thanks!
Ephemeral mode will not use any cache.
NSURLSessionConfiguration *ephemeralConfigObject = [NSURLSessionConfiguration ephemeralSessionConfiguration];
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.
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.
I use AFNetworking as my connection lib to my app. Due to the back-end restrictions, I cannot send two request simultaneously when the app starts because the server will identify a CookieTheftException (Grails). After a first successful connection, I can do as many simultaneous requests as I want but the first need to be serial.
How can I achieve that?
I thought using a Semaphore but i can't block the main thread.
Edit 1
I tried to override but it didn't work. I think the operation queue doesn't wait one request to finish (including it's callback) to start the other.
- (void)enqueueHTTPRequestOperation:(AFHTTPRequestOperation *)operation
{
[self.operationQueue setMaxConcurrentOperationCount:1];
[super enqueueHTTPRequestOperation:operation];
}
Edit 2
I realized that the maxConcurrentOperation worked and in fact 1 operation is executed at a time. The problem is that the request enqueued is already created without the cookies the server needs.
I don't know anything about Grails or the specific architecture of your system, but perhaps this could be solved by simply turning off cookies on that request, with NSMutableURLRequest -setHTTPShouldHandleCookies:.
Other than that, the best way to ensure that only one request operation is ever running for that initial call would be to ignore queues altogether, and simply have an AFHTTPRequestOperation property on your AFHTTPClient subclass. You could even get fancy with KVO to ensure that the operation queue is suspended until that initial request is finished.
I would recommend to read about GCD
You can create a queue and put some block to be executed in this queue.
This way:
It won't block main thread
Since all your networking blocks will be executed in one queue, there is no way that two blocks will be executed simultaneously.
You could set the maximum concurrent operations of the queue to 1. That way only one request will be made at a time.
[self.httpClient.operationQueue setMaxConcurrentOperationCount:1];
But since you only need to wait for the first request, why not just call that request on its own, then initialize the other requests only when the first one completed?
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.