如何用RxSwift先输出带头像的Feed数据,再合并图片数据更新?
问题描述
我需要通过三个API为TableView获取数据,流程如下:
- 首先调用
feedRepository.findOtherFeed,返回包含数据数组和Bool值的Tuple类型Observable; - 接着为数据数组中的每个元素调用
userRepository.findProfileImg获取头像; - 每个数据的
userFeed.imageIdList属性中的每个ID,需调用feedRepository.findFeedImage获取对应图片。
展示需求:无需等待所有Feed图片加载完成,只要头像加载完成就先显示内容,待所有Feed图片加载完成后再更新数据。
我对RxSwift不太熟练,尝试过zip和combineLatest但用法有误,当前代码如下:
private func fetchAndProcessFeedsFinal(setSortOption: SortOption, page: Int?) -> Observable<Mutation> { if page != nil { resetPagination() } else if isLastPage { return .empty() } else { pages += 1 } let nickname = "lemon" return feedRepository.findOtherFeed(request: FindOtherFeedRequest(nickname: nickname, date: requestDate, page: pages, size: 7)) .flatMap { [weak self] (userFeeds, isLast) -> Observable<Mutation> in guard let self = self else { return .empty() } isLastPage = isLast if userFeeds.isEmpty { return Observable.just(.updateDataSource([])) } // MARK: inside processFeedWithProfileImage function it calls findProfileImg let observables: [Observable<UserFeedSection.Item>] = userFeeds.map {[weak self] feed in guard let self = self else { return Observable.just(.feed(FeedReactor(userFeed: feed, feedRepository: FeedRepository(), userRepository: UserRepository()))) } return processFeedWithProfileImage(feed) } let feedWithImage = userFeeds.flatMap { userFeed in userFeed.imageIdList.map { [weak self] imageId in return self?.feedRepository.findFeedImage(request: FindFeedImageRequest(imageId: imageId)) } } return Observable.zip(observables) .map { items in return Mutation.updateDataSource(items) } } }
寻求正确的实现方案。
解决方案
核心思路是拆分两个阶段的数据流:先完成头像加载并发送初始数据更新,再完成所有Feed图片加载后发送最终数据更新,用RxSwift的流组合操作符实现分步推送。
具体实现代码
private func fetchAndProcessFeedsFinal(setSortOption: SortOption, page: Int?) -> Observable<Mutation> { if page != nil { resetPagination() } else if isLastPage { return .empty() } else { pages += 1 } let nickname = "lemon" return feedRepository.findOtherFeed(request: FindOtherFeedRequest(nickname: nickname, date: requestDate, page: pages, size: 7)) .flatMap { [weak self] (userFeeds, isLast) -> Observable<Mutation> in guard let self = self else { return .empty() } self.isLastPage = isLast if userFeeds.isEmpty { return .just(.updateDataSource([])) } // 1. 第一阶段:加载所有头像,生成初始Item(无Feed图片) let initialItemsObservable = Observable.zip( userFeeds.map { self.processFeedWithProfileImage($0) } ) .map { items -> Mutation in .updateDataSource(items) } // 2. 第二阶段:为每个Feed加载所有图片,更新对应Item let updatedItemsObservable = Observable.zip( userFeeds.enumerated().map { index, feed in // 先获取已加载头像的初始Item self.processFeedWithProfileImage(feed) .flatMap { initialItem -> Observable<UserFeedSection.Item> in // 加载当前Feed的所有图片 let imageObservables = feed.imageIdList.map { imageId in self.feedRepository.findFeedImage(request: FindFeedImageRequest(imageId: imageId)) } return Observable.zip(imageObservables) .map { images -> UserFeedSection.Item in // 根据初始Item和加载的图片,生成更新后的Item switch initialItem { case .feed(let reactor): // 假设FeedReactor有更新图片的方法或属性 let updatedReactor = FeedReactor( userFeed: reactor.userFeed, feedRepository: reactor.feedRepository, userRepository: reactor.userRepository ) updatedReactor.feedImages = images // 替换为实际更新逻辑 return .feed(updatedReactor) default: return initialItem } } } } ) .map { updatedItems -> Mutation in .updateDataSource(updatedItems) } // 3. 组合两个阶段:先推送初始数据,再推送更新后的数据 return initialItemsObservable.concat(updatedItemsObservable) } }
关键逻辑说明
- 第一阶段:用
zip等待所有头像加载完成,生成带头像的初始Item,立即发送updateDataSource事件,让TableView先显示带头像的内容。 - 第二阶段:对每个Feed,先拿到已加载头像的初始Item,再加载该Feed的所有图片,完成后更新Item的图片信息;最后用
zip等待所有Feed的图片都加载完成,发送最终的updateDataSource事件。 - 流组合:使用
concat保证初始数据更新先触发,之后再触发图片加载完成后的更新,符合"先显示、后更新"的需求。
注意事项
- 确保
processFeedWithProfileImage返回的Observable<UserFeedSection.Item>已经正确注入了头像数据。 - 根据你的
FeedReactor实际结构调整图片更新的逻辑(示例中用feedImages属性替换,需改成你项目中的实际实现)。 - 若需要支持图片加载过程中逐张更新(而非等待所有图片加载完再更新),可以把第二阶段的
zip换成merge,并针对单个Item发送局部更新事件(比如新增Mutation.updateItem(at:index, with:item)类型)。
内容的提问来源于stack exchange,提问作者Sue
相关产品推荐
相关产品推荐

