I wrote my own implementation of HTTPClient for my iOS application to download the contents of the specified URL asynchronously. HTTPClient uses an NSOperationQueue to host NSURLConnection requests. I chose NSOperationQueue because I wanted to cancel any or all ongoing NSURLConnection at any given time.
I did a lot of research on how to implement my HTTPClient, and I had two options to run NSURLConnection:
1) Run each nested NSURLConnection on a separate secondary thread. An NSOperationQueue performs each second-thread-bound operation in the background, and therefore, I donβt have to explicitly do anything to start secondary threads other than starting my NSURLConnection in an overridden method of starting a subclass of NSOperation and running runloop for the generated secondary thread until while connectionDidFinishLoading or ConnectionDidFailWithError is raised. It looks like this:
if (self.connection != nil) { do { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; } while (!self.isFinished); }
2) Perform each completed NSURLConnection in the main thread. To do this, inside the start method, I used the performSelectorOnMainThread function and again called the start method in the main thread. With this approach, I planned NSURLConnection with NSRunLoopCommonModes, as shown below:
[self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
I chose this second approach and implemented it. From my research, this second approach seemed better because it does not start a separate secondary thread for each NSURLConnection. Now, at any given time, the application can have many requests simultaneously with the first approach, which means that the same number of secondary threads will be generated and will not return to the pool until the corresponding URL requests are completed.
I got the impression that I was still working simultaneously with the second approach, planning an NSURLConnection with NSRunLoopCommonModes. In other terms with this approach, I thought that I was using NSRunLoopCommonModes instead of multithreading for concurrency so that observers for NSURLConnection would either call connectionDidFinishLaunching or connectionDidFailWithError as soon as it could, regardless of what the main thread was doing with the user interface, at that time.
Unfortunately, my whole understanding turned out to be wrong when one of my colleagues showed me this morning that with the current implementation, NSURLConnection does not return until the scroll view on one of the view controllers stops scrolling. The NSURLRequest to receive data is triggered when the scroll view is about to stop scrolling, but even if it was completed before the scroll view stops ringing, somehow NSURLConnection does not call connectionDidFinishLoading or connectionDidFailWithError feedback until the scroll view stops scrolling completely. This means that the whole idea of ββscheduling NSURLConnection with NSRunLoopCommonModes in the main thread to get real concurrency using UI operations (touch / scroll) turned out to be wrong, and NSURLConnection is still waiting while the main thread is busy scrolling scroll.
I tried switching to the first approach of using secondary streams, and it works like a charm. NSURLConnection still calls one of its protocol methods, while scroll scrolling still scrolls. This is understandable because now NSURLConnection is not working in the main thread, so it will not wait for the scroll to view to stop scrolling.
I really don't want to use the first approach, because it is expensive due to multithreading.
Can someone please tell me if my understanding of the second approach is wrong? If this is correct, what could be the reason for scheduling an NSURLConnection with NSRunLoopCommonModes not working properly?
I would really appreciate it if the answer was a little more visual, because it should clarify to me more doubts about how NSRunLoop and NSRunLoopModes work. Just to indicate that I have read the documentation many times.