Alamofire асинхронный completionHandler для JSON-запроса


используя фреймворк AlamoFire я заметил, что completionHandler запускается в основном потоке. Im интересно, если код ниже является хорошей практикой для создания основной задачи импорта данных в обработчике завершения:

Alamofire.request(.GET, "http://myWebSite.com", parameters: parameters)
            .responseJSON(options: .MutableContainers) { (_, _, JSON, error) -> Void in
                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), { () -> Void in
                    if let err = error{
                        println("Error:(error)")
                        return;
                    }

                    if let jsonArray = JSON as? [NSArray]{                       
                        let importer = CDImporter(incomingArray: jsonArray entity: "Artist", map: artistEntityMap);

                    }
                });
            }
3 71

3 ответа:

это действительно хороший вопрос. Ваш подход вполне обоснован. Тем не менее, Alamofire действительно может помочь вам оптимизировать это еще больше.

Ваш Пример Кода Диспетчерской Очереди Разбивка

в примере кода Вы прыгаете между следующими очередями отправки:

  1. очередь отправки NSURLSession
  2. очередь отправки TaskDelegate для проверки и обработки сериализатора
  3. основная очередь отправки для вызова вашего обработчик завершения
  4. очередь с высоким приоритетом для обработки JSON
  5. основная очередь отправки для обновления пользовательского интерфейса (при необходимости)

как вы можете видеть, вы прыгаете повсюду. Давайте рассмотрим альтернативный подход, использующий мощную функцию внутри Alamofire.

Очереди Отправки Ответа Alamofire

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

в то время как GCD удивительно прыгает между очередями отправки, вы хотите избежать перехода в очередь, которая занята (например, основной поток). Устраняя переход обратно к основному потоку в середине асинхронной обработки, вы можете значительно ускорить процесс. В следующем примере показано, как это сделать с помощью логики Alamofire прямо из коробки.

Alamofire 1.x

let queue = dispatch_queue_create("com.cnoon.manager-response-queue", DISPATCH_QUEUE_CONCURRENT)

let request = Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
request.response(
    queue: queue,
    serializer: Request.JSONResponseSerializer(options: .AllowFragments),
    completionHandler: { _, _, JSON, _ in

        // You are now running on the concurrent `queue` you created earlier.
        println("Parsing JSON on thread: \(NSThread.currentThread()) is main thread: \(NSThread.isMainThread())")

        // Validate your JSON response and convert into model objects if necessary
        println(JSON)

        // To update anything on the main thread, just jump back on like so.
        dispatch_async(dispatch_get_main_queue()) {
            println("Am I back on the main thread: \(NSThread.isMainThread())")
        }
    }
)

Alamofire 3.x (Swift 2.2 и 2.3)

let queue = dispatch_queue_create("com.cnoon.manager-response-queue", DISPATCH_QUEUE_CONCURRENT)

let request = Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
request.response(
    queue: queue,
    responseSerializer: Request.JSONResponseSerializer(options: .AllowFragments),
    completionHandler: { response in
        // You are now running on the concurrent `queue` you created earlier.
        print("Parsing JSON on thread: \(NSThread.currentThread()) is main thread: \(NSThread.isMainThread())")

        // Validate your JSON response and convert into model objects if necessary
        print(response.result.value)

        // To update anything on the main thread, just jump back on like so.
        dispatch_async(dispatch_get_main_queue()) {
            print("Am I back on the main thread: \(NSThread.isMainThread())")
        }
    }
)

Alamofire 4.x (Swift 3)

let queue = DispatchQueue(label: "com.cnoon.response-queue", qos: .utility, attributes: [.concurrent])

Alamofire.request("http://httpbin.org/get", parameters: ["foo": "bar"])
    .response(
        queue: queue,
        responseSerializer: DataRequest.jsonResponseSerializer(),
        completionHandler: { response in
            // You are now running on the concurrent `queue` you created earlier.
            print("Parsing JSON on thread: \(Thread.current) is main thread: \(Thread.isMainThread)")

            // Validate your JSON response and convert into model objects if necessary
            print(response.result.value)

            // To update anything on the main thread, just jump back on like so.
            DispatchQueue.main.async {
                print("Am I back on the main thread: \(Thread.isMainThread)")
            }
        }
    )

Alamofire Dispatch Queue Breakdown

вот разбивка различных очередей отправки, связанных с этим подходом.

  1. очередь отправки NSURLSession
  2. очередь отправки TaskDelegate для проверки и сериализатора обработка
  3. очередь параллельной отправки Custom manager для обработки JSON
  4. основная очередь отправки для обновления пользовательского интерфейса (при необходимости)

резюме

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

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

небольшое обновление для Swift 3.0, Alamofire (4.0.1), Edit for @cnoon ответ:

let queue = DispatchQueue(label: "com.cnoon.manager-response-queue",
                          qos: .userInitiated,
                          attributes:.concurrent)
Alamofire?.request(SERVER_URL, method: .post,
parameters: ["foo": "bar"], 
encoding: JSONEncoding.default,//by default
headers: ["Content-Type":"application/json; charset=UTF-8"])
.validate(statusCode: 200..<300).//by default
responseJSON(queue: queue, options: .allowFragments, 
completionHandler: { (response:DataResponse<Any>) in

        switch(response.result) {
        case .success(_):
            break
        case .failure(_):
            print(response.result.error)
            if response.result.error?._code == NSURLErrorTimedOut{
                //TODO: Show Alert view on netwok connection.
            }
            break
        }
    })

просто дополняя идеальный ответ от @cnoon, если вы любите меня использует ResponseObjectSerializable вы можете встроить это параллельное поведение в само расширение запроса:

extension Request {
    public func responseObject<T: ResponseObjectSerializable>(completionHandler: Response<T, NSError> -> Void) -> Self {
        let responseSerializer = ResponseSerializer<T, NSError> { request, response, data, error in
            guard error == nil else { return .Failure(error!) }

            let JSONResponseSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
            let result = JSONResponseSerializer.serializeResponse(request, response, data, error)

            switch result {
            case .Success(let value):
                if let
                    response = response,
                    responseObject = T(response: response, representation: value)
                {
                    return .Success(responseObject)
                } else {
                    let failureReason = "JSON could not be serialized into response object: \(value)"
                    let error = Error.errorWithCode(.JSONSerializationFailed, failureReason: failureReason)
                    return .Failure(error)
                }
            case .Failure(let error):
                return .Failure(error)
            }
        }

        let queue = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT)
        return response(queue: queue, responseSerializer: responseSerializer) { response in
            dispatch_async(dispatch_get_main_queue()) {
                completionHandler(response)
            }
        }
    }
}