【问题标题】:RXSwift + Moya + Error Handling + Refresh ButtonRXSwift + Moya + 错误处理 + 刷新按钮
【发布时间】:2019-03-17 04:08:22
【问题描述】:

我正在尝试设置一个在按下按钮后刷新用户数据的表格视图。 RXSwift 用于整个事件链。 Moya 用于路由。

我正在尝试使用 Moya 给出的标准错误处理,即:

provider.rx.request(.userProfile("ashfurrow")).subscribe { event in
    switch event {
    case let .success(response):
        image = UIImage(data: response.data)
    case let .error(error):
        print(error)
    }
}

我能够让它工作的唯一方法是使用内部订阅方法。请看下面的代码。谁能想到一种不需要内部订阅的方法?看起来有点笨拙。

class ViewController: UIViewController {
    @IBOutlet weak var refreshBtn: UIButton!
    @IBOutlet weak var tableView: UITableView!

    let provider = MoyaProvider<MyAPI>()

    let disposeBag = DisposeBag()

    var latestUsers = Variable<[User]>([])

    override func viewDidLoad() {
        super.viewDidLoad()

        setupObservableBtnRefreshWithDataFetch()
        bindDataToTableView()
    }

    func setupObservableBtnRefreshWithDataFetch() {
        let refreshStream = refreshBtn.rx.tap.startWith(())

        let responseStream = refreshStream.flatMapLatest { _ -> SharedSequence<DriverSharingStrategy, [User]> in
            let request = self.provider.rx.request(.showUsers)

            // Inner Subscribe here, to be able to use the standard Moya subscribe methods for error handling
            request.subscribe { event in
                switch event {
                case .success(let user):
                    print("Success")
                case .error(let error):
                    print("Error occurred: \(error.localizedDescription)")
                }
            }

            return request
                .filterSuccessfulStatusCodes()
                .map([User].self)
                .asDriver(onErrorJustReturn: [])
        }

        let nilOnRefreshTapStream: Observable<[User]> = refreshBtn.rx.tap.map { _ in return [] }
        let tableDisplayStream = Observable.of(responseStream, nilOnRefreshTapStream)
            .merge()
            .startWith([])

        tableDisplayStream
            .subscribe { event in
                switch event {
                case .next(let users):
                    print("Users are:")
                    print(users)
                    self.latestUsers.value = users
                    break
                case .completed:
                    break
                case .error(let error):
                    print("Error occurred: \(error.localizedDescription)")
                    break
                }
            }
            .disposed(by: self.disposeBag)
    }

    func bindDataToTableView() {
        latestUsers.asObservable()
            .bind(to: tableView.rx.items(cellIdentifier: "cell", cellType: UITableViewCell.self)) { (_, model: User, cell: UITableViewCell) in
                cell.textLabel?.text = model.login
            }
            .disposed(by: disposeBag)
    }
}      

class User: Decodable {
    var name: String?
    var mobile: Int?
    var userRequestedTime: String?
    var login: String?

    init(name: String, mobile: Int, login: String = "") {
        self.name = name
        self.mobile = mobile
        self.login = login
    }
}

【问题讨论】:

  • 我对 Moya 不熟悉,但是对request 的双重订阅(您的手动订阅和对refreshStream.flatMap 的返回值的任何订阅)似乎都不对。如果省略手动订阅会发生什么?
  • 订阅事件中可用的字段发生变化(如 .success 更改完成)。虽然效果很好,但我已经做到了。

标签: swift rx-swift moya


【解决方案1】:

我研究了 Moya 并了解到它是网络操作的包装器。

内部订阅服务的目的并不完全清楚 - 根据我的理解,它触发了一个相同但独立的网络请求,这不应该影响其他请求订阅。 似乎 refreshButton 水龙头在 tableDisplayStream 中发出两个元素(来自 responseStream(来自 refreshStream)和 nilOnRefreshTapStream)。

请注意,不推荐使用变量。就个人而言,我也更喜欢 .debug().subscribe() 在订阅关闭中手动打印事件。

基于此,我将代码编写如下。我还没有测试过。希望对您有所帮助!

class ViewController: UIViewController {
    // ...

    private let provider = MoyaProvider<MyAPI>()
    private let disposeBag = DisposeBag()

    /// Variable<T> is deprecated; use BehaviorRelay instead
    private let users = BehaviorRelay<[User]>(value: [])

    private func setupObservableBtnRefreshWithDataFetch() {
        refreshBtn.rx.tap
           .startWith(()) // trigger initial load
           .flatMapLatest { _ in 
               self.provider.rx.request(.showUsers)
                   .debug("moya request")
                   .filterSuccessfulStatusCodes()
                   .map([User].self)
                   .asDriver(onErrorJustReturn: []) // don't let the error escape
           } 
           .drive(users)
           .disposed(by: disposeBag)
    }

    private func bindDataToTableView() {
        users
            .asDriver()
            .debug("driving table view ")
            .drive(tableView.rx.items /* ... */)
            .disposed(by: disposeBag)
    }
}      

【讨论】:

  • 谢谢。我遇到的主要问题是处理成功/错误情况。当您订阅它的请求时,Moya 似乎有一组特定的处理程序,而不是普通的 RXSwift。我对成功特别感兴趣,而不仅仅是完成,这就是我必须使用内部订阅的原因。
  • Moya 的 .request(_) 方法返回 Single&lt;Response&gt;,这是一种特殊的 Observable,它是 RxSwift 的一部分。您能否发布MyAPI 的代码,以便我们尝试重现您描述的行为?
猜你喜欢
  • 2017-01-12
  • 2019-03-18
  • 1970-01-01
  • 2023-03-12
  • 2016-03-14
  • 1970-01-01
  • 2016-08-23
  • 1970-01-01
  • 2017-04-27
相关资源
最近更新 更多