Я хочу миграцию совместить и теперь пытаюсь понять, как это сделать. Учась я столкнулся с одной проблемой, что не понимаю как лучше поступить.
У меня есть динамические списки с AnyPublisher.
Опишу, что у меня есть, чтобы лучше понять, чего я хочу.
У меня есть методы для выполнения запросов, методы возвращают AnyPublisher
с помощью type
и APIError
.
/// return standarts
func fetchStandarts(by key: String) -> AnyPublisher<[A11yStandart], APIError>
/// return accessibilities
func fetchAccessibilities(by id: String) -> AnyPublisher<AccessibilitiesResult, APIError>
/// return images
func fetchImages(id: String) -> AnyPublisher<AccessibilityImagesResult, APIError>
у меня есть экран, на котором мне нужно вызвать fetchStandarts
, fetchAccessibilities
, fetchImages
вместе для отображения экрана.
Но для fetchStandarts
и fetchAccessibilities
мне нужно выполнить несколько запросов, например, у меня есть
let standartsIDs = ["1", "2"]
let accessibilityIDs = ["3", "4"]
в этом случае мне нужно вызвать fetchStandarts
2 раза и fetchAccessibilities
2 раза.
Что я сделал, сначала я сгенерировал все Publishers
let standardsPublishers = ["1", "2"]
.map({ key -> AnyPublisher<[A11yStandart], APIError> in
a11yStandartsRepository.fetchStandarts(by: key)
})
let answersPublishers = ["3", "4"]
.map { key -> AnyPublisher<AccessibilitiesResult, APIError> in
accessibilitiesRepository.fetchAccessibilities(by: key)
}
let imagesPublisher = a11ImagesRepository.fetchImages(id: "1")
теперь для меня это самая сложная часть, потому что я не знаю, как лучше «объединить» (под слиянием я не имею в виду оператор слияния) всех издателей.
Я попробовал сделать так:
Publishers.MergeMany(standardsPublishers)
.zip(Publishers.MergeMany(answersPublishers))
.zip(imagesPublisher)
.collect()
а вот так
Publishers.Zip(
Publishers.MergeMany(standardsPublishers),
Publishers.MergeMany(answersPublishers)
)
.zip(imagesPublisher)
но я жалуюсь, что делаю это неправильно. Можете ли вы дать мне совет, как мне нужно правильное «слияние» (под слиянием я не имею в виду оператор слияния в объединении) всех издателей в одного и получения данных
Думаю, я понимаю, о чем вы спрашиваете, но если нет, мы можем попробовать еще раз.
Вот детская площадка:
import UIKit
import Combine
enum APIError : Error {
}
typealias DownloadedThing = String
func makeRequest(downloading: String) -> Future<DownloadedThing, APIError> {
Future { continuation in
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + .seconds((1...5).randomElement()!)) {
print("Downloaded \(downloading)")
continuation(.success("Downloaded \(downloading)"))
}
}
}
var thingsToDownload = ["Cats", "Dogs", "Elephants", "Ducks"]
let subscription = thingsToDownload
.publisher
.flatMap(makeRequest)
.sink { completion in
switch completion {
case .finished:
print("All the requests are done")
case .failure(let apiError):
print("An API error caused a problem \(apiError)")
}
} receiveValue: { results in
debugPrint(results)
}
В детской площадке список вещей для скачивания находится в thingsToDownload
. Еще у меня есть функция makeRequest(downloading:)
, которая выдает запрос на загрузку одной вещи. В этом случае он возвращает Future
. Future
— это Publisher
, который выдаст одно значение или потерпит неудачу. Если хотите, вы можете использовать .eraseToAnyPublisher()
, чтобы стереть типы, но я оставил его как Future
, чтобы мой код сообщал о том, что запрос либо будет успешным, либо не удастся.
Внутри makeRequest
возвращаемый Future
ждет случайное количество времени, а затем завершается успешно. Он имитирует сетевой запрос, выполнение которого занимает несколько секунд.
Главное, о чем вы спрашивали, находится в трубопроводе внизу. Я получаю массив и запрашиваю его publisher
, чтобы каждое из значений передавалось как значение в следующий конвейер.
Важным оператором в конвейере является flatMap
. flatMap
передает каждое значение моему makeRequest(downloading:)
. При возврате каждого издателя flatMap
устанавливает прослушиватель, ожидающий завершения работы этого издателя, прежде чем передать результат издателя в конвейер.
Внизу находится звонок sink
. По мере завершения работы каждого из издателей, управляемых flatMap
, этот приемник получает результирующее значение и выводит его на консоль. Если бы мои фиктивные вызовы API могли завершиться неудачей, то сюда также были бы доставлены ошибки.
Запуск этой игровой площадки в моей системе однажды дает:
"Downloaded Ducks"
"Downloaded Cats"
"Downloaded Dogs"
"Downloaded Elephants"
All the requests are done
и второй раз дает:
"Downloaded Ducks"
"Downloaded Elephants"
"Downloaded Cats"
"Downloaded Dogs"
All the requests are done
Таким образом, каждый результат доставляется по мере его завершения, а сообщение приходит, когда все запросы выполнены.
Спасибо! Этот ответ помогает мне глубже понять объединение
Каково ожидаемое поведение? Из вопроса не очень понятно.