Wrapping your callbacks in Promises


A little while ago I wrote a post about PromiseKit. In this post I wrote mainly about how you could wrap API calls in Promises using the NSURLConnection extension that the creator of PromiseKit provides. Since writing that article I’ve had a bunch of people asking me more about PromiseKit. More specifically, some people wanted to know how they could wrap their existing code in Promises. To illustrate this I’m going to use Parse as an example.

A regular save action in Parse

Before we start making Promises, let’s have a look at how you’d normally do something with Parse. An example that any Parse user probably has seen before is one where we save a PFUser.

Above code should be fairly straightfowards. First we create a user, then we call signUpInBackgroundWithBlock and then we specify the block that should be executed after the user has been signed up. A lot of Parse’s save and fetch actions follow this pattern where you use callbacks as a means to handle success and error cases. In my previous post I explained that Promises are a cleaner method to deal with asynchronous operations, but wrapping something like signUpInBackgroundWithBlock in a Promise isn’t very straightforward.

Creating a custom Promise

Setting up a Promise isn’t very hard. What you’re going to need to decide on in advance is what your Promise is going to return. After that you’re going to need to figure out the conditions for a fulfilled Promise, and the conditions for a rejected Promise. How about a simple example?

The function above returns a Promise for a String. When you create a Promise you get a fulfill and reject object. When you create your own Promise it’s your responsibility to make sure that you call those objects when appropriate. The doSomething function receives a Bool this flags if the Promise in this example will succeed. In reality this should be dependent on if the action you’re trying to perform succeeds. I’ll show this in an example a little bit later. If we want this Promise to succeed we call fulfill and pass it a String because that’s what our Promise is supposed to return when it’s succesful. When the Promise should be rejected we call the reject function and pass it an ErrorType object. In this example I chose to create an NSError.

Now that we have explored a very basic, but not useful example, let’s move on to actually wrapping a Parse call in a Promise.

Combining Parse and PromiseKit

When you’re doing asynchronous operations in Parse you call functions like ‘signUpUserInBackgroundWithBlock’. This function takes a callback that will be executed when the user has signed up for your service. Let’s see how we can transform this callback oriented approach to a Promise oriented one.

In the example above a function is defined signUpUser, it takes some parameters that are used to set up a PFUser object and it returns a Promise. The next few lines set up the PFUser and then we create (and return) the Promise object. Inside of the Promise’s body we call signUpInBackgroundWithBlock on the PFUser. The callback for this function isn’t entirely avoidable because it’s just how Parse works. So what we can do, is just use the callback to either call reject if there’s an error or call fulfill when the operation succeeded.

That’s all there is to it, not too complex right? It’s basically wrapping the callback inside of a Promise and calling reject or fulfill based on the result of your Parse request. One last snippet to demonstrate how to use this new function? Sure, let’s do it!

The way this works is basically identical to how I described in my previous post. You call the function that returns a Promsie and use the then and error (report in an earlier PromiseKit release) to handle the results of the Promise.

Wrapping up

In this post I’ve showed you how to take something that takes a callback and wrap it in such a way that it becomes compatible with PromiseKit. The example was about Parse but this principle should apply to just about anything with a callback.

The only thing I didn’t cover is where to actually implement this. I did that on purpose because it really depends on your architecture and partially on preference. You could opt to put the wrapping functions in a ViewModel. Or you could create some kind of Singleton mediator. Or maybe, and I think I prefer this method, you could write some extensions on the original objects to provide them with functions like saveUserInBackgroundWithPromise so you can actually call that function on a PFUser instance.

If you’d like to know more, have questions or have feedback, you can find me on Twitter or in the (amazing) iOS Developers Slack community.

Step up your async game with PromiseKit

Some of the most engaging apps we use today are apps that require network connectivity of some kind. They communicate with an API somewhere to fetch and store data for example. Or they use an API to search through a huge amount of data. The point is, you don’t want your application to sit around and wait while an API call is happening. The same is true for a computing task that’s heavy, for example resizing an image or storing it to disk. You want your UI to be snappy and fast. In other words, you don’t want to do your heavy lifting on the main (UI) thread of a device.

