Programmation asynchrone en Swift : guide complet pratique
La programmation asynchrone est l'un des concepts clés du développement moderne sous iOS et macOS. En Swift, à partir de la version 5.5, un système puissant de Structured Concurrency est apparu, rendant le travail avec l'asynchronisme simple et sûr. Dans cet article, nous allons détailler les outils principaux : async/await, Task, Actor et Sendable, et montrer leur application sur des exemples concrets.
1. Les bases d'async/await : comment arrêter d'écrire des closures et commencer à vivre
Avant l'arrivée d'async/await, le code asynchrone en Swift s'écrivait avec des closures et des délégués, ce qui menait souvent à « l'enfer des callbacks ». L'approche moderne permet d'écrire du code asynchrone de manière aussi linéaire que du code synchrone.
Déclarer une fonction asynchrone se fait avec le mot-clé async. L'appel d'une telle fonction nécessite un contexte supportant l'asynchronisme et le mot-clé await.
// Exemple de fonction asynchrone de chargement de donnéesfunc fetchUserData(userID: String) async throws -> User { let url = URL(string: "https://api.example.com/users/\\(userID)")! let (data, response) = try await URLSession.shared.data(from: url) guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { throw URLError(.badServerResponse) } let user = try JSONDecoder().decode(User.self, from: data) return user}
// UtilisationTask { do { let user = try await fetchUserData(userID: "123") print("User name: \\(user.name)") } catch { print("Error: \\(error)") }}Caractéristiques clés :
async— marqueur de fonction asynchroneawait— point de suspension de l'exécution- Une fonction peut être à la fois
asyncetthrowssimultanément - Le thread n'est pas bloqué — pendant l'attente, il peut effectuer d'autres tâches
2. Task et TaskGroup : gestion des tâches parallèles
Task est l'unité de base du travail asynchrone. Elle crée une nouvelle tâche asynchrone qui s'exécute dans le pool global de threads. TaskGroup permet de lancer plusieurs tâches en parallèle et de collecter leurs résultats.
Création et annulation de Task
// Création d'une tâchelet task = Task { let result = await performLongOperation() return result}
// Obtention du résultatlet value = await task.value
// Annulation de la tâchetask.cancel()
// Vérification de l'annulation à l'intérieur de la tâchefunc performLongOperation() async -> String { // Vérifions si la tâche n'est pas annulée guard !Task.isCancelled else { return "Cancelled" } // Émulons un travail long try? await Task.sleep(nanoseconds: 2_000_000_000) return "Completed"}Traitement parallèle avec TaskGroup
// Chargement de plusieurs images en parallèlefunc loadImages(urls: [URL]) async throws -> [UIImage] { return try await withThrowingTaskGroup(of: UIImage.self) { group in var images: [UIImage] = [] images.reserveCapacity(urls.count) for url in urls { group.addTask { let (data, _) = try await URLSession.shared.data(from: url) guard let image = UIImage(data: data) else { throw URLError(.cannotDecodeContentData) } return image } } // Collecte des résultats au fur et à mesure de leur disponibilité for try await image in group { images.append(image) } return images }}Important : TaskGroup gère automatiquement le cycle de vie des tâches filles