The real magic with promises reveals when we add error handling. Let’s recap what we have so far

// Swift 3

struct Promise<T> {

    typealias CompletionCallback = (T) -> Void

    private let operation: (CompletionCallback) -> Void

    func run(onCompletion: CompletionCallback) {
        self.operation {
            onCompletion($0)
        }
    }
}

extension Promise {

    func then<U>(_ f: (T) -> U) -> Promise<U> {
        return Promise<U> { completionCallback in
            self.run { result in
                completionCallback(f(result))
            }
        }
    }

    func then<U>(_ f: (T) -> Promise<U>) -> Promise<U> {
        return Promise<U> { completionCallback in
            self.run { result in
                f(result).run(onCompletion: completionCallback)
            }
        }
    }
}

First off we could use an optional as our T and if it is nil we know that something along the way went wrong. But we would have to check in every our chained promises if the value is nil and pass thorugh the nil value every time. On top of the we would not know where and why the value is nil. Let’s create an Optional like enum but the None-case can hold a value.

// Swift 3

enum Result<T, E> {
    case Some(T)
    case None(E)
}

You can argue about how we would name the None case. Now it is not empty anymore. But it will not hold a value to consume any further the Promise chain. First I called it Error, but is it really an Error? Maybe I do want to stop the Promise-chain and this must not be an error. So we could call it exception. But even this is not quite right. So in the end I kept None with addtional info.

Our Promise need to have two generic types now. One for the Value T and one for the None-case E. And our CompletionCallback has now a Result Parameter of these types. Adding here a convenience run method will let us inject functions for the Some- and None-Case so we do not have to switch ourself. We will use this method in our example later on.

// Swift 3

struct Promise<T, E> {

    typealias CompletionCallback = (Result<T, E>) -> Void

    let operation: (CompletionCallback) -> Void

    func run(onCompletion: CompletionCallback) {
        self.operation {
            onCompletion($0)
        }
    }

    func run(doneWithSome some: (T) -> Void, doneWithNome none: (E) -> Void) {
        run {
            switch $0 {
            case let .Some(s):
                some(s)
            case let .None(e):
                none(e)
            }
        }
    }
}

Next we have to modify the then methods as well. Because we have now a Result in our run callback we first need to switch it. In case of None both thens handle it the same way: Immediately call the completionCallback and pass the additional info through. This will take care of that we do not need to check if the Result has or has not a value in every Promise we build. The Some-Cases are almost identical with the opertations before, but we need to wrap the some value in the first then method in to a Result.
Adding a third then definition lets us chain T -> Result as well. The implementation is no magic. We simply do not need to wrap our T.

// Swift 3

extension Promise {

    func then<U>(f: (T) -> U) -> Promise<U, E> {
        return Promise<U, E> { completionCallback in
            self.run { result in
                switch result {
                    case let .Some(some):
                        completionCallback(.Some(f(some)))
                    case let .None(none):
                        completionCallback(.None(none))
                }
            }
        }
    }

    func then<U>(f: (T) -> Promise<U, E>) -> Promise<U, E> {
        return Promise<U, E> { completionCallback in
            self.run { result in
                switch result {
                    case let .Some(some):
                        f(some).run(onCompletion: completionCallback)
                    case let .None(none):
                        completionCallback(.None(none))
                }
            }
        }
    }

    func then<U>(_ f: (T) -> Result<U, E>) -> Promise<U, E> {
        return Promise<U, E> { completionCallback in
            self.run { result in
                switch result {
                case let .Some(some):
                    completionCallback(f(some))
                case let .None(none):
                    completionCallback(.None(none))
                }
            }
        }
    }
}

Now that we have our Promise ready lets see it in action. In our example we want to connect to the ItunesStore get some Information, and print the Artist Name. First off we define a Error Enum this will be the value of our None-Case.

// Swift 3

enum Error: String {
    case NoUrl
    case RequestFailed
    case parsingJsonFailed
    case dictionaryWrongFormat
    case invalidJson
    case noResultFound
}

Next up we need a Promise which will create a session runs the dataTask and will supply the Data. It is important that the Promise will call the callback in any possible cases. A leak here will lead to unfinished Promises and happy debug times :) So we guard let our url so we do not have to force unwrap it. If it fails we call the callback with the None-Case and supply an error NoUrl. With this url wir create the request and after that we create the session. Setting up the dataTask and then calling resume. resume acts like our run method which will start the task. In the dataTask closure we if let the data and check if error is nil so we can call our callback with the Some-Case and supply the data. If that fails we call with the None-Case and supply a different Error-Case than above. That simply wraps a network request in a Promise we can use mulitple times. Notice we did not call run on this Promise yet so nothing started just yet.

// Swift 3

