Making Promises
From one block to another.
A quick note — all of my future posts will be published on my dedicated website and this publication is no longer being updated. Thanks for reading!
It starts with a promise.
Which, of course, denotes the forthcoming value returned via an asynchronous task. If you’ve been riding the iOS train awhile, your journey has no doubt been filled with multiple instances of asynchronous tasks relying upon one another.
And therein, your train might have derailed. Code quickly becomes messy, dependent on maybes, and sprawled as far as the east is from the west within Xcode. A relatively new open source library seeks to make right where you committed wrongs in regards to async programming. And so it is — this week we’ll take a brief look at PromiseKit.
So……what?
PromiseKit is a promises implementation that also packs in several utility functions that make async patterns much less painful. Due to its modular design, it’s quite simple to marry to your very own code or perhaps the github flavor of the week library.
Besides being a promises API, its marquee feature in my opinion is its semantically pleasing functionality, which typically starts with then:
[self userSessionInitiated].then(^{
return [ServerSide profilePics];
}).then(^(NSArray *pics){
self.pics = pics
[self.collectionView reloadData];
}).catch(^(NSError *error){
//Failwhale
});
Using a few short lines, PromiseKit has done quite a bit of legwork for the programmer:
- A method wrapped an async task inside a promise
- A promise is returned that’s resolved as an array
- Anything that goes wrong it is caught responsibly in the last block, catch
PromiseKit should be commended already. It’s a good sign that most anyone who reads the previous code can immediately identify with what it’s trying to accomplish. As the old adage goes, code is meant to be read by others and only incidentally run by computers.
No Spaghetti
One might have trouble reasoning why promises can be valuable unless they’ve first tangled with code like this:
[myObj aParam:@"" task:^{
//do stuff
}completion:^(BOOL done){
[anotherObj aParam:@"" anotherTask:^{
//do more stuff
}completion:^(BOOL done){
[yetAnotherObj anotherParam:@"" finalTask:^{
//.....ugh
}completion:^(BOOL done){
//Life has no meaning
}];
}];
}];
…..and further and further your code will flow to the right side of the screen until it’s no longer able. Nested upon nested async blocks can quickly become tiresome, and even worse, unreadable.
The plot thickens since most of these scenarios execute in parallel and the execution depends entirely on the results of said operations. With PromiseKit these types of async operations are essentially standardized whilst being chainable.
This means PromiseKit can essentially eat up any async spaghetti your codebase has been serving up. PromiseKit itself even brands itself as solving two key problems in Cocoa development: Messy async code and iffy error handling.
…And Then
It’s then who is the captain of the proverbial PromiseKit ship. With it, one can access a promise’s value. This is always a safe operation, since the pending value will just hang out and be nil. When they have finished resolving, nil will pack its bags and leave within its wake the intended value.
Essentially, sending the then message passes on your promise value into the block you provide it. For example, refer to some of their helpful functions in its UIKit extensions, such as the following promise for showing a UIAlertView:
UIAlertView *av = [UIAlertView ...];
[av promise].then(^(NSNumber *cancelButtonIndex){
if(cancelButtonIndex != av.cancelButtonIndex) //Party on
});
Easy. And, dare I say, promising? Sorry.
When
The previous was elementary, and one might want such functionality to be present when two or more async tasks are required. This can be a difficult undertaking on your own, and when is here for such situations. The first inclination most have is to execute the tasks in series, one after another as shown below:
id firstName = [API getName:url completion:completionBlock];
id userLocations = [API getLocations:url completion:completionBlock];//Later on is some completion handler, something like this:
// if (self.firstNames && self.userLocations) [self.tableView reloadData];
This is inadvisable for a few reasons, a chief concern being that it will take O(n) as long.
PromiseKit has an efficient and sane means of solving the same problem using when:
id firstName = [API getName:url] promise];
id userLocations = [API getLocations:url] promise];[PMKPromise when:@[firstName, userLocations].then(^(NSArray *data){
//Handle data
}).catch(^
//Man down
});
This one holds a special place in my heart. Most Cocoa devs never see the light of day when it comes to the fabled server side code. They are instead shackled to however an API works and use whatever the API gives them. The above example may seem laughable at first, until you realize that it came from a real world use case.
In that sense, PromiseKit alleviates scattered code from you and poor API design from them.
And Finally
A worthy and natural ally to both then and when is finally. Developers from across the globe are taught error handling from day one, and as such, finally is both a common and approachable concept. PromiseKit does not seek to fix what was never broken.
A perfect example from PromiseKit’s own starter guide:
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES; [self myPromise].then(^{
//…
}).finally(^{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
})
Expected behavior is to be celebrated. No matter the outcome, finally will execute without fail.
GCD FTW
It’s worth noting that then always will execute on the main queue. This is likely the standard use case, though far from an absolution.
Consider that all user interface manipulation must take place on the main thread. So, should one have a relatively intensive operation that must occur, it’s polite to shift it off the main queue.
PromiseKit supports this type of behavior out of the box.
[NSURLConnection GET:myEndpoint].thenInBackground(^(NSArray *data){
return [self data];
}).then(^(NSArray *committedData){
self.data = committedData;
});
The utility method thenInBackground automatically will dispatch to a background thread, making expensive functions that require promises a reality. It’s also worth noting that all the operations carried out in PromiseKit are thread safe, which one might’ve correctly assumed given its nature.
It should also be noted that Promisekit is friendly with swift as well. Utilizing trailing closures within the API makes the syntax all the more inviting. However, like a foolish teenage boy who cannot seem to stay away from his ex, I felt that I needed to reconnect with Objective-C, if only for a little while.
Wrapping Up
PromiseKit is something of a swiss army knife of a promises implementation. In one corner a beautiful promises API awaits you, yet in the other a robust set of utility functions heed your call. Used correctly, PromiseKit saves as much time as it does sanity in async operations. Why not give their docs a glance over? If not now.when({})?
Jordan Morgan is an iOS software engineer who runs Dreaming In Binary.
@jordanmorgan10.