Как я могу зациклить на выходе издателя с Combine? - PullRequest
0 голосов
/ 09 июля 2019

Я работаю над переписыванием моего читателя Hacker News, чтобы использовать Combine более интенсивно. У меня есть две функции, которые обе возвращают AnyPublisher, одна из них получает идентификаторы нескольких историй HN с сервера, а другая получает историю по ее идентификатору. Я не уверен, как я мог бы зациклить результаты fetchStoryIds, запустить fetchStory с идентификатором и в итоге получить массив Story объектов с 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()
    }
}

Прежде чем я начал переписывать, я использовал этот код, чтобы перебрать идентификаторы и получить истории.

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

1 Ответ

2 голосов
/ 09 июля 2019

Хм .. Не похоже, что есть Publishers.ZipMany, который принимает коллекцию издателей, поэтому вместо этого я объединил истории и собрал их.В идеале это будет собирать их в правильном порядке, но я не проверял это, и документация по-прежнему немного разбросана по всему Combine.сохранит порядок: https://gist.github.com/mwahlig/725fe5e78e385093ba53e6f89028a41c

Хотя я бы подумал, что такая вещь будет существовать в рамках.

...