Anyone that reads my blog or talks to me professionally knows how much I hate dislike asynchronous network programming. While working on rewriting some networking code, I came across a few more reasons why synchronous networking is a poor decision.
The first issue occurs when a developer abstracts the networking, then forgets that when the call is invoked, it actually makes a network call and does it on the main thread. Synchronous networking should never be done on the main thread. For instance, let us use the following made up example:
- (void) displayUserPreferences { Preferences *prefs = [[Utility sharedInstance] getPreferences]; if (prefs) { // Update the user interface } }
- (Preferences *) getPreferences { NSDictionary *dict = nil; NSData *result = [Networking queryPreferences]; if (result) { dict = [self parseData:result]; } return dict; }
+ (NSData *) queryPreferences { return [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.something.com/data.json"] returningResponse:nil error:nil]; }
In order to not block the main thread, the developer has to always make sure that displayUserPreferences is called on a secondary thread. While this may sound simple, forgetting to do this is quite easy especially for a developer that didn’t initially write the code. Another developer might think that getPreferences is a local call and doesn’t hit the network and therefore call displayUserPreferences on the main thread. This is, of course, a recipe for disaster.
The second issue isn’t specific to networking, but has to do with threading. In the above example, the “Update the user interface” code under the comment must run on the main thread as user interface calls can/will crash when run on secondary threads. It is far too easy to forget to use performSelectorOnMainThread to run the code on the main thread. Having to keep track of what can and can’t run on a secondary thread just adds confusion and inevitably will lead to mistakes. A simple call like:
[self.tableView reloadData];
run on the secondary thread for networking will cause a crash.
I’ve seen both of these issues in code and there really is no excuse for being lazy in writing networking code. Once you write a good networking class, it can be reused over and over; I’ve used a networking class I wrote a few months ago in 4 or 5 different projects. I wrote it once; tested it extensively and now reusing it is quite simple.
I’m tempted to file a radar bug to ask Apple to deprecate the synchronous call, but I know that they won’t do it. The synchronous networking call screams lazy and should be avoided in my opinion.