RxSwift+MVVM架构下,如何在SearchController中实现Hashtag搜索?
RxSwift + MVVM模式下Hashtag搜索落地方案
现有代码核心问题:ViewModel的Input层仅绑定了初始加载信号,完全未关联搜索栏的输入事件,搜索触发链路未打通,按以下步骤改造即可,原有业务逻辑无需改动。
1. ViewController层改造:打通搜索输入信号
把搜索栏输入、搜索按钮点击事件封装成Rx可订阅的信号,传给ViewModel层:
func setUI() { // 初始化搜索栏 self.searchBar = UISearchBar(frame: CGRect(x: 300, y: 0, width: 300, height: 20)) // 搜索输入信号:加防抖、去重,避免输入过程中频繁触发请求 let searchInput = searchBar.rx.text.orEmpty .debounce(.milliseconds(300), scheduler: MainScheduler.instance) .distinctUntilChanged() .asSignal(onErrorJustReturn: "") // 搜索按钮点击触发:取最新的搜索文本作为关键词 let searchTrigger = searchBtn.rx.tap .withLatestFrom(searchInput) .asSignal(onErrorJustReturn: "") // 原有动画逻辑完全保留 searchBtn.rx.tap.subscribe(onNext: { _ in self.navigationItem.searchController = self.searchController self.navigationItem.hidesSearchBarWhenScrolling = false UIView.animate(withDuration: 0.25) { self.searchBar.frame = CGRect(x: 0, y: 0, width: 300, height: 20) } }).disposed(by: rx.disposeBag) // 绑定ViewModel let viewModel = SearchViewModel() let input = SearchViewModel.Input( loadData: .just(()), // 原有初始加载逻辑保留 searchKeyword: searchTrigger // 传入新增的搜索关键词信号 ) let output = viewModel.transform(input) // 绑定搜索结果到列表/UI output.hashtagSearchResult .drive(tableView.rx.items(cellIdentifier: "HashtagCell")) { (_, item, cell) in // 配置cell显示Hashtag内容即可 } .disposed(by: rx.disposeBag) }
2. ViewModel层改造:新增Hashtag搜索逻辑
扩展Input/Output结构,新增搜索信号处理、接口调用逻辑:
final class SearchViewModel { private let disposeBag = DisposeBag() // 新增Hashtag结果模型,按业务需求定义字段即可 struct HashtagItem {} struct Input { let loadData: Signal<Void> let searchKeyword: Signal<String> // 新增:搜索触发的关键词信号 } struct Output { let result: Signal<String> let loadApplyList: PublishRelay<[friends]> let hashtagSearchResult: PublishRelay<[HashtagItem]> // 新增:返回搜索结果给VC } func transform(_ input: Input) -> Output { let api = SearchAPI() let result = PublishSubject<String>() let loadApplyList = PublishRelay<[friends]>() let hashtagResult = PublishRelay<[HashtagItem]>() // 原有初始好友列表加载逻辑完全保留 input.loadData.asObservable().subscribe(onNext: { [weak self] in guard let self = self else { return } api.getFriend().subscribe(onNext: { (response, statuscode) in switch statuscode { case .ok: if let response = response { loadApplyList.accept(response) } default: print("default") } }).disposed(by: self.disposeBag) }).disposed(by: disposeBag) // 新增:Hashtag搜索逻辑处理 input.searchKeyword .asObservable() .filter { !$0.isEmpty } // 空关键词直接过滤,不发请求 .map { keyword -> String in // 按产品规则做关键词预处理:自动补#、裁剪空格等 keyword.hasPrefix("#") ? keyword : "#\(keyword.trimmingCharacters(in: .whitespaces))" } .flatMapLatest { processedKeyword in // 调用Hashtag搜索接口,自动取消上一次未完成的请求 api.searchHashtag(keyword: processedKeyword) .catchAndReturn((nil, .serverError)) } .subscribe(onNext: { (response, statuscode) in switch statuscode { case .ok: if let items = response { hashtagResult.accept(items) } default: result.onNext("搜索失败,请稍后重试") } }) .disposed(by: disposeBag) return Output( result: result.asSignal(onErrorJustReturn: ""), loadApplyList: loadApplyList, hashtagSearchResult: hashtagResult ) } }
注:需要在
SearchAPI中新增searchHashtag方法,入参为预处理后的Hashtag关键词,返回对应的结果和状态码。
3. 可选优化点
- 结果分页:如果Hashtag搜索结果有分页,可在VC层新增列表滚动到底部的信号,传给ViewModel拼接分页参数
- 历史搜索:可把用户的搜索关键词通过本地存储,搜索框点击时弹出历史标签
- 输入联想:如果需要边输入边展示联想的Hashtag,可把
searchBtn.rx.tap触发改成searchInput直接触发,保留防抖逻辑即可 - 错误兜底:搜索失败、空结果的文案统一通过
result信号传给VC展示
内容的提问来源于stack exchange,提问作者earth
相关产品推荐
相关产品推荐

