Complemento asynchronous do AlamoFireHandler para solicitação JSON

Depois de usar o framework AlamoFire, notei que o completionHandler é executado no thread principal. Gostaria de saber se o código abaixo é uma boa prática para criar uma tarefa de importação de Core Data no manipulador de conclusão:

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

Esta é uma boa pergunta. Sua abordagem é perfeitamente válida. No entanto, Alamofire pode realmente ajudá-lo a agilizar isso ainda mais.

Seu exemplo de quebra de fila de distribuição de código

Em seu código de exemplo, você está pulando entre as seguintes filas de despacho:

  1. Fila de despacho NSURLSession
  2. TaskDelegate fila de expedição para validação e processamento serializador
  3. Fila de envio principal para chamar seu manipulador de conclusão
  4. Fila de alta prioridade para manipulação de JSON
  5. Fila de despacho principal para atualizar a interface do usuário (se necessário)

Como você pode ver, você está pulando por todo o lugar. Vamos dar uma olhada em uma abordagem alternativa que alavanca um poderoso recurso dentro do Alamofire.

Filas de Envio de Resposta Alamofire

O Alamofire tem uma ótima abordagem incorporada em seu próprio processamento de baixo nível. O método de response única que, em última instância, é chamado por todos os serializadores de resposta personalizados tem suporte para uma fila de distribuição personalizada, se você optar por usá-la.

Embora o GCD seja incrível ao alternar entre as filas de distribuição, você deseja evitar o salto para uma fila ocupada (por exemplo, o segmento principal). Ao eliminar o salto de volta ao thread principal no meio do processamento asynchronous, você pode acelerar consideravelmente as coisas. O exemplo a seguir demonstra como fazer isso usando a lógica Alamofire diretamente da checkbox.

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

Aqui está o detalhamento das diferentes filas de despacho envolvidas com essa abordagem.

  1. Fila de despacho NSURLSession
  2. TaskDelegate fila de expedição para validação e processamento serializador
  3. Fila de expedição simultânea do gerenciador customizado para manipulação de JSON
  4. Fila de despacho principal para atualizar a interface do usuário (se necessário)

Resumo

Ao eliminar o primeiro salto de volta à fila principal de despacho, você eliminou um gargalo potencial, além de ter tornado assíncrona toda a sua solicitação e processamento. Impressionante!

Com isso dito, não posso enfatizar o suficiente como é importante se familiarizar com os aspectos internos de como o Alamofire realmente funciona. Você nunca sabe quando pode encontrar algo que possa realmente ajudá-lo a melhorar seu próprio código.

Atualização pequena para o Swift 3.0, Alamofire (4.0.1), Edit para @cnoon answer:

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

Apenas complementando a resposta perfeita do @cnoon, se você gosta de mim está usando o ResponseObjectSerializable você pode incorporar esse comportamento simultâneo na própria extensão de solicitação:

 extension Request { public func responseObject(completionHandler: Response -> Void) -> Self { let responseSerializer = ResponseSerializer { 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) } } } } 
    Intereting Posts