Taking something off of the main thread

There’s several ways to take something away from the main thread. An NSURLConnection automatically performs requests in the background and uses delegates to make callbacks about progress or completion for example. You can also use the dispatch_async function to make something happen on a different thread. While this works perfectly fine it’s not very ideal. When an NSURLConnection performs it’s delegate callbacks it has to be coupled to that delegate. Also, imagine you want to chain together a couple of network requests; it will start to become kind of messy to keep track of everything.

Now imagine an object that you can pass around, it will automatically perform a task once it’s done what it’s supposed to do. These tasks can be chained so if you want to chain multiple things together it’s very simple. Or maybe you want your callback to fire only if a couple of tasks are complete. Imagine implementing that with multiple NSURLConnections. You would probably have multiple instances and whenever one is complete you check the status of the others and then when all are complete you can actually execute the code you’ve been meaning to execute. That sounds a lot more complicated than just writing:

The above snippet is actually really close to how PromiseKit works. Let’s explore that a bit further, shall we?

Note: I am going to apply PromiseKit to network requests for this post. The concepts actually apply to anything that you want to do asynchronously.

A simple Promise example

To demonstrate a simple Promise we’ll make a network request. First we create an NSURLRequest that we’ll pass to an NSURLConnection. Then we kick off the loading with a Promise so we can use the result once the request is done:

PromiseKit has provided us with an extension on NSURLConnection that allows us to call promise(req:NSURLRequest) on it. After that we call then. The code that’s inside of this closure gets called once the Promise is fulfilled. This happens whenever the request completed with success. If the request fails we can add a report (‘catch’ in swift 1.2) as well to make sure we catch that error:

And if there’s code we want to execute regardless of error or success we can use ensure (defer in swift 1.2) like this:

If you understand this, you basically understand all you need to know to start using Promises in a very basic way. But let’s get a little bit more advanced and start returning our own Promise objects.

Returning Promises

Imagine this, we’re building an application that uses an API. We want to ask the API for a user’s feed and we want to use PromiseKit for this. A nice implementation might be to have an API instance that has a method on it called fetchUserFeed. That method will return a Promise so we can easily use the result from the API in the class that actually wants the API to fetch data, a ViewModel for example. The fetchUserFeed function might look something like this:

Note: Feed is a just a data object not included in the sample for brevity. It is used to illustrate how you would return something from a Promise

The function above is very similar to what we had before except now it returns NSURLConnection.promise which is a Promise. The then of that Promise now returns a Feed and the fetchUserFeed function now returns Promise<Feed>. What this means is that fetchUserFeed now returns a Promise that will resolve with a Feed. So if we use this function it looks like this:

That’s pretty clean, right? Now let’s say that we not only want to fetch the Feed but also a user’s Profile. And we want to wait until both of these requests are done (and successful). We can use the when function for that:

Let’s make this this a little bit more complicated shall we? Currently we’re able to use an API to fetch stuff, and we’re able to do multiple requests and wait until they’re all complete. Now we’re going to wrap that into a function we can call from somewhere else. The function will return a single object that uses both a Feed and UserInfo to create itself. Let’s call it ProfileModel.

And when we want to use this function we would write something like this:

That’s pretty cool isn’t it? Pretty complicated logic wrapped in promises to make it simple and enjoyable again.

Wrapping it up

In this post we’ve touched up on the basics of PromiseKit, a library that makes asynchronous programming cleaner and easier. I’ve shown you how to use them in a very simple and basic setting and in a situation where you wait for multiple operations/promises and return a single promise with both results. Promises can help you to build a very clean API for your async operations and they help you to keep your code clean and readable. I highly suggest to try using promises in your own projects, they’re really cool and easy to use. To find out more about PromiseKit you should check out their github.

If you have questions or feedback for me on this subject make sure to hit me up on Twitter or look for me in the ios-developers slack community.