连锁多个Alamofire请求

jlh*_*ora 46 ios swift alamofire promisekit

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

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

  1. 提出PUT请求
  2. 发出GET请求
  3. 用数据重新加载表

承诺的概念似乎很适合这一点.如果我可以做这样的事情,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
}
Run Code Online (Sandbox Code Playgroud)

但那是不可能的,或者至少我不知道.

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

我是iOS的新手,所以也许有一些我缺少的基础知识.我在其他框架(如Android)中所做的是在后台进程中执行这些操作并使请求同步.但Alamofire本质上是异步的,所以这种模式不是一种选择.

mxc*_*xcl 44

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

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)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:现在,使用:https://github.com/PromiseKit/Alamofire-

  • 你能举一个用例的例子吗?也许实施问题中发布的请求? (9认同)

Eik*_*ike 27

我写了一个逐个处理请求链的类.

我创建了一个RequestChainAlamofire.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
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

我就是这样用的

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


}
Run Code Online (Sandbox Code Playgroud)

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

    let manager = Manager.sharedInstance
    manager.startRequestsImmediately = false
Run Code Online (Sandbox Code Playgroud)

希望它会帮助别人

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
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

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

        }
Run Code Online (Sandbox Code Playgroud)


cno*_*oon 17

你有多种选择.


选项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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这绝对是我推荐的方法.将一个呼叫嵌入另一个呼叫非常简单,并且很容易遵循.它也让事情变得简单.


选项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
}
Run Code Online (Sandbox Code Playgroud)

此选项密度较小,并将内容拆分为较小的块.根据您的需求和响应解析的复杂性,这可能是一种更具可读性的方法.


选项3 - PromiseKit和Alamofire

Alamofire可以轻松处理这个问题而无需拉入PromiseKit.如果你真的想走这条路,你可以使用@mxcl提供的方法.

  • 在我的回答中我没有看到Alamofire处理这个"非常好"的答案.我只是指出了完成任务的三种不同选择.不是PromiseKit的专家,我想我只提供了几个使用Alamofire的选项,第三个选项直接推迟到PromiseKit.将两个请求链接在一起可以直接使用Alamofire轻松完成.超过两个,它开始变得非常笨拙.这是我们将来要调查的事情. (2认同)

Eti*_*ule 6

这是使用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")
            })

        })

    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这就是调度组的用途。这是更好的答案,因为它教给您一个非常有用的概念,供以后使用(当您进入认真的多线程时) (2认同)