Последовательность действий с RxSwift

Я использую RxSwift просто для своего кода. В моем текущем проекте я хотел бы применить принципы RxSwift к беспорядку блоков завершения из LayerKit:

layerClient.connectWithCompletion { (success, error) -> () in
  if (!success) {
     // Error     
  } else {
    layerClient.requestAuthenticationNonceWithCompletion { (nonce, error) -> () in
      // Even more blocks
    }
  }
}

Я думаю примерно так:

// In extension
public func rx_connect() -> Observable<Bool> {
    return create { observer in

        self.connectWithCompletion { (success, error) -> ()in
            if (success) {
                observer.on(.Next(success))
                observer.on(.Completed)
            } else {
                observer.on(.Error(error))
            }
        }
        return NopDisposable.instance
    }
} 

public func rx_requestAuthenticationNonce() -> Observable<String> {
    // Same for annother method
}

// In AppDelegate
self.layerClient.rx_connect()
 .then() // requestAuthenticationNonceWithCompletion and use the nonce for next action
 .then()
 .subscribeNext(…
 .onError(… // To catch all errors

RxSwift не имеет then() метода. Есть ли другой способ сделать эту цепочку, или я неправильно думаю о том, как использовать ReactiveX в целом?


person Niklas    schedule 23.09.2015    source источник


Ответы (2)


Правильно, в RxSwift нет оператора then, потому что это имя очень рискованно.

then может быть много чего в мире реактивных расширений map flatMap или даже switchLatest, в зависимости от контекста.

В этом случае я бы предложил использовать flatMap, потому что он будет возвращен с использованием map - это Observable of Observables, которые в большинстве случаев сложно комбинировать и управлять. Итак, я бы сделал:

self.layerClient.rx_connect()
 .flatMap(){ _ in
   return rx_requestAuthenticationNonce()
             .catchError(displayError)
 }
 .subscribeNext(…
 .onError(… // To catch all errors

В этом случае я бы использовал flatMap вместо map, потому что тогда я могу отлавливать ошибки, не прерывая последовательность, в случае сбоя rx_requestAuthenticationNonce().

Будьте осторожны при использовании оператора catchError, этот оператор завершит последовательность после восстановления, и любое дополнительное событие после этого будет проигнорировано.

person bontoJR    schedule 11.10.2015
comment
если then имя является проблемой, мы можем выбрать лучшее имя, но этот вариант использования очень распространен - person onmyway133; 24.04.2016

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

       let client = RxSimpleLazyHttpClient.sharedInstance
       let uuid = UIDevice().identifierForVendor!.UUIDString

       let helloheaders = ["X-User-Identifier": "id:" + uuid]
       let helloRequest: Observable<StringDictionary> = client.makeRequest(verb: .GET, url: NSURL(string: self.API_HOST + self.API_HELLO)!, parameters: [:], headers: helloheaders)
            .map { result in
                if (self.enableDebugOutput) {
                    self.debug(result)
                }

                let json = JSON(data: result.2!)
                let userKey = json["user_key"].string
                let userSecretHash = json["user_secret_hash"].string

                return ["user_key": userKey ?? "", "user_secret_hash": userSecretHash ?? ""]
            }

        let jointAuthRequest: (StringDictionary -> Observable<StringDictionary>) = { [unowned self] stringDictionary in
            Logger.D(stringDictionary.debugDescription)

            let userKey = stringDictionary["user_key"]
            let headers = ["X-User": userKey ?? ""]
            return client.makeRequest(verb: .GET, url: NSURL(string: self.API_HOST + self.API_AUTH)!, parameters: [:], headers: headers)
                .map { result in

                    if (self.enableDebugOutput) {
                        self.debug(result)
                    }

                    let json = JSON(data: result.2!)
                    let nonce = json["nonce"].string
                    var result = [String: String]()
                    result["nonce"] = nonce

                    return result.merge(stringDictionary)
                }
        }

        let jointAuthNonceRequest: (StringDictionary -> Observable<StringDictionary>) = { [unowned self] stringDictionary in
            Logger.D(stringDictionary.debugDescription)

            let nonce = stringDictionary["nonce"] ?? ""
            let userSecretHash = stringDictionary["user_secret_hash"] ?? ""
            let headers = ["X-Pass": (userSecretHash + nonce).sha1().webSafeBase64()]

            return client.makeRequest(verb: .GET, url: NSURL(string: self.API_HOST + self.API_AUTH + "/" + nonce)!, parameters: [:], headers: headers)
                .map { result in

                    if (self.enableDebugOutput) {
                        self.debug(result)
                    }

                    let json = JSON(data: result.2!)
                    let sessionKey = json["session_key"].string
                    let sessionSecret = json["session_secret"].string
                    var result = [String: String]()
                    result["session_key"] = sessionKey
                    result["session_secret"] = sessionSecret
                    return result.merge(stringDictionary)
                }
        }

        let jointResult: (StringDictionary -> Observable<Bool>) = { result in
            let userKey = result["user_key"]
            let userSecretHash = result["user_secret_hash"]
            let sessionKey = result["session_key"]
            let sessionSecret = result["session_secret"]

            if userKey == nil || userSecretHash == nil || sessionKey == nil || sessionSecret == nil {
                Logger.D("Auth Fail")
                return just(false)
            }

            /* You can store session key here */

            return just(true)

        }

        return helloRequest
            .shareReplay(1)
            .observeOn(RxScheduler.sharedInstance.mainScheduler)
            .flatMap(jointAuthRequest)
            .flatMap(jointAuthNonceRequest)
            .flatMap(jointResult)

Это метод makeRequest.

public func makeRequest(verb verb: Alamofire.Method, url: NSURL, parameters: [String : String]?, headers: [String : String]?) -> Observable<RxRequestResult> {
    return create { observer in
        Alamofire.request(verb, url, parameters: nil, encoding: ParameterEncoding.URL, headers: headers)
            .response { request, response, data, error in
                observer.onNext((request, response, data, error))
            }
        return AnonymousDisposable {
            // when disposed
        }
    }
}
person Zhenghong Wang    schedule 09.12.2015
comment
используя concat, оба запроса должны быть одного типа - person onmyway133; 24.04.2016