How To: Promises in Swift

Written by: Reinder de Vries, January 11 2018, in App Development

How To: Promises in Swift

Promises in Swift simplify your asynchronous code. Instead of “callback hell” you create a concise chain of functions, and keep your code clean. Let’s find out how.

In this article you’ll learn how to use promises. We’ll use PromiseKit, a great library for iOS written in Swift. Promises are part of good app architecture.

Ready? Let’s go.

  1. The Problem Promises Solve
  2. Writing Asynchronous Code With Promises
  3. How To Make Promises
  4. Multiple Concurrent Tasks With “when”
  5. Further Reading

You can run any of the Swift code on this page in your own Xcode projects, as long as you’ve imported PromiseKit, Alamofire and SwiftyJSON.

The Problem Promises Solve

Why should you use promises? Let’s take a look at some Swift code:

Alamofire.request("http://jsonplaceholder.typicode.com/posts").responseData(completionHandler: { response in

    guard response.result.isSuccess else {
        print("Request error: \(response.result.error)")
        return
    }

    if let data = response.data
    {
        do {
            let json = try JSON(data: data)
            print(json)
        }
        catch {
            print("JSON error!")
        }
    }
})

What happens in this code?

  • An asynchronous HTTP request to http://jsonplaceholder.typicode.com/posts is made. When the response returns a callback is executed (often called a completion handler).
  • Within the callback, you check if the request was successful. If not, you print out an error and exit the closure.
  • Continuing, you check if response.data is not nil. If not, you try to parse the JSON from the response with a do-try-catch block.

The above code example is effective Swift code, but it’s neither elegant nor readable!

Here’s what’s wrong:

  1. We’ve created a “pyramid” of brackets. Most of what happens in our code is indented on a new level. The most important part of the code is hidden away on the deepest level. It takes some reading to figure out all that this code example does, is print a bunch of JSON…
  2. The code has two checks for errors. Three if you count if let data .... Imagine what happens when you extend that code with more functionality. Every time you clear the path forward – no HTTP errors, no JSON errors, no data errors – you indent one level. This makes your code progressively unreadable.
  3. The example just makes one request. What if you want to make subsequent requests? Your code quickly becomes an entangled mess. And when you make requests in serial, that could have been parallelized, you’re writing underoptimized code.

You get the point. The code is hard to read and not efficient. You’re likely going to make a mistake. Chances are you’ll forget what the code does altogether. Not good!

Get 5 of my best practices

Learn how to build better iOS apps

I’ll show you exactly how I built a dozen professional iOS apps,
write extensible Swift code, and turn coffee into code.
Wait, what? Yup – into Swift code.

Writing Asynchronous Code With Promises

Let’s bring in promises! This is what the previous Swift code example now looks like:

Alamofire.request("http://jsonplaceholder.typicode.com/posts").responseData().then { data -> Void in

    let json = try JSON(data: data)
    print(json)

}.catch { error -> Void in
    print("Error: \(error)")
}

Much better, right? This is what happens:

  1. The request is set up just like before. Instead of defining a completion handler, the response is now “chained” with a call to then. Chaining is typical when working with promises, and there are more syntax options like “then”.
  2. Within then, the data from the request is turned into a JSON object and printed out. Just like before, the JSON(data: data) initializer is marked with try because it can throw errors. However, it’s not wrapped in a do-catch block!
  3. The catch handler, also part of working with promises, handles the errors. It’s called for both HTTP errors from Alamofire and JSON errors with do-try-catch. Neat, right?

The example above is called a promise chain. You literally chain multiple promises together, which greatly improves your code readability.

Most code examples in this article use Trailing Closure Syntax. This means the function parameter name and parentheses can be omitted when the last function parameter is a closure.

Let’s decompose promises a bit further. This is a typical promise chain:

firstly {
    login()
}.then { creds in
    fetch(avatar: creds.user)
}.then { image in
    self.imageView = image
}.catch { error in
    print("Error: \(error)")
}.always {
    UIApplication.shared.isNetworkActivityIndicatorVisible = false
}

This is how it works:

  • The chain uses functions called firstly, then, catch and always. All of them are indented on the same level.
  • The firstly handler is executed immediately. Within that block a function login() is called. This function returns a promise, but more on that later!
  • The first then handler gets the returned result from the previous firstly handler. So the value of creds is the same as the return value of login(). The result is used to call fetch(avarar: creds.user). This fetch(...) function also returns a promise.
  • The second then handler uses the result of the previous handler, so the result of fetch(...). It assigns the returned image to an image view. See how the result of one promise is fed into the next?
  • The catch handler catches any errors that can occur in the preceding handlers. When logging in fails or an error is thrown the catch handler is invoked and the error is printed on screen.
  • Finally, the always handler. It’s always executed at the end of a chain regardless of errors. It is similar to Swift’s defer syntax, so you can use it to wrap up some tasks. In the example we hide iOS’s network indicator.