func iTunesLookUp(id: String) -> Promise<Data, Error> {
    return Promise<Data, Error> { callback in
        guard let url = URL(string: "https://itunes.apple.com/lookup?id=\(id)") else {
            callback(.None(.NoUrl))
            return
        }

        let request = URLRequest(url: url)
        let session = URLSession.shared()

        session.dataTask(with: request) { (data, response, error) in

            if let data = data
                where error == nil {
                callback(.Some(data))
            } else {
                callback(.None(.RequestFailed))
            }
            
            }.resume()
    }
} 

Data is not the type we were looking for so we need to convert it in a more readable type like a Dictionary. Lets just create a function which can do that. We do not need to return a Promise because no async action is done here. We could use a Optional return type but than we would be missing an error message if there is any. So lets use the Result in this place. Return a Dictionary if successful and an Error if not. Nothing fancy done in here, just serialize the data if possible and check if we got an Dicitonary.

// Swift 3

func toDictionary(from data: Data) -> Result<[String: AnyObject], Error> {
    do {
        if let dict = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: AnyObject] {
            return .Some(dict)
        } else {
            return .None(.dictionaryWrongFormat)
        }
    } catch {
        return .None(.parsingJsonFailed)
    }
}

A Dictionary is not quite the type we expected either so lets create a model for our network request. Or actually two models. The Apple API we are using will respond with two types of Objects. The root Object which will hold an array of Result Objects. For both objects we create a model and create a initializer to init the model with a json. The initializer is failable, so if any unexcpected value is present in any of the objects the model will be nil. This will prevent our app from crashing if at any time the API will change. A lot of boilerplate code but it is worth it, even in smaller projects.

// Swift 3

struct ItunesLookupResponseModel {
    let resultCount: Int
    let results: [ItunesLookupResultResponseModel]

    init?(dict: [String: AnyObject]) {
        guard let
            resultCount = dict["resultCount"] as? Int,
            results = dict["results"] as? [[String: AnyObject]] else {
                return nil
        }

        self.results = results.flatMap {
            return ItunesLookupResultResponseModel(dict: $0)
        }
        self.resultCount = resultCount
    }
}

struct ItunesLookupResultResponseModel {
    let amgArtistId: Int
    let artistId: Int
    let artistLinkUrl: String
    let artistName: String
    let artistType: String
    let primaryGenreId: Int
    let primaryGenreName: String
    let wrapperType: String

    init?(dict: [String: AnyObject]) {
        guard let
            amgArtistId = dict["amgArtistId"] as? Int,
            artistId = dict["artistId"] as? Int,
            artistLinkUrl = dict["artistLinkUrl"] as? String,
            artistName = dict["artistName"] as? String,
            artistType = dict["artistType"] as? String,
            primaryGenreId = dict["primaryGenreId"] as? Int,
            primaryGenreName = dict["primaryGenreName"] as? String,
            wrapperType = dict["wrapperType"] as? String else {
            return nil
        }

        self.amgArtistId = amgArtistId
        self.artistId = artistId
        self.artistLinkUrl = artistLinkUrl
        self.artistName = artistName
        self.artistType = artistType
        self.primaryGenreId = primaryGenreId
        self.primaryGenreName = primaryGenreName
        self.wrapperType = wrapperType
    }
}

So lets create a function which convert our Dictionary into a model.

// Swift 3

func toModel(from dict: [String: AnyObject]) -> Result<ItunesLookupResponseModel, Error>{
    if let model = ItunesLookupResponseModel(dict: dict) {
        return .Some(model)
    } else {
        return .None(.invalidJson)
    }
}

Just to get our story going we need just one Result model so a function to filter an arbitrary result from our array will do the trick.

// Swift 3

func filterModels(model: ItunesLookupResponseModel) -> Result<ItunesLookupResultResponseModel, Error> {

    let results = model.results.filter {
        return $0.primaryGenreName == "Rock"
    }

    if let result = results.first {
        return .Some(result)
    }

    return .None(.noResultFound)
}

We are almost there to keep it clean we create our success and error functions which only prints out our result.

// Swift 3

func printName(model: ItunesLookupResultResponseModel) {
    print(model.artistName)
}

func error(error: Error) {
    print(error)
}

So now what? We setup everything we need. Lets create the operation and and chain everything together:

// Swift 3

iTunesLookUp(id: "909253")
    .then(toDictionary)
    .then(toModel)
    .then(filterModels)
    .run(some: printName, none: error)

The end result is not so suprising, but what it does with your code is! No nested scopes and you can read what every operation is doing. One could ask: Why would we split everything into single functions? Single Responsibility Principle thats why! And because it is highly testable and more readable for new teammembers (or oneself). You can easy imagine that is just as easy to make another async request to an API with the result from this promise and chain it even more.

Where do we go from here? This is not a complete library/framework at all, but we do use it in some of our customer projects and its doing well. It is slim and helps to clean our code. The mentioned frameworks form part1 are more complete and will give you more options, but this was not the intend of this article.

In my next article I want to cover how to use Promises and ViewController to create simple App navigation.