【问题标题】:Receive and send observable in RxSwift在 RxSwift 中接收和发送 observable
【发布时间】:2020-01-23 08:17:11
【问题描述】:

我有现有的工作代码,看起来像这样(删除了不必要的东西):

queryTextField.rx.text.orEmpty
            .throttle(.milliseconds(300), scheduler: MainScheduler.instance)
            .distinctUntilChanged()
            .flatMapLatest { query in
                return try AddFriendViewController.getSearchResults(query)
                    .retry(3)
                    .startWith([])
                    .catchErrorJustReturn([])
            }
            .bind(to: tableView.rx.items(cellIdentifier: "cell", cellType: AddFriendTableViewCell.self)) { (row, item, cell) in                
                cell.nameLabel.text = "\(item.firstName) \(item.lastName)"
            }
            .disposed(by: disposeBag)

当您在文本字段中键入名称时,它会自动搜索(通过 API)匹配此查询的朋友。

但我还想添加另一个功能 - 从联系人导入,由附加按钮调用。我有一个本地联系人列表,并使用它们向 API 询问搜索结果。我想在同一个地方显示这些结果。但我不知道如何将这些结果发送到 tableView 中。我想创建一个变量:

var items: Observable<[FriendSearchResult]> = Observable.just([])

但我不知道如何发送/接收数据。有什么线索吗?

编辑:

感谢@andromedainative,现在代码看起来像这样:

var items: BehaviorRelay<[FriendSearchResult]> = BehaviorRelay(value: [])

items.asObservable()
        .bind(to: tableView.rx.items(cellIdentifier: "cell", cellType: AddFriendTableViewCell.self)) { (row, item, cell) in                
            cell.nameLabel.text = "\(item.firstName) \(item.lastName)"
        }
        .disposed(by: disposeBag)

queryTextField.rx.text.orEmpty
        .throttle(.milliseconds(300), scheduler: MainScheduler.instance)
        .distinctUntilChanged()
        .flatMapLatest { query in
            return try AddFriendViewController.getSearchResults(query)
                .retry(3)
                .startWith([])
                .catchErrorJustReturn([])
        }
        .bind(to: items) //this was a missing piece
        .disposed(by: disposeBag)

从 getContacts 传递结果很简单:

items.accept(data)

但是,我有一个特殊情况,我必须刷新数据。如何调用 queryTextField 来调用同一个查询的 API?

我只找到了一个 hacky 方法:

let query = queryTextField.text ?? ""
queryTextField.text = ""
queryTextField.sendActions(for: .valueChanged)
queryTextField.text = query
queryTextField.sendActions(for: .valueChanged)

我必须更改为其他值并将其更改回来。有没有更干净的解决方案?

【问题讨论】:

    标签: ios swift reactive-programming rx-swift


    【解决方案1】:

    您绝对可以使用 rx 将对象绑定到表视图。我将 BehaviorRelay 用于我的数组,因为我希望能够访问项目的 .value 属性。

    这是一个非常伪代码分析器,所以它不会编译,但我希望你能明白它的要点:

    class MyViewController: UITableViewController {
    
      private let disposeBag = DisposeBag()
    
      var myItems: BehaviorRelay<[FeedItem]>
    
      func bindData() {
    
        tableView.rx.setDelegate(self).disposed(by: disposeBag)
    
        myItems.asObservable()
            .bind(to: tableView.rx.items) { [weak self] tableView, row, myItemType in
                guard let `self` = self else { return UITableViewCell() }
                let indexPath = IndexPath(row: row, section: 0)
                let myItem = self.myItems.value[indexPath.row]
                let myCell = tableView.dequeueCell(reuseIdentifier: "MyIdentifier", for: indexPath) as! MyCellType
    
                return myCell
        }.disposed(by: disposeBag)
      }
    
    }
    

    【讨论】:

    • 是的,我明白了,但问题是如何连接这两个案例。
    • 这很简单,只需将另一个按钮调用中的项目的值添加到同一个数组中。所以首先你有你添加到数组中的查询结果。 var queriedResults = [SomeItems] 然后你就有了从你的按钮按下得到的项目。 func handleButtonPress() { ... let myNewImportedContacts = [SomeItems] myItems.accept(myItems.value + myNewImportedContacts) // 现在你在同一个表视图数据源中 }
    • 让我换个方式问这个问题。我应该在 flatMapLatest 之后调用什么来将结果传递给项目?
    【解决方案2】:

    一般来说,当你想组合两个或多个 observable 发出的值时,你需要使用combineLatestzipwithLatestFrommerge 之一(可能与scan 一起使用)。这取决于您应该使用哪个上下文。这篇文章可能会有所帮助:Recipes for Combining Observables in RxSwift

    以下是针对您的具体情况的建议。下面的代码添加了几个您需要实现的函数:func getContacts() -&gt; Observable&lt;[Contact]&gt;func getSearchResults(_ contact: Contact) -&gt; Observable&lt;[Friend]&gt;

    let singleSearchResult = queryTextField.rx.text.orEmpty
        .debounce(.milliseconds(300), scheduler: MainScheduler.instance)
        .distinctUntilChanged()
        .flatMapLatest { query in
            return try getSearchResults(query)
                .retry(3)
                .startWith([])
                .catchErrorJustReturn([])
        }
    
    let contactsSearchResult = checkContactsButton.rx.tap
        .flatMapLatest { getContacts() }
        .flatMapLatest {
            Observable.combineLatest($0.map {
                getSearchResults($0)
                    .catchErrorJustReturn([])
            })
            .startWith([])
        }
        .map { $0.flatMap { $0 } }
    
    Observable.merge(singleSearchResult, contactsSearchResult)
        .bind(to: tableView.rx.items(cellIdentifier: "cell", cellType: AddFriendTableViewCell.self)) { (row, item, cell) in
            cell.nameLabel.text = "\(item.firstName) \(item.lastName)"
    }
    .disposed(by: disposeBag)
    

    顺便说一句,你的getContacts 不应该扔东西。它的错误应该从 Observable 发出,而不是抛出。此外,您应该在文本字段上使用 debounce,而不是节流。后者更适合 Void 类型,而不是字符串。

    【讨论】:

    • 很好的提示,谢谢。但是,就我而言,我不想合并数据,而是替换它。正如我在编辑的答案中所述,我刚刚使用了接受(您可能想帮助我解决此设置中出现的另一个问题,请参阅我的编辑)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-14
    • 1970-01-01
    • 2015-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多