如何发出初始数据,然后使用 RxSwift 与新数据结合?

问题描述 投票:0回答:1

我使用 3 个 api 来获取我的 tableview 的数据。首先,

feedRepository.findOtherFeed
返回包含数据数组和Bool的元组的Observable。对于数据数组中的每个元素,我调用
userRepository.findProfileImg
来获取每个数据的个人资料图像。每个数据都有一个名为
userFeed.imageIdList
的属性,对于每个 id,我调用
feedRepository.findFeedImage
来获取数据的图像。

这些数据将显示在tableView中。我想尽快向用户展示,这意味着我迫不及待地等待所有数据的 imageIdList 调用

feedRepository.findFeedImage
。每当
userRepository.findProfileImg
完成时,我想首先向用户展示我所拥有的内容,然后当
feedRepository.findFeedImage
全部完成时,我想更新数据。

我不太熟练使用 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() }
    
                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)
                    }
                
            
            }
    }

我尝试过 zip 和combineLatest,但我认为我没有以正确的方式使用它..

swift rx-swift
1个回答
0
投票

这是一个非常复杂的问题,特别是因为分页。您的示例中没有足够的信息来处理完整的询问。

这是我可能如何使用我的CLE 系统做这样的事情。

基本思路如下:

  • 每当表视图到达底部时,尝试获取 UserFeeds 页面。从第一页的初始请求开始。
  • 每当出现 UserFeeds 页面时,请求所有个人资料图片。
  • 当出现 UserFeeds 页面时,请求所有 feedImages。
  • 每当有个人资料图片出现时,将其存储在状态中。
  • 每当 feedImage 进来时,将其存储在状态中。

接下来的想法是遵循

example(api:tableView:)
函数的输出。每当页面或图像进入时,状态就会更新。它的
items
数组将包含迄今为止下载的所有 UserFeed 以及
CellDisplayable
对象内每个 userFeed 对象的所有图像。

enum Input {
    case receivePage(Int, [UserFeed])
    case addProfileImage(UserFeed, UIImage)
    case addFeedImage(String, UIImage)
    case getPage
}

struct CellDisplayable {
    let userFeed: UserFeed
    let profileImage: UIImage?
    let feedImages: [UIImage]
}

struct State {
    var pages: [Int: [UserFeed]] = [:]
    var profileImages: [UserFeed: UIImage] = [:]
    var feedImages: [String: UIImage] = [:]
    var items: [CellDisplayable] {
        pages.sorted { $0.key < $1.key }
            .flatMap { userFeeds in
                userFeeds.value.map {
                    CellDisplayable(
                        userFeed: $0,
                        profileImage: profileImages[$0],
                        feedImages: $0.imageIdList.compactMap { feedImages[$0] }
                    )
                }
            }
    }
}

func example(api: API, tableView: UITableView) -> Observable<State> {
    cycle(
        input: tableView.rx.reachedBottom().startWith(()).map(to: Input.getPage),
        initialState: State(),
        reduce: { state, input in
            switch input {
            case let .receivePage(page, userFeeds):
                state.pages = [page: userFeeds]
            case let .addProfileImage(userFeed, image):
                state.profileImages[userFeed] = image
            case let .addFeedImage(imageId, image):
                state.feedImages[imageId] = image
            case .getPage:
                break
            }
        },
        reactions: [
            getProfileImages(api: api),
            getFeedImages(api: api),
            getPage(api: api)
        ]
    )
}

func getProfileImages(api: API) -> (Observable<(State, Input)>) -> Observable<Input> {
    { reaction in
        reaction
            .flatMap { userFeeds(from: $1) }
            .flatMap { userFeed in
                api.response(.findProfileImage(feed: userFeed))
                    .map { (userFeed, $0) }
            }
            .map { Input.addProfileImage($0.0, $0.1) }
    }
}

func getFeedImages(api: API) -> (Observable<(State, Input)>) -> Observable<Input> {
    { reaction in
        reaction
            .flatMap { userFeeds(from: $1) }
            .flatMap { Observable.from($0.imageIdList) }
            .flatMap { imageId in
                api.response(.findFeedImage(imageId: imageId))
                    .map { (imageId, $0) }
            }
            .map { Input.addFeedImage($0.0, $0.1) }
    }
}

func getPage(api: API) -> (Observable<(State, Input)>) -> Observable<Input> {
    { reaction in
        reaction
            .compactMap { neededPage(state: $0, input: $1) }
            .flatMap { page in
                api.response(.findOtherFeed(nickname: "lemon", date: Date(), page: page, size: 7))
                    .map { (page, $0.0) }
            }
            .map { Input.receivePage($0.0, $0.1) }
    }
}

func userFeeds(from input: Input) -> Observable<UserFeed> {
    guard case let .receivePage(_, userFeeds) = input else { return .empty() }
    return .from(userFeeds)
}

func neededPage(state: State, input: Input) -> Int? {
    guard case .getPage = input else { return nil }
    return (state.pages.keys.max() ?? 0) + 1
}
© www.soinside.com 2019 - 2024. All rights reserved.