Swift Combine - 访问单独的发布者列表

Swift Combine - Accessing separate lists of publishers

我有两个 URL 列表,return 一些图像链接。 这些列表被传递到未来

static func loadRecentEpisodeImagesFuture(request: [URL]) -> AnyPublisher<[RecentEpisodeImages], Never> {
        return Future { promise in
            print(request)
             networkAPI.recentEpisodeImages(url: request)
                .sink(receiveCompletion: { _ in },
                        receiveValue: { recentEpisodeImages in
                            promise(.success(recentEpisodeImages))
                        })
                .store(in: &recentImagesSubscription)
        }
        .eraseToAnyPublisher()
    }

调用:

    /// Get a list of image sizes associated with a featured episode .
    func featuredEpisodeImages(featuredUrl: [URL]) -> AnyPublisher<[FeaturedEpisodeImages], Error> {
        let featuredEpisodesImages = featuredUrl.map { (featuredUrl) -> AnyPublisher<FeaturedEpisodeImages, Error> in
        return URLSession.shared
            .dataTaskPublisher(for: featuredUrl)
            .map(\.data)
            .decode(type: FeaturedEpisodeImages.self, decoder: decoder)
            .receive(on: networkApiQueue)
            .catch { _ in Empty<FeaturedEpisodeImages, Error>() }
            .print("###Featured###")
            .eraseToAnyPublisher()
        }
        return Publishers.MergeMany(featuredEpisodesImages).collect().eraseToAnyPublisher()
    }
    /// Get a list of image sizes associated with a recent episode .
    func recentEpisodeImages(recentUrl: [URL]) -> AnyPublisher<[RecentEpisodeImages], Error> {
        let recentEpisodesImages = recentUrl.map { (recentUrl) -> AnyPublisher<RecentEpisodeImages, Error> in
        return URLSession.shared
            .dataTaskPublisher(for: recentUrl)
            .map(\.data)
            .decode(type: RecentEpisodeImages.self, decoder: decoder)
            .receive(on: networkApiQueue)
            .catch { _ in Empty<RecentEpisodeImages, Error>() }
            .print("###Recent###")
            .eraseToAnyPublisher()
        }
        return Publishers.MergeMany(recentEpisodesImages).collect().eraseToAnyPublisher()
    }

并附加到应用状态:

/// Takes an action and returns a future mapped to another action.
     static func recentEpisodeImages(action: RequestRecentEpisodeImages) -> AnyPublisher<Action, Never> {
        return loadRecentEpisodeImagesFuture(request: action.request)
             .receive(on: networkApiQueue)
             .map({ images in ResponseRecentEpisodeImages(response: images) })
             .replaceError(with: RequestFailed())
             .eraseToAnyPublisher()
     }

好像是:

return Publishers.MergeMany(recentEpisodes).collect().eraseToAnyPublisher()

没有给我可靠的下游值,因为最后完成的响应会覆盖较早的响应。

我能够记录这两个请求系列的响应。两者都在处理正确的数组和 returning 正确的 json.

我想要这样的东西:

return recentEpisodeImages

但目前这给了我错误

Cannot convert return expression of type '[AnyPublisher<RecentEpisodeImages, Error>]' to return type 'AnyPublisher<[RecentEpisodeImages], Error>'

如何收集内部发布者的值并将它们return作为

AnyPublisher<[RecentEpisodeImages], Error>

假设问题是如何将 URL 的数组转换为下载和处理来自 URL 的数据时得到的数组,答案是:转将数组转换为序列发布者,通过 flatMap 处理每个 URL,并 collect 结果。

例如,这里是如何将表示图像的 URL 数组转换为实际图像数组(与您尝试做的不完全相同,但可能非常接近):

func publisherOfArrayOfImages(urls:[URL]) -> AnyPublisher<[UIImage],Error> {
    urls.publisher
        .flatMap { (url:URL) -> AnyPublisher<UIImage,Error> in
            return URLSession.shared.dataTaskPublisher(for: url)
                .compactMap { UIImage(data: [=10=].0) }
                .mapError { [=10=] as Error }
                .eraseToAnyPublisher()
        }.collect().eraseToAnyPublisher()
}

测试方法如下:

let urls = [
    URL(string:"http://www.apeth.com/pep/moe.jpg")!,
    URL(string:"http://www.apeth.com/pep/manny.jpg")!,
    URL(string:"http://www.apeth.com/pep/jack.jpg")!,
]
let pub = publisherOfArrayOfImages(urls:urls)
pub.sink { print([=11=]) }
    receiveValue: { print([=11=]) }
    .store(in: &storage)

您会看到从管道底部弹出的是一个包含三个图像的数组,对应于我们开始时包含三个 URL 的数组。

(请注意,结果数组的 顺序 是随机的。我们异步获取图像,因此结果以他们喜欢的任何顺序返回到我们的机器。有很多方法可以解决这个问题,但这不是你问的问题。)