Как в iOS приложении одновременно обработать и отобразить данные из трех независимых сетевых запросов?

«Как в iOS приложении одновременно обработать и отобразить данные из трех независимых сетевых запросов?» — вопрос из категории Архитектура, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Для синхронизации нескольких асинхронных операций в iOS используются следующие подходы:

1. DispatchGroup (GCD) Идеален для объединения задач, выполняющихся на разных очередях.

let group = DispatchGroup()
var results: [ResultType] = []
var errors: [Error] = []

// Запрос 1
group.enter()
networkService.fetchFirstData { result in
    defer { group.leave() }
    switch result {
        case .success(let data): results.append(data)
        case .failure(let error): errors.append(error)
    }
}

// Запрос 2 и 3 аналогично...

// Обработка завершения всех задач
group.notify(queue: .main) {
    if errors.isEmpty {
        updateUI(with: results)
    } else {
        // Реализовать стратегию: показать частичные данные или общую ошибку
        handlePartialResults(results, errors: errors)
    }
}

2. TaskGroup (Swift Concurrency) Современный способ с использованием async/await (Swift 5.5+).

Task {
    async let firstData = networkService.fetchFirstData()
    async let secondData = networkService.fetchSecondData()
    async let thirdData = networkService.fetchThirdData()

    do {
        let allData = try await [firstData, secondData, thirdData]
        await MainActor.run { updateUI(with: allData) }
    } catch {
        // Обработка ошибки (первой или агрегированной)
    }
}

3. Combine Для реактивного подхода с использованием фреймворка Combine.

Publishers.Zip3(
    networkService.fetchFirstDataPublisher(),
    networkService.fetchSecondDataPublisher(),
    networkService.fetchThirdDataPublisher()
)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in
    // Обработка завершения/ошибки
}, receiveValue: { data1, data2, data3 in
    updateUI(with: [data1, data2, data3])
})
.store(in: &cancellables)

Ключевые моменты:

  • Обработка ошибок: Решите, нужно ли ждать успеха всех запросов или можно отобразить частичные данные.
  • Прогресс: Для отображения общего прогресса можно использовать DispatchGroup с счетчиком или Progress.
  • Производительность: Запросы, не зависящие друг от друга, следует запускать параллельно, а не последовательно.