Приведи пример использования async/await для сетевого запроса в Swift.

«Приведи пример использования async/await для сетевого запроса в Swift.» — вопрос из категории Многопоточность, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

async/await — это модель асинхронного программирования в Swift, позволяющая писать асинхронный код в линейном, легко читаемом стиле, без цепочек completion handlers (callbacks).

Базовый пример сетевого запроса:

// 1. Объявление асинхронной функции
func fetchUserData(from url: URL) async throws -> User {
    // 2. `await` приостанавливает функцию, пока не будет получен ответ,
    // но не блокирует поток.
    let (data, response) = try await URLSession.shared.data(from: url)

    guard let httpResponse = response as? HTTPURLResponse,
          httpResponse.statusCode == 200 else {
        throw NetworkError.invalidResponse
    }
    // 3. Декодирование также может быть асинхронной операцией.
    let user = try JSONDecoder().decode(User.self, from: data)
    return user
}

// 4. Вызов асинхронной функции.
// Должен происходить внутри асинхронного контекста: `Task` или другой `async` функции.
Task {
    do {
        let url = URL(string: "https://api.example.com/user/1")!
        let user = try await fetchUserData(from: url)
        print("User received: (user.name)")
        // 5. Обновление UI должно выполняться на главном потоке.
        await MainActor.run {
            self.nameLabel.text = user.name
        }
    } catch {
        print("Failed to fetch user: (error)")
    }
}

Пример параллельного выполнения запросов (Concurrency):

func fetchUserAndPosts(userId: Int) async throws -> (User, [Post]) {
    async let user = fetchUserData(from: userURL) // Запускаем асинхронно
    async let posts = fetchPostsData(from: postsURL) // Запускаем асинхронно
    // Оба запроса выполняются параллельно.
    // `try await` ожидает завершения обоих.
    return try await (user, posts)
}

Ключевые концепции:

  • async — помечает функцию как асинхронную. Она может быть приостановлена.
  • await — точка возможной приостановки, где функция ожидает результат асинхронной операции.
  • Task — единица работы, которая выполняется асинхронно. Создает асинхронный контекст.
  • throws — асинхронные функции могут выбрасывать ошибки, как и обычные.
  • MainActor — глобальный актор, гарантирующий выполнение кода на главном потоке. Используется для обновления UI.