ios - 在 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)
我必须更改为其他值并将其更改回来。有没有更干净的解决方案?
解决方案
通常,当您想要组合两个或多个 observables 发出的值时,您需要使用combineLatest
、zip
、withLatestFrom
或merge
(可能与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 类型,而不是字符串。