JSON 요청을위한 AlamoFire 비동기 완료 핸들러


80

AlamoFire 프레임 워크를 사용한 후, CompletionHandler가 메인 스레드에서 실행되는 것을 확인했습니다. 아래 코드가 완료 핸들러 내에서 Core Data 가져 오기 작업을 만드는 데 좋은 방법인지 궁금합니다.

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

                    }
                });
            }

답변:


156

이것은 정말 좋은 질문입니다. 귀하의 접근 방식은 완벽하게 유효합니다. 그러나 Alamofire는 실제로이를 더욱 간소화하는 데 도움이 될 수 있습니다.

예제 코드 디스패치 대기열 분석

예제 코드에서 다음 디스패치 큐 사이를 점프합니다.

  1. NSURLSession 디스패치 큐
  2. 유효성 검사 및 직렬 변환기 처리를위한 TaskDelegate 디스패치 큐
  3. 완료 핸들러를 호출하기위한 기본 디스패치 큐
  4. JSON 처리를위한 높은 우선 순위 대기열
  5. 사용자 인터페이스를 업데이트하기위한 기본 디스패치 대기열 (필요한 경우)

보시다시피, 당신은 여기 저기 뛰어 다니고 있습니다. Alamofire 내부의 강력한 기능을 활용하는 대체 접근 방식을 살펴 보겠습니다.

Alamofire 응답 디스패치 대기열

Alamofire는 자체 저수준 처리에 최적의 접근 방식이 내장되어 있습니다. response궁극적으로 모든 사용자 지정 응답 serializer에 의해 호출되는 단일 메서드는 사용하기로 선택한 경우 사용자 지정 디스패치 큐를 지원합니다.

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 디스패치 대기열 분석

다음은이 접근 방식과 관련된 다양한 디스패치 큐의 분석입니다.

  1. NSURLSession 디스패치 큐
  2. 유효성 검사 및 직렬 변환기 처리를위한 TaskDelegate 디스패치 큐
  3. JSON 처리를위한 커스텀 매니저 동시 디스패치 큐
  4. 사용자 인터페이스를 업데이트하기위한 기본 디스패치 대기열 (필요한 경우)

요약

기본 디스패치 대기열로 돌아가는 첫 번째 홉을 제거함으로써 잠재적 인 병목 현상을 제거하고 전체 요청 및 처리를 비동기식으로 만들 수 있습니다. 대박!

그렇긴해도 Alamofire가 실제로 작동하는 방식의 내부에 익숙해지는 것이 얼마나 중요한지 충분히 강조 할 수 없습니다. 자신의 코드를 개선하는 데 실제로 도움이되는 무언가를 언제 찾을 수 있는지 결코 알 수 없습니다.


3
철저한 설명에 감사드립니다, @cnoon. response이제 메서드 의 두 번째 매개 변수 responseSerializerserializer(Alamofire 3.0에서) 대신 호출 된 것으로 보입니다. 그것은 Cannot call value of non-function type 'NSHTTPURLResponse?'나를 약간 혼란스럽게 만드는 오류를 일으켰습니다 .
Hélène Martin

변경 사항을 업로드하세요. 코드가 작동하지 않습니다. Swift 2.1, XCode 7.1
Beraliv 2011

responseJSON은 어떻습니까? 어떻게 큐 매개 변수에 전달할 수 있습니다
OMGPOP

@cnoon, Swift 3에 대한 업데이트도 추가하면 좋을 수 있습니다.
Mike.R

이제 Swift 3에서도 작동합니다. Brilliant
dejavu89

2

Swift 3.0, Alamofire (4.0.1), @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
        }
    })

1

@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)
            }
        }
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.