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

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

Ответ

Для комфортной работы с асинхронными задачами в iOS следует выбирать инструменты в зависимости от сложности и версии iOS.

1. Swift Concurrency (async/await) (iOS 13+, предпочтительно)

Современный, безопасный и читаемый подход.

// Объявление асинхронной функции
func fetchUserData() async throws -> User {
    let (data, _) = try await URLSession.shared.data(from: userURL)
    return try JSONDecoder().decode(User.self, from: data)
}

// Вызов в Task
Task {
    do {
        let user = try await fetchUserData() // Приостановка без блокировки потока
        // Автоматический возврат на MainActor для обновления UI
        await MainActor.run {
            self.usernameLabel.text = user.name
        }
    } catch {
        // Обработка ошибок
    }
}
// Использование `async let` для параллельных задач
func loadDashboard() async throws -> Dashboard {
    async let user = fetchUserData()
    async let posts = fetchPosts()
    return try await Dashboard(user: user, posts: posts)
}

2. Grand Central Dispatch (GCD) (DispatchQueue)

Универсальный, но более низкоуровневый подход для поддержки старых iOS или специфичных сценариев.

// Приоритет (QoS) и избегание блокировки main потока
DispatchQueue.global(qos: .userInitiated).async {
    let image = self.applyHeavyFilter(to: rawImage)

    DispatchQueue.main.async {
        // Все UI-обновления — строго на главном потоке
        self.imageView.image = image
    }
}

3. OperationQueue

Для сложных зависимостей между задачами (очередность, отмена).

let downloadQueue = OperationQueue()
downloadQueue.maxConcurrentOperationCount = 2 // Ограничение параллелизма

let downloadOp = BlockOperation { /* Загрузка данных */ }
let parseOp = BlockOperation { /* Парсинг данных */ }
let updateUIOp = BlockOperation { /* Обновление UI на главном потоке */ }

parseOp.addDependency(downloadOp) // Парсинг только после загрузки
updateUIOp.addDependency(parseOp) // UI только после парсинга

OperationQueue.main.addOperation(updateUIOp) // UI-операция на главной очереди
downloadQueue.addOperations([downloadOp, parseOp], waitUntilFinished: false)

Рекомендации по выбору:

  • Новый код для iOS 13+: Используйте Swift Concurrency. Он устраняет «ад колбэков», обеспечивает структурированную обработку ошибок и автоматическое управление потоками.
  • Поддержка старых iOS или работа с C-библиотеками: Используйте GCD.
  • Сложные цепочки зависимых задач с возможностью отмены и паузы: Используйте OperationQueue.
  • Главное правило: Все операции, изменяющие UI, должны выполняться на главном потоке (MainActor.run, DispatchQueue.main.async).