Promise Pattern on Swift with PromiseKit

Written by raymund.cat | Published 2017/07/07
Tech Story Tags: swift | promises | design-patterns | ios-app-development | ios

TLDRvia the TL;DR App

And why you should migrate your async calls to them

Asynchronous API/Server calls are almost a staple on mobile apps with several advantages from offloading hardware work, to designing reliable network behaviors.

If you have been developing iOS Apps for some time now, you probably already know the usual conventions on this: Closures

Closures can capture and store references to any constants and variables from the context in which they are defined. This is known as closing over those constants and variables. Swift handles all of the memory management of capturing for you.

~https://developer.apple.com

Understandably, it is the preferred design pattern for accomplishing light asynchronous communication. You can quickly let API calls access your local variables where they can call/manipulate them whenever they please.

Like any OOP pattern they too can be abused. Take for example this beast:

Looking closely, you’ll see how this code is simply trying to consecutively perform 3 async tasks:

  1. Upload an image for the User
  2. Register an account for the User
  3. Login a token for the User

Each and every async call comes with a completion block which handles the responses that can be switched in 2 different ways: success or failure.

Now if writing this wasn’t already an enormous pain for you, wait until you must maintain it. To handle errors and successful responses, you would have to inspect and edit your code from 3 different blocks, that is, if you aren’t already lost tracking which block you’re supposed to be editing.

Surely, there must be a better way to handle this?

The Promise Pattern

A promise is an object that represents an asynchronous task. Pass that object around, and write clean, ordered code; a logical, simple, modular stream of progression from one asynchronous task to another.

~http://promisekit.org/docs

Sometimes called Futures, Delay, or Deferred on other languages, they’re specifically constructed to handle asynchronous tasks that may or may not succeed.

But unlike closures, promises are designed to be passed around without needing to capture whole contexts and their result only within a block.

The design works by actually wrapping asynchronous functions around an object and only exposing their promised end result via a .then{ } block, which can then be modified to return another promise or other objects.

intPromise.then { intResult -> Promise<Bool> inreturn boolPromise()}.then { boolResult -> Promise<String>return stringPromise()}.then { stringResult -> Void//finally do something with//your end result here}.catch { error in//oh noes error}

This example shows how 3 asynchronous functions can be cascaded to each other where all their end results can be handled cleanly using PromiseKit.

Not only are your Errors handled in a single block, all proceeding tasks are arranged in a cleaner order avoiding long indentations.

Catching everything so far? Awesome!

Now let’s see if we can transform our original “registration” code to something similar.

Integrating PromiseKit

use_frameworks!swift_version = "3.0"pod "PromiseKit", "~> 4.0"

Let’s start by installing the library through pods, or for other methods you prefer, checkout their readme https://github.com/mxcl/PromiseKit

In our previous user registration code, we can see that it uses 3 different server API calls namely:

It looks clean on paper, yes? Let’s see how else we can improve this.

Converting Async Calls

We’re going to start by fully revising our calls from taking Closures as parameters:

func asyncCall(parameter: String, completion: (String) -> Void)

to returning Promises:

func asyncCall(parameter: String) -> Promise<String>

A Promise Object is going to be created with a block, having a fulfill and reject parameters. These two are simple functions that will take your Generic Typed parameter or an Error as a result.

You are free to implement your async calls inside this block, then call the parameters to send a result signal.

Ideally, your refactored functions would look like this:

Also, notice that we are now mocking an error result. This is simply for error handling demonstrations. Now try to do the whole registration flow again with the new Promises:

Now doesn’t that look better? Now you can easily manage where your calls, results and errors go. It’s very readable and maintainable.

Wrapping Existing Async Calls

If creating new Promises for each API call you have seem little too much for your development flow, there is another option for you. It’s a little more tedious to implement, but it means less new API writing:

public func wrap<T>(_ body: (@escaping (T?, Error?) -> Void) throws -> Void) -> Promise<T>

Wrap functions are available from PromiseKit’s library and they come with different parameter options depending on your case. In this case, say you have a function that looks like this:

func asyncCall(parameter: String, completion: @escaping (Token?, Error?) -> Void)

Wrapping it will simply look like this:

wrap({ asyncCall(parameter: String, completion: $0) })

Now this will return a new Promise which will copy your original result type and pass it as its own. Since we (well, at least I do) are writing our result objects represented by an Enum:

enum Result<T>{

case success(result: T)

case failure(error: Error)

}

We have to write a new wrapping function to accommodate our flow:

Looking closely and you’ll see how this simply takes your closure block and converts it into a Promise object.

Now try to implement the original registration flow again, but now with wrapped closures:

Looks like you have to write more characters, yes?

I prefer this for most cases. It saves me from having to rewrite or duplicate my functions, resulting in fewer possible errors and better code maintainability.

Alamofire Promises

If you use Alamofire for your REST calls, then you should checkout their Alamofire extension library that you can get through

pod 'PromiseKit/Alamofire', '~> 4.0'

or check the whole readme here: https://github.com/PromiseKit/Alamofire-

It comes with options such as:

public func responseJsonDictionary(options: JSONSerialization.ReadingOptions = .allowFragments) -> Promise<[String: Any]>

which lets Alamofire’s DataRequests to return a Promise object instead of you having to capture them all through a block. An example would be a login call like this:

This is one of the few cases where I use throw to handle errors, and it works very cleanly.

What’s next?

Promises are fun! Start by slowly integrating them into your projects where you see fit. Read more of the awesome uses of PromiseKit, straight from their website: http://promisekit.org/docs/

Look for places where closures are starting to get messy. Try wrapping up some of your functions. Introduce more Promises instead of completion blocks to your code.

And just like trying out any new design patterns, don’t be afraid to step back and reevaluate if they truly fit your style.

I wish you best of luck!

Quick Follow Up

If you would like to see a more in depth guide on how to manage you custom async calls, I have written a short guide on it so feel free to check it out:

https://hackernoon.com/wrapping-up-apis-with-promisekit-d1302f446ad6

You made it all the way here!

I hope that you got what you’re looking for when you opened this article. If so, I would very much appreciate it if you would recommend this to your friends ^^

If you have any more questions/feedbacks, please feel free to drop a message!


Published by HackerNoon on 2017/07/07