简体   繁体   English

如何直接将UIButton的rx_tap转换为网络请求,而无需在嵌套订阅中发送请求?

[英]How to transform rx_tap of UIButton to a network request directly without sending the request in a nested subscribe?

Suppose that I have a UIButton loginButton , I want to send a network request while tapping the button with the following code: 假设我有一个UIButton loginButton ,我想在使用以下代码点击按钮时发送一个网络请求:

override func viewDidLoad() {
    super.viewDidLoad()

    let session = self.session // NSURLSession
    loginButton.rx_tap.subscribeNext { [unowned self] in
        session.rx_response(myRequest).subscribe { event in
            switch event {
            case .Next(let data, let response):
                // Handling Response
            case .Error(let error):
                // Handling Error
            default:
                return
            }
        }.addDisposableTo(disposeBag)
     }.addDisposableTo(disposeBag)
}

And in such case I can resend the request by tapping the button even if an error occurred with the network request. 在这种情况下,即使网络请求发生错误,我也可以通过点击按钮重新发送请求。

Although the code works very well, I thought it is a little bit ugly due to the nested subscription. 虽然代码效果很好,但是由于嵌套订阅,我认为它有点难看。 I tried the flatMap method to flatten the subscription: 我尝试了flatMap方法来展平订阅:

loginButton.rx_tap
    .flatMap {
        return session.rx_response(myRequest)
    }
    .subscribe { event in
        switch event {
        case .Next(let data, let response):
            print("Next")
        case .Error(let error):
            print(error)
        default:
            return
        }
     }
     .addDisposableTo(disposeBag)

It seems that the two snippets above are of different logic. 上面的两个代码片段似乎具有不同的逻辑。 The latter subscription only works while no error happened just like normal subscription rather than subscribe the network request every time the button has been tapped. 后一种订阅仅在正常订阅没有出错的情况下起作用,而不是在每次点击按钮时订阅网络请求。

Is there any way to flatten the formal snippet? 有什么办法可以压平正式片段吗?


Added a snippet of nested subscription: 添加了嵌套订阅的代码段:

loginButton.rx_tap
    .debug("LoginButtonTapped")
    .subscribeNext {
        let disposable = session.rx_response(myRequest)
            .debug("AuthorizationRequest")
            .subscribe(
                onNext: { [unowned self] data, response in
                    // Handling Response
            },
                onError: { [unowned self] error in
                    // Showing Error
            })

        disposable.addDisposableTo(self.disposeBag)

        let alert = UIAlertController(title: "Please Wait.", message: "Requesting Tokens", preferredStyle: .Alert)
        alert.addAction(UIAlertAction(title: "Cancel", style: .Cancel) { _ in
            disposable.dispose()
            alert.dismissViewControllerAnimated(true, completion: nil)
        })
        self.presentViewController(alert, animated: true, completion: nil)
    }.addDisposableTo(disposeBag)

Error can be caught using the following code: 使用以下代码可以捕获错误:

let disposable = loginButton.rx_tap
    .map { session.rx_response(request) }
    .flatMap { [unowned self] in $0.catchError(self.presentError) }
    .subscribeNext { data, response in
        // Handling Response
    }

I also need to cancel the network request if necessary. 如果需要,我还需要取消网络请求。 If I manually dispose the disposable in the above snippet, the subscription will be disposed and I can not send the request again. 如果我在上述摘要中手动处理disposable ,则订阅将被处理,我无法再次发送请求。

You have 2 ways to achieve the requested behaviour. 您有2种方法来实现要求的行为。 The first one is to user map and then switchLatest , this is the classic way. 第一个是用户map ,然后switchLatestswitchLatest ,这是经典方法。 The second one is to user flatMap if you need to catch / retry in the network request sequence. 第二个是如果您需要按网络请求顺序进行catch / retry ,请使用flatMap用户。

Ash Furrow has a very nice example in a workshop demo code using the second example and Moya: Ash Furrow在研讨会演示代码中使用第二个示例和Moya给出了一个非常不错的示例:

    submitButton.rx_tap.map { _ -> Observable<MoyaResponse> in
        return provider.request(.Image)
    }.flatMap() { obs in
        return obs.filterSuccessfulStatusCodes()
            .mapImage()
            .catchError(self.presentError)
            .filter({ (thing) -> Bool in
                return thing != nil
            })
    }
    .take(1)
    .bindTo(imageView.rx_image)
    .addDisposableTo(disposeBag)

Alternatively, you can check the Github example in the RxSwift repository to see how these sort of things are handled. 另外,您可以检查RxSwift存储库中Github示例,以了解如何处理这些事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM