A simple demonstration of Result
import Foundation
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
typealias JSONDictionary = [String: Any]
enum SomeError : Error {
case zoError
}
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)
}
}
}
struct Gist {
let id: String
}
func jsonFrom(data: Data) -> Result<Any> {
let json = try! JSONSerialization.jsonObject(with: data, options: [])
return .success(json as Any)
}
func gistFrom(json: JSONDictionary) -> Gist {
let id = json["id"] as! String
return Gist(id: id)
}
func gistsFrom(json: Any) -> Result<[Gist]> {
let gistsJSON = json as! [JSONDictionary]
let gists = gistsJSON.flatMap {
gistFrom(json: $0)
}
return .success(gists)
}
// May be we don't need no reason
// Can't stop.
func fetch(url: URL, completion: @escaping (Result<Data>) -> Void) {
URLSession.shared.dataTask(with: url) { (data, response, error) in
completion( Result {
if let error = error { throw error }
guard let data = data else { throw SomeError.zoError }
return data
})
}.resume()
}
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()
print(gists)
} catch {
print("error")
}
}