连锁多个Alamofire请求
我正在寻找一个好的模式,我可以链接多个HTTP请求。 我想用Swift,最好是Alamofire 。
比方说,我想要做以下事情:
- 做一个PUT请求
- 做一个GET请求
- 用数据重新载入表格
看起来承诺的概念可能是适合这个的。 PromiseKit可能是一个不错的select,如果我能做到这样的事情:
NSURLConnection.promise( Alamofire.request( Router.Put(url: "http://httbin.org/put") ) ).then { (request, response, data, error) in Alamofire.request( Router.Get(url: "http://httbin.org/get") ) }.then { (request, response, data, error) in // Process data }.then { () -> () in // Reload table }
但这是不可能的,至less我不知道这一点。
如何在不嵌套多个方法的情况下实现这个function?
我是新来的iOS,所以也许有更重要的东西,我错过了。 我在其他框架(如Android)中所做的是在后台进程中执行这些操作,并使请求同步。 但Alamofire本质上是asynchronous的 ,所以这种模式不是一种select。
在承诺中包装其他asynchronous的东西是这样的:
func myThingy() -> Promise<AnyObject> { return Promise{ fulfill, reject in Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"]).response { (_, _, data, error) in if error == nil { fulfill(data) } else { reject(error) } } } }
编辑:如今,使用: https : //github.com/PromiseKit/Alamofire-
你有多个选项。
选项1 – 嵌套呼叫
func runTieredRequests() { let putRequest = Alamofire.request(.PUT, "http://httpbin.org/put") putRequest.response { putRequest, putResponse, putData, putError in let getRequest = Alamofire.request(.GET, "http://httpbin.org/get") getRequest.response { getRequest, getResponse, getData, getError in // Process data // Reload table } } }
这绝对是我会推荐的方法。 将一个呼叫embedded另一个呼叫非常简单,并且很容易遵循。 它也使事情变得简单。
选项2 – 分成多种方法
func runPutRequest() { let putRequest = Alamofire.request(.PUT, "http://httpbin.org/put") putRequest.response { [weak self] putRequest, putResponse, putData, putError in if let strongSelf = self { // Probably store some data strongSelf.runGetRequest() } } } func runGetRequest() { let getRequest = Alamofire.request(.GET, "http://httpbin.org/get") getRequest.response { [weak self] getRequest, getResponse, getData, getError in if let strongSelf = self { // Probably store more data strongSelf.processResponse() } } } func processResponse() { // Process that data } func reloadData() { // Reload that data }
这个选项不太密集,把事物分成更小的块。 根据您的需求和您的响应parsing的复杂性,这可能是一个更可读的方法。
选项3 – PromiseKit和Alamofire
Alamofire可以很容易地处理这个,而不必拉入PromiseKit。 如果你真的想走这条路,你可以使用@mxcl提供的方法。
我写了一个处理一个请求链的类。
我创build了一个需要Alamofire.Request
作为参数的Alamofire.Request
类
class RequestChain { typealias CompletionHandler = (success:Bool, errorResult:ErrorResult?) -> Void struct ErrorResult { let request:Request? let error:ErrorType? } private var requests:[Request] = [] init(requests:[Request]) { self.requests = requests } func start(completionHandler:CompletionHandler) { if let request = requests.first { request.response(completionHandler: { (_, _, _, error) in if error != nil { completionHandler(success: false, errorResult: ErrorResult(request: request, error: error)) return } self.requests.removeFirst() self.start(completionHandler) }) request.resume() }else { completionHandler(success: true, errorResult: nil) return } } }
我就这样使用它
let r1 = Alamofire.request(Router.Countries).responseArray(keyPath: "endpoints") { (response: Response<[CountryModel],NSError>) in print("1") } let r2 = Alamofire.request(Router.Countries).responseArray(keyPath: "endpoints") { (response: Response<[CountryModel],NSError>) in print("2") } let r3 = Alamofire.request(Router.Countries).responseArray(keyPath: "endpoints") { (response: Response<[CountryModel],NSError>) in print("3") } let chain = RequestChain(requests: [r1,r2,r3]) chain.start { (success, errorResult) in if success { print("all have been success") }else { print("failed with error \(errorResult?.error) for request \(errorResult?.request)") } }
重要的是,您要让Manager不要立即执行请求
let manager = Manager.sharedInstance manager.startRequestsImmediately = false
希望它能帮助别人
Swift 3.0更新
class RequestChain { typealias CompletionHandler = (_ success:Bool, _ errorResult:ErrorResult?) -> Void struct ErrorResult { let request:DataRequest? let error:Error? } fileprivate var requests:[DataRequest] = [] init(requests:[DataRequest]) { self.requests = requests } func start(_ completionHandler:@escaping CompletionHandler) { if let request = requests.first { request.response(completionHandler: { (response:DefaultDataResponse) in if let error = response.error { completionHandler(false, ErrorResult(request: request, error: error)) return } self.requests.removeFirst() self.start(completionHandler) }) request.resume() }else { completionHandler(true, nil) return } } }
用法示例Swift 3
/// set Alamofire default manager to start request immediatly to false SessionManager.default.startRequestsImmediately = false let firstRequest = Alamofire.request("https://httpbin.org/get") let secondRequest = Alamofire.request("https://httpbin.org/get") let chain = RequestChain(requests: [firstRequest, secondRequest]) chain.start { (done, error) in }
这是另一种使用DispatchGroup的方法(Swift 3,Alamofire 4.x)
import Alamofire struct SequentialRequest { static func fetchData() { let authRequestGroup = DispatchGroup() let requestGroup = DispatchGroup() var results = [String: String]() //First request - this would be the authentication request authRequestGroup.enter() Alamofire.request("http://httpbin.org/get").responseData { response in print("DEBUG: FIRST Request") results["FIRST"] = response.result.description if response.result.isSuccess { //Authentication successful, you may use your own tests to confirm that authentication was successful authRequestGroup.enter() //request for data behind authentication Alamofire.request("http://httpbin.org/get").responseData { response in print("DEBUG: SECOND Request") results["SECOND"] = response.result.description authRequestGroup.leave() } authRequestGroup.enter() //request for data behind authentication Alamofire.request("http://httpbin.org/get").responseData { response in print("DEBUG: THIRD Request") results["THIRD"] = response.result.description authRequestGroup.leave() } } authRequestGroup.leave() } //This only gets executed once all the requests in the authRequestGroup are done (ie FIRST, SECOND AND THIRD requests) authRequestGroup.notify(queue: DispatchQueue.main, execute: { // Here you can perform additional request that depends on data fetched from the FIRST, SECOND or THIRD requests requestGroup.enter() Alamofire.request("http://httpbin.org/get").responseData { response in print("DEBUG: FOURTH Request") results["FOURTH"] = response.result.description requestGroup.leave() } //Note: Any code placed here will be executed before the FORTH request completes! To execute code after the FOURTH request, we need the request requestGroup.notify like below print("This gets executed before the FOURTH request completes") //This only gets executed once all the requests in the requestGroup are done (ie FORTH request) requestGroup.notify(queue: DispatchQueue.main, execute: { //Here, you can update the UI, HUD and turn off the network activity indicator for (request, result) in results { print("\(request): \(result)") } print("DEBUG: all Done") }) }) } }
无限地调用自己并定义结束条件。 API链接的urlring和json的Dictionary
我们可以构造队列模型或委托
func getData(urlring : String , para : Dictionary<String, String>) { if intCount > 0 { Alamofire.request( urlring,method: .post, parameters: para , encoding: JSONEncoding.default, headers: nil) .validate() .downloadProgress {_ in } .responseSwiftyJSON { dataResponse in switch dataResponse.result { case .success(let json): print(json) let loginStatus : String = json["login_status"].stringValue print(loginStatus) if loginStatus == "Y" { print("go this") print("login success : int \(self.intCount)") self.intCount-=1 self.getData(urlring: urlring , para : para) } case .failure(let err) : print(err.localizedDescription) } } }else{ //end condition workout } }