【问题标题】:Swift Combine - Accessing separate lists of publishersSwift Combine - 访问单独的发布者列表
【发布时间】:2021-03-17 18:31:53
【问题描述】:

我有两个 URL 列表,它们返回一些图像链接。 列表被传递到未来,如

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()

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

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

我想要类似的东西:

return recentEpisodeImages

但目前这给了我错误

无法将“[AnyPublisher]”类型的返回表达式转换为“AnyPublisher”类型的返回表达式

如何收集内部发布者的值并将其返回为

AnyPublisher<[RecentEpisodeImages], Error>

【问题讨论】:

  • 为什么要配置双向合并?这是没有意义的。还有这里的目标是什么?这些是相同的网址吗?不同的 URL 集?响应顺序被打乱了怎么办?您是否不在乎失去与每个图像来自哪个 URL 的连接?你会不加选择地混合不同类型的图像吗?
  • 好吧,你还没有解释你想要做什么。这个问题毫无意义:“我怎样才能将这些流的结果分开?”如果你想让它们分开,你为什么要合并它们?深呼吸,然后解释手头的整个问题:输入和期望的输出。
  • 是的,合并肯定是问题所在,函数采用两个不同的 URL 数组并返回两个不同的 URL 数组。它们返回的顺序无关紧要。我需要将 Publishers.MergeMany() 替换为抓取 url.map 的结果而不与其范围之外的流合并的东西。
  • 但是你为什么不能回答我的问题呢?目标是什么?图像流?还是由一组图像组成的流?还是什么?
  • 我需要输入 [URL] 中的 AnyPublisher 类型。流中的数组是一个包含图像链接的 json 数组。我需要能够将此流传递给其他一些流,并且这些流需要停止相互覆盖。

标签: swift asynchronous combine urlsession


【解决方案1】:

您仍然没有回答我关于您要达到的目标的问题。如果问题是如何将 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: $0.0) }
                .mapError { $0 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($0) }
    receiveValue: { print($0) }
    .store(in: &storage)

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

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

【讨论】:

  • 好吧,虽然我的目标是明确地表示类型级别。就像大图一样,这是我有一个节目列表,它们有一些指向不同大小图像的链接作为道具。我正在使用 show props 链接来获取 json 接口以获取不同大小的图像。当需要剧集图像的页面加载时,我正在调度一个操作,其中包含要获取的 URL 列表作为有效负载。该操作将两个数组传递给两个效果,这两个效果调用调用两个期货,后者调用调用URL.Session Streams的api方法。
猜你喜欢
  • 2019-11-08
  • 1970-01-01
  • 2021-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-10
  • 1970-01-01
  • 2021-02-24
相关资源
最近更新 更多