首页 > 解决方案 > 在 RxSwift 中接收和发送 observable

问题描述

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

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)

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

标签: iosswiftreactive-programmingrx-swift

解决方案


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

这是针对您的具体情况的想法。下面的代码添加了一些您需要实现的功能:func getContacts() -> Observable<[Contact]>func getSearchResults(_ contact: Contact) -> Observable<[Friend]>.

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 类型,而不是字符串。


推荐阅读