Concurrence accessible Swift 6.2
Modèles pour adopter le modèle de concurrence de Swift 6.2, où le code s'exécute par défaut sur un seul thread et où la concurrence est introduite explicitement. Élimine les erreurs classiques de course aux données sans sacrifier les performances.
Quand l'activer
- Migration de projets Swift 5.x ou 6.0/6.1 vers Swift 6.2
- Résolution d'erreurs de sécurité liées aux courses aux données lors de la compilation
- Conception d'architecture d'application basée sur MainActor
- Déchargement de travaux intensifs vers des threads d'arrière-plan
- Implémentation de conformances de protocole sur des types isolés par MainActor
- Activation des paramètres de build « Approachable Concurrency » dans Xcode 26
Problème fondamental : déchargement implicite en arrière-plan
Dans Swift 6.1 et antérieur, les fonctions async pouvaient être implicitement déchargées vers des threads d'arrière-plan, provoquant des erreurs de course aux données même dans du code apparemment sûr :
// Swift 6.1 : ERREUR
@MainActor
final class StickerModel {
let photoProcessor = PhotoProcessor()
func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? {
guard let data = try await item.loadTransferable(type: Data.self) else { return nil }
// Erreur : l'envoi de 'self.photoProcessor' risque de provoquer des courses aux données
return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier)
}
}
Swift 6.2 corrige cela : les fonctions async restent par défaut sur l'acteur appelant.
// Swift 6.2 : OK — async reste sur MainActor, pas de course aux données
@MainActor
final class StickerModel {
let photoProcessor = PhotoProcessor()
func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? {
guard let data = try await item.loadTransferable(type: Data.self) else { return nil }
return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier)
}
}
Modèle fondamental — Conformances isolées
Les types MainActor peuvent désormais adopter des protocoles non isolés en toute sécurité :
protocol Exportable {
func export()
}
// Swift 6.1 : ERREUR — traverse du code isolé par MainActor
// Swift 6.2 : OK avec la conformance isolée
extension StickerModel: @MainActor Exportable {
func export() {
photoProcessor.exportAsPNG()
}
}
Le compilateur garantit que la conformance n'est utilisée que sur l'acteur principal :
// OK — ImageExporter est également @MainActor
@MainActor
struct ImageExporter {
var items: [any Exportable]
mutating func add(_ item: StickerModel) {
items.append(item) // Sûr : même isolement d'acteur
}
}
// ERREUR — un contexte non isolé ne peut pas utiliser la conformance MainActor
nonisolated struct ImageExporter {
var items: [any Exportable]
mutating func add(_ item: StickerModel) {
items.append(item) // Erreur : la conformance isolée par MainActor ne peut pas être utilisée ici
}
}
Modèle fondamental — Variables globales et statiques
Protégez l'état global/statique avec MainActor :
// Swift 6.1 : ERREUR — le type non Sendable peut avoir un état mutable partagé
final class StickerLibrary {
static let shared: StickerLibrary = .init() // Erreur
}
// Correction : annoter avec @MainActor
@MainActor
final class StickerLibrary {
static let shared: StickerLibrary = .init() // OK
}
Mode d'inférence par défaut de MainActor
Swift 6.2 introduit un mode où MainActor est inféré par défaut — aucune annotation manuelle nécessaire :
// Avec l'inférence par défaut de MainActor activée :
final class StickerLibrary {
static let shared: StickerLibrary = .init() // Implicitement @MainActor
}
final class StickerModel {
let photoProcessor: PhotoProcessor
var selection: [PhotosPickerItem] // Implicitement @MainActor
}
extension StickerModel: Exportable { // Conformance implicitement @MainActor
func export() {
photoProcessor.exportAsPNG()
}
}
Ce mode est optionnel (opt-in) et recommandé pour les applications, scripts et autres cibles exécutables.
Modèle fondamental — @concurrent pour le travail en arrière-plan
Lorsque vous avez besoin de parallélisme réel, déchargez explicitement avec @concurrent :
Important : Cet exemple nécessite les paramètres de build Approachable Concurrency — SE-0466 (isolation par défaut de MainActor) et SE-0461 (NonisolatedNonsendingByDefault). Avec ceux-ci activés,
extractStickerreste sur l'acteur appelant, rendant l'accès à l'état mutable sûr. Sans ces paramètres, ce code présente une course aux données — le compilateur le signalera.
nonisolated final class PhotoProcessor {
private var cachedStickers: [String: Sticker] = [:]
func extractSticker(data: Data, with id: String) async -> Sticker {
if let sticker = cachedStickers[id] {
return sticker
}
let sticker = await Self.extractSubject(from: data)
cachedStickers[id] = sticker
return sticker
}
// Décharger le travail coûteux vers le pool de threads concurrents
@concurrent
static func extractSubject(from data: Data) async -> Sticker { /* ... */ }
}
// Les appelants doivent utiliser await
let processor = PhotoProcessor()
processedPhotos[item.id] = await processor.extractSticker(data: data, with: item.id)
Pour utiliser @concurrent :
- Marquez le type contenant comme
nonisolated - Ajoutez
@concurrentà la fonction - Ajoutez
asyncsi elle n'est pas déjà asynchrone - Ajoutez
awaitaux sites d'appel
Décisions de conception clés
| Décision | Justification |
|---|---|
| Monofilaire par défaut | Le code le plus naturel est exempt de courses aux données ; la concurrence est opt-in |
| Async reste sur l'acteur appelant | Élimine le déchargement implicite qui causait des erreurs de courses aux données |
| Conformances isolées | Les types MainActor peuvent adopter des protocoles sans contournements dangereux |
@concurrent explicite (opt-in) | L'exécution en arrière-plan est un choix délibéré de performance, pas accidentel |
| Inférence par défaut de MainActor | Réduit les annotations @MainActor répétitives pour les cibles d'application |
| Adoption optionnelle | Chemin de migration non cassant — activez les fonctionnalités de manière incrémentale |
Étapes de migration
- Activer dans Xcode : Compilateur Swift > section Concurrency dans les Paramètres de build
- Activer dans SPM : Utilisez l'API
SwiftSettingsdans le manifeste du paquet - Utiliser les outils de migration : Modifications de code automatiques via swift.org/migration
- Commencer par les valeurs par défaut de MainActor : Activez le mode d'inférence pour les cibles d'application
- Ajouter
@concurrentlà où nécessaire : Analysez d'abord, puis déchargez les chemins chauds - Tester en profondeur : Les problèmes de course aux données deviennent des erreurs de compilation
Bonnes pratiques
- Commencez sur MainActor — écrivez du code monofilaire d'abord, optimisez ensuite
- Utilisez
@concurrentuniquement pour le travail intensif en CPU — traitement d'images, compression, calculs complexes - Activez le mode d'inférence de MainActor pour les cibles d'application principalement monofilaires
- Analysez avant de décharger — utilisez Instruments pour trouver les véritables goulots d'étranglement
- Protégez les variables globales avec MainActor — l'état mutable global/statique nécessite un isolement par acteur
- Utilisez les conformances isolées au lieu de contournements
nonisolatedou de wrappers@Sendable - Migrez de manière incrémentale — activez les fonctionnalités une par une dans les paramètres de build
Anti-patrons à éviter
- Appliquer
@concurrentà chaque fonctionasync(la plupart n'ont pas besoin d'exécution en arrière-plan) - Utiliser
nonisolatedpour supprimer des erreurs du compilateur sans comprendre l'isolement - Conserver les motifs
DispatchQueuehérités alors que les acteurs offrent la même sécurité - Ignorer les vérifications
model.availabilitydans le code Foundation Models lié à la concurrence - Lutter contre le compilateur — s'il signale une course aux données, le code a un vrai problème de concurrence
- Supposer que tout code asynchrone s'exécute en arrière-plan (Swift 6.2 par défaut : reste sur l'acteur appelant)
Quand utiliser
- Tous les nouveaux projets Swift 6.2+ (Approachable Concurrency est le mode par défaut recommandé)
- Migration d'applications existantes depuis Swift 5.x ou 6.0/6.1
- Résolution d'erreurs de sécurité liées aux courses aux données lors de l'adoption de Xcode 26
- Construction d'architectures d'applications centrées sur MainActor (la plupart des applications d'interface utilisateur)
- Optimisation des performances — déchargement de calculs lourds spécifiques en arrière-plan