swift - jsonserialization - AlamoFire finalización asincrónica Manejador para solicitud JSON
parse json swift 4 (3)
Esta es una muy buena pregunta. Tu enfoque es perfectamente válido. Sin embargo, Alamofire realmente puede ayudarlo a simplificar esto aún más.
Su código de ejemplo Desglose de cola de despacho
En su código de ejemplo, está saltando entre las siguientes colas de despacho:
- Cola de despacho NSURLSession
- Tarea de despacho de tareas para la validación y el procesamiento del serializador
- Cola de despacho principal para llamar a su controlador de finalización
- Cola de alta prioridad para el manejo de JSON
- Cola de despacho principal para actualizar la interfaz de usuario (si es necesario)
Como puedes ver, estás saltando por todo el lugar. Echemos un vistazo a un enfoque alternativo que aprovecha una característica poderosa dentro de Alamofire.
Colas de envío de respuesta de Alamofire
Alamofire tiene un enfoque óptimo integrado en su propio procesamiento de bajo nivel.
El método de
response
única que finalmente es llamado por todos los serializadores de respuesta personalizados tiene soporte para una cola de despacho personalizada si elige usarla.
Si bien GCD es increíble para saltar entre las colas de despacho, desea evitar saltar a una cola que está ocupada (por ejemplo, el hilo principal). Al eliminar el salto de regreso al hilo principal en el medio del procesamiento asíncrono, potencialmente puede acelerar las cosas considerablemente. El siguiente ejemplo demuestra cómo hacer esto usando la lógica de Alamofire directamente de fábrica.
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 y 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)")
}
}
)
Desglose de la cola de despacho de Alamofire
Aquí está el desglose de las diferentes colas de despacho involucradas con este enfoque.
- Cola de despacho NSURLSession
- Tarea de despacho de tareas para la validación y el procesamiento del serializador
- Cola de despacho simultáneo del administrador personalizado para el manejo de JSON
- Cola de despacho principal para actualizar la interfaz de usuario (si es necesario)
Resumen
Al eliminar el primer salto de regreso a la cola de despacho principal, ha eliminado un posible cuello de botella y ha realizado toda su solicitud y procesamiento de forma asincrónica. ¡Increíble!
Dicho esto, no puedo enfatizar lo suficiente lo importante que es familiarizarse con lo interno de cómo funciona realmente Alamofire. Nunca se sabe cuándo puede encontrar algo que realmente pueda ayudarlo a mejorar su propio código.
Después de haber utilizado el marco AlamoFire, he notado que completeHandler se ejecuta en el hilo principal. Me pregunto si el siguiente código es una buena práctica para crear una tarea de importación de datos básicos dentro del controlador de finalización:
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);
}
});
}
Pequeña actualización para Swift 3.0, Alamofire (4.0.1), Editar para respuesta @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
}
})
Simplemente complementando la respuesta perfecta de @cnoon, si te gusto, estoy usando
ResponseObjectSerializable
, puedes incrustar este comportamiento concurrente en la extensión de solicitud:
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)
}
}
}
}