There are three key points to understanding promises.

  • First, in a promise chain, one task leads to another. They are executed procedurally, one by one, until the end of the chain is reached.
  • Second, with promises your functions return promises. Each handler in a chain returns a promise. Each then call waits for the promise of the previous handler. (You’re allowed to return nothing, too.)
  • Third, a promise represents the future value of an asynchronous task. In the example above, the login() function returns a promise that will represent the users credentials.

What’s so exciting about this? Don’t forget that this is all asynchronous code…

Even though the login() and fetch() functions take an arbitrary amount of time to complete – you’re not sure when – you can code them as if they happen one after the other! Paradoxically, promises can synchronize asynchronous code.

Build better iOS apps by mastering best practices and app architecture » Find out how

How To Make Promises

OK, let’s dive even further in. What if you need to create your own promises?

PromiseKit has extensions for most iOS SDKs and popular libraries, so you can use them with promises out of the box. For instance, in the first example we used the PromiseKit extension for Alamofire.

So far, you’ve only used promises. You can also create your own promises. You do this when you’ve written your own networking code, lengthy custom task, or use an external API.

In most cases you can use PromiseKit’s wrap function, like this:

func download() -> Promise<String> {
    return PromiseKit.wrap(download)
}

In the above example, download is the asynchronous closure you want to use in your promise. Its result is of type String and PromiseKit automatically picks up on errors when it can.

PromiseKit has a number of generic wrap() functions that can automatically resolve asynchronous functions as promises.

You can also use PromiseKit’s resolver, like this:

func download() -> Promise<String> {
    return Promise { fulfill, reject in
        asyncTask { result, error in
            if result != nil {
                fulfill(result)
            } else {
                reject(Error.invalidResult)
            }
        }
    }
}

In the above example, the asyncTask function is called with a closure as a completion handler. This is how you would typically code an asynchronous task, before using promises. The download() function returns a Promise, which is expected to return a value of type String.

PromiseKit needs to know when the asynchronous code has completed executing. In the example code you see one call to fulfill() and one call to reject(). This is the code that resolves the promise.

With fulfill() you indicate a positive result and with reject() you can indicate an error. Logically, fulfill() leads to the next then handler and reject() leads to the catch handler.

You rarely have to write your own promise resolving code. When in doubt, check the PromiseKit repository for extensions for your favourite SDKs or use the wrap function.

Multiple Concurrent Tasks With “when”

This is so much fun! One last thing… What if you want to use multiple concurrent asynchronous tasks? For instance, saving a photo and uploading it to your webserver.

A typical way is this:

savePhoto { path in 
    uploadPhoto { url in
        tasksFinished(path, url)
    }
}

That’s serial code, so it’s slow. First, you save the photo. When that’s done, you upload the photo. When that’s done, you call taskFinished().

Doing it in parallel, i.e. saving and uploading at the same time, is even more complicated.

With promises it’s easy:

firstly {
    when(fulfilled: savePhoto(), uploadPhoto())
}.then { path, url in
    // Do stuff
}

With the when function you define multiple promise handlers. The next then is only executed when these promise handlers resolve. When an error occurs in any of the promises, the catch handler is invoked – just as with any other promise.

So, in the above example code, both savePhoto() and uploadPhoto() are started at the same time. The then handler is executed when both tasks are completed, and it’s even provided the return values for those closures.

I say, that’s nothing short of amazing!

Get 5 of my best practices

Learn how to build better iOS apps

I’ll show you exactly how I built a dozen professional iOS apps,
write extensible Swift code, and turn coffee into code.
Wait, what? Yup – into Swift code.

Further Reading

So, promises. Now you know! See if you can use them in your next project, because they greatly reduce the complexity of asynchronous programming while improving readability.

Questions? Write a comment down below! I read and respond to every one.

You learned how to use promises, what problem they solve, how to create your own promises, and how to work with the various syntaxes like firstly, then, catch and when. Nice!

Make sure to check out PromiseKit and their Getting Started guide. You can usually find a PromiseKit extensions by simply Googling for “promisekit [sdk or library name]”.

Want to learn more? Check out these resources:

Delight your fellow developers by sharing this article. They’ll thank you for it!

Reinder de Vries

Reinder de Vries is a professional iOS developer. He teaches app developers how to build their own apps at LearnAppMaking.com. Since 2009 he has developed a few dozen apps for iOS, worked for global brands and lead development at several startups. When he’s not coding, he enjoys strong espresso and traveling.

Comments & Questions

Got a comment or question? Let me know! I read and respond to every one. Thanks!