Troubleshooting NSURLConnection

I was recently helping a friend debug an interesting piece of code to asynchronously download images. Without giving away the application idea, let me explain what we were attempting to do from the 30,000 foot view and then share what we discovered, hopefully saving you time should you ever need to implement similar functionality.

The basic idea was to download thumbnail images in the background, and update rows in a table in realtime, as the images were retrieved. This lends to a really nice visual effect on the iPhone. Picture a table with two columns; the right column displays text and the left column shows an avatar (as png file). For each visible row, the text is immediately shown, and the image on the left starts out with a placeholder image (packaged with the application). As each avatar is downloaded, the applicable row in the table is updated with the new image.

The basic idea was to create a URL request (NSURLRequest) specifying the desired URL. With this in hand, create a URL connection (NSURLConnection) to load the URL request.

NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:_url]];
[NSURLConnection connectionWithRequest:request delegate:self];
[connection start];

From the reference documentation, the description for the start method is as follows: “Causes the receiver to begin loading data, if it has not already.” Seems the right thing to do after creating the URL and connection requests.

Using delegate methods of NSURLConnection, you can receive callbacks for things such as when data is received and when a connection has finished loading. This is handy, as our approach was to initiate additional requests for images while waiting for others to download.

The trouble we ran into was that we kept getting time out errors. The specific error was often displayed as “EXC_BAD_ACCESS” in the debug console. What was odd, was that we could account for each URL request, connection and download completing. It was obvious that a connection was being created somewhere on the way, and timing out, question was, what connection?

Here’s where we went wrong…when calling connectionWithRequest: (line #2 above) an initialized URL connection is returned and the data is immediately requested. The call to start must of begun a new connection request, which would eventually timeout as the original request was already underway. When we removed the call to start the application worked like a champ.

This was a surprisingly tricky bug to track down. From reading the documentation one would think calling start would be a reasonable thing to do, in other words, if the receiver hasn’t begun to download data, begin to do so (it’s obviously not quite that simple). It’s not clear to me when one would use this method. If anyone has some insight, I’d love to hear it.

  1. Any traction on this issue? I’m having the same problem. I’m trying to shoot off a web services call to an external service, so I’m doing what you’re doing above and getting the same result. Without the [connection start] nothing happens. Thoughts?

  2. Oddly, I’ve discovered a case in which an async call never gets started … its odd. If I can figure out what is going on I’ll post again.

  3. Ok … so here was the key. The best way to make an async request is basically … not to. The most reliable approach is to wrap a synchronous request within an NSThread. Boom .. done!

  4. Have you faced a problem with POST-ing images from iPhone’s photo library using NSURLConnection via EDGE? My application tries to upload approximately 300Kb image data. The code works perfectly via WiFi, but connection times out when using EDGE even if timeout interval equals to 120s. Any suggestions?

  5. Thanks, this post saved me a lot of time! Seems the documentation is a bit misleading hereā€¦

  6. Apple recommends not wrapping your fetches with NSThreads because this is pretty resource intensive (Threads have a much higher overhead than the asynchronous, NSRunloop based approach). Most importantly is that NSURLConnection will optimally allocate sockets as well as re-use connections which should dramatically increase multiple fetches from the same host in rapid succession.

    The failure here is in the docs:

    NSURLConnection – (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately

    when passed NO for the last argument in the method will create a parked connection which you can call -start on. The version you use is equivalent to passing YES to the last argument of the above method.

Comments are closed.