如何使用Combine遍历发布者的输出? [英] How can I loop over the output of a publisher with Combine?

查看:45
本文介绍了如何使用Combine遍历发布者的输出?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在重写我的Hacker News阅读器,以更广泛地使用Combine.我有两个函数都返回一个 AnyPublisher ,其中一个从服务器获取一堆HN故事的ID,另一个通过ID获取故事.我不确定如何遍历 fetchStoryIds 的结果,使用id运行 fetchStory 并以 Story 对象的数组结束结合起来.

I'm working on rewriting my Hacker News reader to use Combine more heavily. I'm have two functions which both return an AnyPublisher, one of them get's the ids of a bunch of HN stories from the server and the other one fetches a story by it's id. I'm not sure how I could loop over the results of fetchStoryIds, run fetchStory with the id and end up with an array of Story objects with Combine.

import Combine
import Foundation

struct HackerNewsService {
    private var session = URLSession(configuration: .default)
    static private var baseURL = "https://hacker-news.firebaseio.com/v0"

    private func fetchStoryIds(feed: FeedType) -> AnyPublisher<[Int], Error> {
       let url = URL(string: "\(HackerNewsService.baseURL)/\(feed.rawValue.lowercased())stories.json")!

        return session.dataTaskPublisher(for: url)
            .retry(1)
            .map { $0.data }
            .decode(type: [Int].self, decoder: JSONDecoder())
            .eraseToAnyPublisher()
    }

    private func fetchStory(id: Int) -> AnyPublisher<Story, Error> {
        let url = URL(string: "\(HackerNewsService.baseURL)/item/\(id).json")!

        return session.dataTaskPublisher(for: url)
            .map { $0.data }
            .decode(type: Story.self, decoder: JSONDecoder())
            .eraseToAnyPublisher()
    }
}

在开始重写之前,我使用此代码遍历了id并获取了故事.

Before I started the rewrite, I used this code to loop over the ids and get the stories.

func fetchStories(feed: FeedType, completionHandler: @escaping ([Story]?, Error?) -> Void) {
        fetchStoryIds(feed: feed) { (ids, error) in
            guard error == nil else {
                completionHandler(nil, error)
                return
            }

            guard let ids = ids else {
                completionHandler(nil, error)
                return
            }

            let dispatchGroup = DispatchGroup()

            var stories = [Story]()

            for id in ids {
                dispatchGroup.enter()

                self.fetchStory(id: id) { (story, error) in
                    guard error == nil else {
                        dispatchGroup.leave()
                        return
                    }

                    guard let story = story else {
                        dispatchGroup.leave()
                        return
                    }

                    stories.append(story)

                    dispatchGroup.leave()
                }
            }

            dispatchGroup.notify(queue: .main) {
                completionHandler(stories, nil)
            }
        }
    }
}

推荐答案

嗯.看起来好像没有 Publishers.ZipMany 可以接受发布者的集合,所以我合并了故事,然后收集它们.理想情况下,这将按正确的顺序收集它们,但我尚未对其进行测试,并且在Combine上,文档仍然有些稀疏.

Hmm.. It doesn't look like there is a Publishers.ZipMany that accepts a collection of publishers, so instead I merged the stories and collected them instead. Ideally this would collect them in the correct order but I haven't tested that and the documentation is still somewhat sparse across Combine.

func fetchStories(feed: FeedType) -> AnyPublisher<[Story], Error> {
    fetchStoryIds(feed: feed)
        .flatMap { ids -> AnyPublisher<[Story], Error> in
            let stories = ids.map { self.fetchStory(id: $0) }
            return Publishers.MergeMany(stories)
                .collect()
                .eraseToAnyPublisher()
        }
        .eraseToAnyPublisher()
}

如果您愿意接受外部代码,则这是ZipMany的主要实现,它将保留该顺序: https://gist.github.com/mwahlig/725fe5e78e385093ba53e6f89028a41c

If you are open to external code this is a gist implementation of ZipMany that will preserve the order: https://gist.github.com/mwahlig/725fe5e78e385093ba53e6f89028a41c

尽管我认为这样的事情会在框架中存在.

Although I would think that such a thing would exist in the framework.

这篇关于如何使用Combine遍历发布者的输出?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