API Interactions with KVO in Objective-C
API driven applications generally tend to follow a similar pattern: a request is made, the response is received, and then the UI is updated appropriately. Traditionally a loading indicator is shown, the web request is made, and when the response comes back a block of code is run that updates local data and the UI. This pattern works, but often breaks organization as you've suddenly got one chunk of code updating things all over the place. Thankfully, Objective-C offers a solid key-value observing (KVO) interface which can be used to maintain code separation when processing API results.
Making the call
API requests in Objective-C are commonly handled with the NSURLConnection
class. This class provides all the necessary tools to send HTTP requests and execute blocks of code on the received responses. NSURLConnection
offers three ways to handle a call.
The most robust method available is to instantiate an NSURLConnection
object with an NSURLRequest
and an NSURLConnectionDelegate
. This delegate provides lots of options for handling data, caching, and authentication, but must implement methods the required methods listed in the documentation to handle responses and errors.
Often simple API request doesn't need all the functionality offered by instantiating an NSURLConnection
object directly, so two other (class) methods are provided on NSURLConnection
to make simpler calls with less code. These methods are
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request
returningResponse:(NSURLResponse **)response
error:(NSError **)error
and
+ (void)sendAsynchronousRequest:(NSURLRequest *)request
queue:(NSOperationQueue *)queue
completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler
Both methods do essentially the same thing, however the first blocks the current thread and returns the response data, while the second is run in a background thread and fires a completion block
with the response data when appropriate.
You should almost always use the asynchronous requests when making API calls. While a synchronous request may seem tempting as it follows a simple procedural thought process, it blocks the thread it's called on and if handled improperly can lock the UI for a very long time. Applications that block the UI like this are both frustrating to use and will be rejected by the app-review team. Asynchronous calls allow for both a better user experience and more flexibility in how results are handled.
Handling the response
When the API response is received it's tempting to simply handle all necessary updating in the completion block, however this will often put you in a situation where you're updating model data and the UI together. This breaks typical organization which means the code will be less predictable and less manageable in the future. The solution to this is to only process and store data in the completion block, and to use KVO to respond to those changes.
KVO lets you register observers on certain variables and have methods fire when they change. For example, if you had an API method which updates the books in a users library, you can register an observer on a user's books
array and then that observers observeValueForKeyPath
method will fire whenever the books
array changes. In the APIs completion handler you can update the books, and in the observeValueForKeyPath
method you can call functions to update the UI.
KVO is especially beneficial because multiple controllers can register to observe the same variable. If you have several view controllers in the view hierarchy, a single model change could trigger UI updates in all of them, eliminating the need to pass data between controllers or to refresh data on view focus. Also it allows you to use one single piece of code to update a view, and whether data is modified locally or as the result of an API event, the updates are the same and happen automatically.
dispatch_async() and dispatch_get_main_queue()
When an API response completion handler is run, it's typically on a background thread. Because of this, UI updates (though there shouldn't generally be much, but some things like showing a confirmation message or removing a loading indicator can be appropriate) will not be immediately triggered. Only commands run on the main dispatch queue will show in the UI immediately, so we have to do some code wrapping to get things to show up.
We can use the following code to hide an MBProgressHUD loading indicator (or something similar) immediately regardless of which thread it's on:
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideAllHUDsForView:self.tableView_feed animated:YES];
});
dispatch_async
uses Apple's Grand Central Dispatch to execute a new task in the background. This method takes in a dispatch queue (dispatch_queue_t
), to handle the priority of things running. Using dispatch_get_main_queue()
for the queue will cause code execution to happen on the programs main thread, creating immediate UI updates. You can also use dispatch_get_global_queue(long priority, unsigned long flags);
which will return a non-main queue of the given priority, however UI updates will take longer to show from here.
Bringing them together
Mixing together async API calls, KVO, and main thread dispatches allows for great code separation, incredibly organized code, and a UI that's guaranteed to be as up to date as possible regardless of how the data updates were made. Apps should be made to take advantage of these technologies whenever possible as they allow for less code, better code, and easier upgrading in the future.