Created
August 13, 2014 08:32
-
-
Save benjamingr/2a98d0d1d5fdc4710d07 to your computer and use it in GitHub Desktop.
promise library in swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ar topOps = Request.GetJson("http://tipranksphone.azurewebsites.net/api/Phone/GetRecentRatings") | |
var p = Promise.resolve(10) | |
var p2 = Promise.resolve("Hello") | |
let all = Promise<Int>.all(p, p2, topOps); | |
all.tap({ (a,b,c) in | |
print("All done \(a) \(b) \(c)"); | |
}) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Promise.swift | |
// Promise | |
// | |
// Created by Benjamin Gruenbaum on 8/12/14. | |
// Copyright (c) 2014 Tipranks. All rights reserved. | |
// | |
import Foundation | |
public class Promise<T>{ | |
var state:PromiseState<T,Any> = PromiseState(state: .Pending, value: nil, error: nil) | |
var fHandlers:[(T) -> ()] = [] | |
var rHandlers:[(Any) -> ()] = [] | |
func getState() -> State{ | |
return self.state.state | |
} | |
public func value() -> T?{ | |
return self.state.value; | |
} | |
public class func reject(t:Any) -> Promise<Any> { | |
let p:Promise<Any> = Promise<Any>() | |
p.state.state = .Rejected | |
p.state.error = t | |
return p | |
} | |
// no real need to alias anything, we do not assimilate foreign thenables | |
public class func resolve(t:Promise<T>) -> Promise<T>{ | |
return t; | |
} | |
public class func resolve(t:T) -> Promise<T>{ | |
let p:Promise<T> = Promise<T>() | |
p.state.state = .Fulfilled | |
p.state.value = t | |
return p | |
} | |
private class func async(fn:() -> ()) { | |
dispatch_async(dispatch_get_main_queue(), { | |
fn() | |
}) | |
} | |
public init(resolver:((T) -> (), reject:(Any) -> ()) -> ()) { | |
resolver({(t:T) in | |
Promise.async({ | |
self.fulfill(t) | |
println("Fulfill part in resolver, running handlers \(self.fHandlers.count)") | |
}) | |
} , | |
{ (e:Any) in | |
Promise.async({ | |
self.reject(e) | |
}) | |
}) | |
} | |
private init(){ | |
} | |
func tap(onFulfilled:(T) -> ()) -> Promise<T> { | |
return self.then({ onFulfilled($0); return self }) | |
} | |
func then<NT>(onFulfilled:(T) -> Promise<NT>) -> Promise<NT> { | |
var p = Promise<NT>() | |
switch self.state.state { | |
case .Rejected: | |
Promise.async({ p.reject(self.state.error); return }) | |
case .Fulfilled: | |
Promise.async({ | |
let result = onFulfilled(self.state.value!) | |
let resolver:(nt:NT) -> () = { p.fulfill($0) } | |
result.tap(resolver) | |
result.catch( {p.reject($0); }) | |
}) | |
case .Pending: | |
fHandlers.append({ (t:T) in | |
let res = onFulfilled(t) | |
res.tap({ p.fulfill($0) }) | |
res.catch({ p.reject($0) }) | |
}) | |
} | |
return p | |
} | |
func then<NT>(onFulfilled:(T) -> NT) -> Promise<NT> { | |
println("Inside then that returns a value") | |
var p = Promise<NT>() | |
switch self.state.state { | |
case .Rejected : | |
println("Rej case") | |
Promise.async({ | |
p.reject(self.state.error!) | |
}) | |
case .Fulfilled : | |
println("Ful case") | |
Promise.async({ | |
let result = onFulfilled(self.state.value!) | |
p.fulfill(result) | |
}) | |
case .Pending : | |
println("Pending case") | |
fHandlers.append({ (t:T) in | |
let res = onFulfilled(t) | |
p.fulfill(res) | |
}) | |
} | |
return p | |
} | |
func catch<NT>(onRejected: (Any?) -> Promise<NT>) -> Promise<NT>{ | |
var p = Promise<NT>() | |
switch self.state.state { | |
case .Pending: | |
rHandlers.append({ (t:Any?) in | |
let res = onRejected(t) | |
res.then({ p.fulfill($0) }) | |
res.catch({ p.reject($0) }) | |
}) | |
case .Fulfilled: | |
Promise.async({ | |
p.fulfill(self.state.value as NT) | |
}) | |
case .Rejected: | |
Promise.async({ | |
let res = onRejected(self.state.error) | |
res.then({ p.fulfill($0) }) | |
res.then({ p.fulfill($0) }) | |
}) | |
} | |
return p; | |
} | |
func catch<NT>(onRejected: (Any?) -> NT) -> Promise<NT?> { | |
var p = Promise<NT?>() | |
switch self.state.state { | |
case .Pending: | |
rHandlers.append({ (t:Any?) in | |
let res = onRejected(t) | |
p.fulfill(res) | |
}) | |
case .Fulfilled: | |
Promise.async({ | |
p.fulfill(self.state.value as? NT) | |
}) | |
case .Rejected: | |
Promise.async({ | |
let res = onRejected(self.state.error) | |
p.fulfill(res) | |
}) | |
} | |
return p | |
} | |
private func fulfill(val:T) { | |
if state.state != .Pending { | |
return | |
} | |
self.state.state = .Fulfilled | |
self.state.value = val | |
for handler in self.fHandlers { | |
Promise.async({ handler(val) }) | |
} | |
self.fHandlers = [] | |
} | |
private func reject(err:Any?) { | |
if state.state != .Pending { | |
return | |
} | |
assert(self.state.state != .Rejected) | |
self.state.state = .Rejected | |
self.state.error = err | |
for handler in self.rHandlers { | |
Promise.async({ handler(err) }) | |
} | |
self.rHandlers = [] | |
} | |
class func all<A,B,C>(a:Promise<A>,_ b:Promise<B>,_ c:Promise<C>) -> Promise<(A,B,C)> { | |
return Promise<(A,B,C)>({ (resolve:((A,B,C)) -> (), rej: (Any) -> ()) in | |
var sem = 3 | |
a.catch(rej) | |
b.catch(rej) | |
c.catch(rej) | |
func oneDone(){ | |
sem = sem - 1 | |
if sem == 0 { | |
resolve((a.state.value!, b.state.value!, c.state.value!)) | |
} | |
} | |
a.tap({ (a:A) in oneDone() }) | |
b.tap({ (b:B) in oneDone() }) | |
c.tap({ (c:C) in oneDone() }) | |
}) | |
} | |
class func all<A>(proms:[Promise<A>]) -> Promise<[A]> { | |
return Promise<[A]>({ (resolve, rej) in | |
var sem = proms.count | |
for prom in proms { | |
prom.catch(rej) | |
prom.tap({ (_:A) in | |
sem = sem - 1; | |
if sem == 0 { | |
resolve(proms.map({ $0.state.value!})) | |
} | |
}) | |
} | |
}) | |
} | |
class func all<A,B>(a:Promise<A>,_ b:Promise<B>) -> Promise<(A,B)> { | |
return Promise<(A,B)>({ (resolve:((A,B)) -> (), rej: (Any) -> ()) in | |
var sem = 2 | |
a.catch(rej) | |
b.catch(rej) | |
a.tap({ (_:A) in | |
sem = sem - 1; | |
if sem == 0 { | |
resolve((a.state.value!,b.state.value!)) | |
} | |
}) | |
b.tap({ (_:B) in | |
sem = sem - 1; | |
if sem == 0 { | |
resolve((a.state.value!,b.state.value!)) | |
} | |
}) | |
}) | |
} | |
} | |
struct PromiseState<T,E> { | |
var state:State | |
var value:T? | |
var error:E? | |
} | |
// hack for - unimplemented IR generation feature non-fixed multi-payload enum layout | |
enum State { | |
case Pending | |
case Fulfilled | |
case Rejected | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Request.swift | |
// Promise | |
// | |
// Created by Benjamin Gruenbaum on 8/13/14. | |
// Copyright (c) 2014 Tipranks. All rights reserved. | |
// | |
import Foundation | |
class Request { | |
class func Get(url:String) -> Promise<NSData?>{ | |
println("Inside Request.get") | |
return Promise({ (resolve, reject) in | |
println("Inside Request.get inner promise ctor") | |
Web.Get(url, handleResponse: { (err, data) in | |
println("Inside Request.get - request is done") | |
if let error = err { | |
println("Inside Request.get in reject") | |
reject(err) | |
} | |
println("Inside Request.get in resolve") | |
resolve(data) | |
}) | |
}) | |
} | |
class func GetJson(url:String) -> Promise<AnyObject>{ | |
return Get(url).then({ (data) -> (AnyObject) in | |
if let data:NSData = data { | |
var err : NSError? | |
let json: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &err) | |
if let j: AnyObject = json { | |
println("Inside Request.get - deserialization good") | |
return j | |
} else if let error = err{ | |
return Promise<AnyObject>.reject(NSError(domain:"GetJson",code:0,userInfo:["Reason":"Could not deserialize JSON", "Error": err!])) | |
} else { | |
return Promise<AnyObject>.reject(NSError(domain:"GetJson",code:0,userInfo:["Reason":"JSON deserialized into nil but no error"])) | |
} | |
} | |
return Promise<AnyObject>.reject(NSError(domain:"GetJson",code:0,userInfo:["Reason":"Got nil response from web"])) | |
}) | |
} | |
} | |
internal class Web{ | |
class func Post(url:String,data:NSData, handleResponse:(NSError?,AnyObject?) -> ()){ | |
var req = NSMutableURLRequest(URL: NSURL(string:url)) | |
req.HTTPMethod = "POST" | |
req.HTTPBody = data | |
Web.Request(url, request: req, handleResponse: handleResponse) | |
} | |
class func PostJson(url:String,data:AnyObject?, handleResponse:(NSError?,NSData?) -> ()){ | |
var req = NSMutableURLRequest(URL: NSURL(string:url)) | |
req.HTTPMethod = "POST"; | |
req.setValue("application/json", forHTTPHeaderField: "Content-Type") | |
req.setValue("TipRanks iOS client", forHTTPHeaderField: "User-Agent") | |
var e:NSError? | |
let jsonData = NSJSONSerialization.dataWithJSONObject( | |
data, | |
options: NSJSONWritingOptions(0), | |
error: &e) | |
let asString = NSString(data:jsonData,encoding:NSUTF8StringEncoding) | |
if e != nil { | |
return handleResponse(e, nil) | |
} | |
req.HTTPBody = jsonData | |
Web.Request(url, request: req, handleResponse: handleResponse); | |
} | |
class func Request(url:String,request:NSURLRequest? = nil, handleResponse:(NSError?,NSData?) -> ()){ | |
let req = request ?? NSURLRequest(URL: NSURL(string:url)) | |
NSURLConnection.sendAsynchronousRequest(req, queue: NSOperationQueue.mainQueue(), completionHandler: { (resp: NSURLResponse!, data, err) in | |
if let error = err { | |
if error.description.rangeOfString("-1012") != nil{ // 401 | |
return handleResponse(NSError(domain: "WebDomain", code: 401, userInfo: ["Data":data, "Error": error]),nil) | |
} | |
return handleResponse(error, nil) | |
} | |
if let httpResp = resp as? NSHTTPURLResponse { | |
if httpResp.statusCode >= 400 { | |
handleResponse(NSError(domain: "WebDomain", code: httpResp.statusCode, userInfo: ["Data":data]), nil) | |
} | |
if let data:NSData = data { | |
handleResponse(nil, data) | |
} else { | |
handleResponse(err, nil) | |
} | |
} else { | |
handleResponse(NSError(domain: "WebDomain", code: 404, userInfo: ["Reason":"Web Request error, could not cast to NSHTTPURLResponse"]), nil) | |
} | |
}) | |
} | |
class func Get(url:String,handleResponse:(NSError?, NSData?) -> ()){ | |
Request(url, handleResponse:handleResponse) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment