Подтвердить что ты не робот

Цепочка нескольких запросов Alamofire

Я ищу хороший шаблон, с помощью которого я могу связать несколько HTTP-запросов. Я хочу использовать Swift и предпочтительнее Alamofire.

Скажем, например, я хочу сделать следующее:

  • Сделать запрос PUT
  • Сделать запрос GET
  • Обновить таблицу с данными

Похоже, что концепция promises может быть подходящей для этого. 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
}

но это невозможно или, по крайней мере, я не знаю об этом.

Как я могу достичь этой функции, не вставляя несколько методов?

Я новичок в iOS, поэтому, возможно, есть нечто более фундаментальное, что мне не хватает. То, что я сделал в других рамках, таких как Android, - это выполнить эти операции в фоновом режиме и сделать запросы синхронными. Но Alamofire по сути асинхронен, так что шаблон не является вариантом.

4b9b3361

Ответ 1

Обтекание других асинхронных файлов в 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)
            }
        }
    }
}

Изменить: В настоящее время используйте: https://github.com/PromiseKit/Alamofire-

Ответ 2

У вас есть несколько вариантов.


Вариант 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
        }
    }
}

Это определенно тот подход, который я бы рекомендовал. Вложение одного вызова в другое очень просто и довольно легко следовать. Это также делает вещи простыми.


Вариант 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
}

Эта опция менее плотная и разбивает вещи на более мелкие куски. В зависимости от ваших потребностей и сложности анализа вашего ответа, это может быть более читаемым подходом.


Вариант 3 - PromiseKit и Alamofire

Alamofire может справиться с этим довольно легко, без необходимости использовать PromiseKit. Если вы действительно хотите пройти этот маршрут, вы можете использовать подход, предоставляемый @mxcl.

Ответ 3

Я написал класс, который обрабатывает цепочку запросов один за другим.

Я создал класс 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
        }

    }
}

И я использую его вот так

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


}

Импортирование - это то, что вы сообщаете менеджеру, чтобы он не выполнял запрос немедленно

    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

        }

Ответ 4

Вот еще один способ сделать это (Swift 3, Alamofire 4.x) с помощью DispatchGroup

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

        })

    }
}

Ответ 5

Называть себя бесконечно и DEFINE END CONDITION. urlring для ссылки API и словарь для json

WE может создать модель очереди или делегировать

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