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

Posted

技术标签:

【中文标题】如何将 UIButton 的 rx_tap 直接转换为网络请求,而不在嵌套订阅中发送请求?【英文标题】:How to transform rx_tap of UIButton to a network request directly without sending the request in a nested subscribe? 【发布时间】:2015-11-28 06:22:43 【问题描述】:

假设我有一个 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)

在这种情况下,即使网络请求发生错误,我也可以通过点击按钮重新发送请求。

虽然代码运行得很好,但由于嵌套订阅,我觉得它有点难看。我尝试了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)

好像上面的两个sn-ps的逻辑不同。后一种订阅仅在没有错误发生时才有效,就像正常订阅一样,而不是每次点击按钮时都订阅网络请求。

有没有办法把正式的sn-p弄平?


添加了嵌套订阅的sn-p:

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)

可以使用以下代码捕获错误:

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

如有必要,我还需要取消网络请求。如果我在上面的sn-p中手动dispose disposable,订阅就会被dispose,我无法再次发送请求。

【问题讨论】:

【参考方案1】:

您有 2 种方法来实现所请求的行为。第一个是用户map,然后是switchLatest,这是经典的方式。第二个是用户flatMap,如果你需要在网络请求序列中catch/retry

Ash Furrow 在workshop demo code 中有一个非常好的示例,使用第二个示例和 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)

或者,您可以查看Github example in the RxSwift repository 了解如何处理这些事情。

【讨论】:

已发现错误。我还有另一个问题:取消请求而不处置它。 = =!!!

以上是关于如何将 UIButton 的 rx_tap 直接转换为网络请求,而不在嵌套订阅中发送请求?的主要内容,如果未能解决你的问题,请参考以下文章

如何将NSData 型直接转成NSInteger型

第三章:MySQL日期时间函数讲解:将时间戳直接转换成日期时间的函数

mysql 将时间戳直接转换成日期时间

请问如何把wma文件直接转化成asf文件呢

利用vs将json格式直接转换成Model实体类

封装一个ExcelHelper,方便将Excel直接转成Datatable对象