I am using ReactiveCocoa in an app which makes calls to remote web APIs. But before any thing can be retrieved from a given API host, the app must provide the user\'s creden
Thinking about token will expire later and we have to refresh it.
I store token in a MutableProperty, and used a lock to prevent multiple expired request to refresh the token, once the token is gained or refreshed, just request again with new token.
For the first few requests, since there's no token, request signal will flatMap to error, and thus trigger refreshAT, meanwhile we do not have refreshToken, thus trigger refreshRT, and set both at and rt in the final step.
here's full code
static var headers = MutableProperty(["TICKET":""])
static let atLock = NSLock()
static let manager = Manager(
configuration: NSURLSessionConfiguration.defaultSessionConfiguration()
)
internal static func GET(path:String!, params:[String: String]) -> SignalProducer<[String: AnyObject], NSError> {
let reqSignal = SignalProducer<[String: AnyObject], NSError> {
sink, dispose in
manager.request(Router.GET(path: path, params: params))
.validate()
.responseJSON({ (response) -> Void in
if let error = response.result.error {
sink.sendFailed(error)
} else {
sink.sendNext(response.result.value!)
sink.sendCompleted()
}
})
}
return reqSignal.flatMapError { (error) -> SignalProducer<[String: AnyObject], NSError> in
return HHHttp.refreshAT()
}.flatMapError({ (error) -> SignalProducer<[String : AnyObject], NSError> in
return HHHttp.refreshRT()
}).then(reqSignal)
}
private static func refreshAT() -> SignalProducer<[String: AnyObject], NSError> {
return SignalProducer<[String: AnyObject], NSError> {
sink, dispose in
if atLock.tryLock() {
Alamofire.Manager.sharedInstance.request(.POST, "http://example.com/auth/refresh")
.validate()
.responseJSON({ (response) -> Void in
if let error = response.result.error {
sink.sendFailed(error)
} else {
let v = response.result.value!["data"]
headers.value.updateValue(v!["at"] as! String, forKey: "TICKET")
sink.sendCompleted()
}
atLock.unlock()
})
} else {
headers.signal.observe(Observer(next: { value in
print("get headers from local: \(value)")
sink.sendCompleted()
}))
}
}
}
private static func refreshRT() -> SignalProducer<[String: AnyObject], NSError> {
return SignalProducer<[String: AnyObject], NSError> {
sink, dispose in
Alamofire.Manager.sharedInstance.request(.POST, "http://example.com/auth/refresh")
.responseJSON({ (response) -> Void in
let v = response.result.value!["data"]
headers.value.updateValue(v!["at"] as! String, forKey: "TICKET")
sink.sendCompleted()
})
}
}