Swift-Combine, соединяйте все AnyPublisher в один и получайте данные

Я хочу миграцию совместить и теперь пытаюсь понять, как это сделать. Учась я столкнулся с одной проблемой, что не понимаю как лучше поступить.

У меня есть динамические списки с 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)

но я жалуюсь, что делаю это неправильно. Можете ли вы дать мне совет, как мне нужно правильное «слияние» (под слиянием я не имею в виду оператор слияния в объединении) всех издателей в одного и получения данных

Каково ожидаемое поведение? Из вопроса не очень понятно.

Cristik 11.04.2024 12:50
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
252
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Думаю, я понимаю, о чем вы спрашиваете, но если нет, мы можем попробовать еще раз.

Вот детская площадка:

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

Таким образом, каждый результат доставляется по мере его завершения, а сообщение приходит, когда все запросы выполнены.

Спасибо! Этот ответ помогает мне глубже понять объединение

user24060880 13.04.2024 11:40

Другие вопросы по теме