zeeshankhan
10/23/2016 - 5:25 AM

Swift 3 updated code with simple examples from this fantastic article: http://alisoftware.github.io/swift/async/error/2016/02/06/async-error

Swift 3 updated code with simple examples from this fantastic article: http://alisoftware.github.io/swift/async/error/2016/02/06/async-errors/

// A Gist.
struct Gist {
    let id: String
}

typealias JSONDictionary = [String: AnyObject]

// These methods are "helpers" to transform Data -> JSON -> Result<[Gist]> 
func jsonFrom(data: Data) -> Result<AnyObject> {
    let json = try! JSONSerialization.jsonObject(with: data, options: [])
    return Result.success(json as AnyObject)
}

func gistFrom(json: JSONDictionary) -> Gist {
    let id = json["id"] as! String
    return Gist(id: id)
}

func gistsFrom(json: AnyObject) -> Result<[Gist]> {
    let gistsJSON = json as! [JSONDictionary]
    let gists = gistsJSON.flatMap { gistFrom(json: $0) }
    return Result.success(gists)
}

// Result Type w/ extensions
enum Result<T> {
    case success(T)
    case failure(Error)
}

extension Result {
    func map<U>(f: (T) -> U) -> Result<U> {
        switch self {
        case .success(let t): return .success(f(t))
        case .failure(let error): return .failure(error)
        }
    }
    
    func flatMap<U>(f: (T) -> Result<U>) -> Result<U> {
        switch self {
        case .success(let t): return f(t)
        case .failure(let error): return .failure(error)
        }
    }
}

extension Result {
    // Return the value if it is a .success or throw the error if it is a .failure
    func resolve() throws -> T {
        switch self {
        case Result.success(let value): return value
        case Result.failure(let error): throw error
        }
    }
    
    // Construct a .success if the expression returns a value or a .failure if it throws
    init( _ throwingExpression: (Void) throws -> T) {
        do {
            let value = try throwingExpression()
            self = Result.success(value)
        } catch {
            self = Result.failure(error)
        }
    }
}

// Error Type
enum GistError: Error {
    case badData(Data)
}

let url = URL(string: "https://api.github.com/gists/public")!

fetch(url: url) { (resultData: Result<Data>) in
    let resultGists = resultData
        .flatMap(f: jsonFrom)
        .flatMap(f: gistsFrom)
    
    // Then if we want to go back in the do/try/catch world for the rest of the code:
    do {
        let gists = try resultGists.resolve()
    } catch {
        print("error")
    }
}