ios 链接多个 Alamofire 请求

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28634995/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 04:56:08  来源:igfitidea点击:

Chain multiple Alamofire requests

iosswiftalamofirepromisekit

提问by jlhonora

I'm looking for a good pattern with which I can chain multiple HTTP requests. I want to use Swift, and preferrably Alamofire.

我正在寻找一种可以链接多个 HTTP 请求的良好模式。我想使用 Swift,最好是Alamofire

Say, for example, I want to do the following:

比如说,我想做以下事情:

  1. Make a PUT request
  2. Make a GET request
  3. Reload table with data
  1. 发出 PUT 请求
  2. 发出 GET 请求
  3. 用数据重新加载表

It seems that the concept of promisesmay be a good fit for this. PromiseKitcould be a good option if I could do something like this:

看来,这个概念的承诺可能是一个非常适合这个。如果我可以做这样的事情,PromiseKit可能是一个不错的选择:

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
}

but that's not possible or at least I'm not aware of it.

但这是不可能的,或者至少我不知道。

How can I achieve this functionality without nesting multiple methods?

如何在不嵌套多个方法的情况下实现此功能?

I'm new to iOS so maybe there's something more fundamental that I'm missing. What I've done in other frameworks such as Android is to perform these operations in a background process and make the requests synchronous. But Alamofire is inherently asynchronous, so that pattern is not an option.

我是 iOS 新手,所以也许我缺少一些更基本的东西。我在Android等其他框架中所做的是在后台进程中执行这些操作并使请求同步。但是Alamofire 本质上是异步的,所以这种模式不是一种选择。

采纳答案by mxcl

Wrapping other asynchronous stuff in promises works like this:

在 Promise 中包装其他异步内容的工作方式如下:

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)
            }
        }
    }
}

Edit: Nowadays, use: https://github.com/PromiseKit/Alamofire-

编辑:如今,使用:https: //github.com/PromiseKit/Alamofire-

回答by Eike

I wrote a class which handles a chain of request one by one.

我写了一个类,它一个接一个地处理请求链。

I created a class RequestChainwich takes Alamofire.Requestas parameter

我创建了一个类RequestChain至极需要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
        }

    }
}

And I use it like this

我像这样使用它

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)")
    }


}

Importent is that you are telling the Manager to not execute the request immediately

重要的是您告诉经理不要立即执行请求

    let manager = Manager.sharedInstance
    manager.startRequestsImmediately = false

Hope it will help someone else

希望它会帮助别人

Swift 3.0 Update

斯威夫特 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
        }

    }
}

Usage Example Swift 3

使用示例 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

        }

回答by cnoon

You have multiple options.

您有多种选择。



Option 1- Nesting Calls

选项 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
        }
    }
}

This is definitely the approach I would recommend. Nesting one call into another is very simple and is pretty easy to follow. It also keeps things simple.

这绝对是我推荐的方法。将一个调用嵌套到另一个调用中非常简单并且很容易遵循。它也使事情变得简单。



Option 2- Splitting into Multiple Methods

选项 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
}

This option is less dense and splits things up into smaller chunks. Depending on your needs and the complexity of your response parsing, this may be a more readable approach.

此选项不太密集,并将事物分成更小的块。根据您的需求和响应解析的复杂性,这可能是一种更具可读性的方法。



Option 3- PromiseKit and Alamofire

选项 3- PromiseKit 和 Alamofire

Alamofire can handle this pretty easily without having to pull in PromiseKit. If you really want to go this route, you can use the approach provided by @mxcl.

Alamofire 可以很容易地处理这个问题,而无需引入 PromiseKit。如果你真的想走这条路,你可以使用@mxcl 提供的方法。

回答by Etienne Beaule

Here is another way to do this (Swift 3, Alamofire 4.x) using a DispatchGroup

这是使用 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 (i.e. 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 (i.e. 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")
            })

        })

    }
}

回答by Vasily Bodnarchuk

Details

细节

  • Alamofire 4.7.2
  • PromiseKit 6.3.4
  • Xcode 9.4.1
  • Swift 4.1
  • 阿拉莫火 4.7.2
  • PromiseKit 6.3.4
  • Xcode 9.4.1
  • 斯威夫特 4.1

Full Sample

完整样本

NetworkService

网络服务

import Foundation
import Alamofire
import PromiseKit

class NetworkService {

    static fileprivate let queue = DispatchQueue(label: "requests.queue", qos: .utility)

    fileprivate class func make(request: DataRequest) -> Promise <(json: [String: Any]?, error: Error?)> {
        return Promise <(json: [String: Any]?, error: Error?)> { seal in
            request.responseJSON(queue: queue) { response in

                // print(response.request ?? "nil")  // original URL request
                // print(response.response ?? "nil") // HTTP URL response
                // print(response.data ?? "nil")     // server data
                //print(response.result ?? "nil")   // result of response serialization

                switch response.result {
                case .failure(let error):
                    DispatchQueue.main.async {
                        seal.fulfill((nil, error))
                    }

                case .success(let data):
                    DispatchQueue.main.async {
                        seal.fulfill(((data as? [String: Any]) ?? [:], nil))
                    }
                }
            }
        }
    }

    class func searchRequest(term: String) -> Promise<(json: [String: Any]?, error: Error?)>{
        let request = Alamofire.request("https://itunes.apple.com/search?term=\(term.replacingOccurrences(of: " ", with: "+"))")
        return make(request: request)
    }
}

Main func

主函数

func run() {
    _ = firstly {
        return Promise<Void> { seal in
            DispatchQueue.global(qos: .background).asyncAfter(deadline: DispatchTime.now() + .seconds(2)) {
                print("1 task finished")
                DispatchQueue.main.async {
                    seal.fulfill(Void())
                }
            }
        }
        }.then {
            return NetworkService.searchRequest(term: "John").then { json, error -> Promise<Void> in
                print("2 task finished")
                //print(error ?? "nil")
                //print(json ?? "nil")
                return Promise { 
 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
    }
}
.fulfill(Void())} } }.then {_ -> Promise<Bool> in print("Update UI") return Promise { ##代码##.fulfill(true)} }.then { previousResult -> Promise<Void> in print("previous result: \(previousResult)") return Promise { ##代码##.fulfill(Void())} } }

Result

结果

enter image description here

在此处输入图片说明

回答by Raju yourPepe

Call itself infinitely and DEFINE END CONDITION. urlring for API link and Dictionary for json

无限地调用自己并定义最终条件。API链接的urlring和json的字典

WE may construct the queue model or delegate

我们可以构造队列模型或委托

##代码##