TL;DR
- Swift 6.2 rend la strict concurrency utilisable au quotidien. L'inference @MainActor reduit drastiquement le nombre d'annotations manuelles.
- Le default actor isolation permet de configurer un module entier en @MainActor. Pour les apps SwiftUI, ca change tout.
- Scene bridging resout le probleme de l'interop UIKit/AppKit dans SwiftUI. Plus de bricolages UIViewRepresentable.
- L'Observation framework gagne en maturite avec des patterns simplifies et de meilleures performances.
- La strategie de migration recommandee : activer les checks un par un avec les UpcomingFeature flags.
Swift 6 a ete douloureux. Swift 6.2 repare ca.
Soyons honnetes : Swift 6 a ete un choc. La strict concurrency a casse des milliers de projets. Des warnings partout, des erreurs de compilation obscures, et une documentation qui n'expliquait pas clairement comment resoudre les problemes. Beaucoup de devs sont restes sur Swift 5 en attendant que ca se calme.
Swift 6.2 est la version ou ca se calme. Apple a ecoute les retours de la communaute et a fait des concessions pragmatiques sans sacrifier la securite du modele de concurrence. Voici ce qui change concretement.
La strict concurrency, version humaine
Le probleme de Swift 6 n'etait pas le concept -- isoler les acteurs et prevenir les data races au compile time, c'est une excellente idee. Le probleme etait l'implementation : trop de friction, trop d'annotations, et des messages d'erreur incomprehensibles.
@MainActor inference amelioree
En Swift 6, le compilateur exigeait des annotations @MainActor explicites sur pratiquement tout ce qui touchait l'UI. Un ViewModel, ses methodes, ses proprietes, ses closures -- tout devait etre annote.
Swift 6.2 introduit une inference beaucoup plus intelligente. Si une classe conforme a ObservableObject a une propriete @Published, le compilateur deduit automatiquement que la classe et ses methodes doivent tourner sur le MainActor.
// Swift 6.0 : annotations partout
@MainActor
class ProjectViewModel: ObservableObject {
@Published var projects: [Project] = []
@Published var isLoading = false
@MainActor
func loadProjects() async {
isLoading = true
let result = await api.fetchProjects()
projects = result // @MainActor nécessaire ici aussi
isLoading = false
}
}
// Swift 6.2 : inférence automatique
class ProjectViewModel: ObservableObject {
@Published var projects: [Project] = []
@Published var isLoading = false
func loadProjects() async {
isLoading = true
let result = await api.fetchProjects()
projects = result // MainActor inféré automatiquement
isLoading = false
}
// Opt-out explicite pour le travail en arrière-plan
nonisolated func parseResponse(_ data: Data) throws -> [Project] {
try JSONDecoder().decode([Project].self, from: data)
}
}
Le changement de philosophie est important : au lieu de tout annoter et d'opter out quand tu veux du background, tu laisses le compilateur inferer et tu optes out explicitement avec nonisolated quand tu veux sortir du MainActor. C'est plus naturel et ca reflete mieux la realite d'une app SwiftUI ou 90% du code touche l'UI.
Default actor isolation par module
C'est le changement le plus pragmatique. Tu peux configurer un module entier pour qu'il soit @MainActor par defaut.
// Package.swift
.target(
name: "MyApp",
swiftSettings: [
.defaultIsolation(MainActor.self)
]
)
Avec ce reglage, chaque type, fonction et propriete du module est implicitement @MainActor. Tu n'annotes que ce qui doit explicitement tourner en arriere-plan.
Pour une app SwiftUI classique, ca elimine litteralement des centaines d'annotations. Et ca rend le code beaucoup plus lisible.
Le modele mental simplifie
Avec Swift 6.2, le modele mental de la concurrence devient :
- Par defaut, tout tourne sur le MainActor (si tu actives
defaultIsolation). - Pour du travail en arriere-plan, tu marques explicitement avec
nonisolatedou tu utilises un acteur dedie. - Les frontieres entre acteurs sont les seuls endroits ou tu as besoin d'
await.
C'est beaucoup plus simple que le modele Swift 6 ou il fallait reflechir a l'isolation de chaque declaration individuellement.
SwiftUI scene bridging : la fin des bricolages
Si tu maintiens une app avec du code UIKit ou AppKit existant (autrement dit, la majorite des apps professionnelles), scene bridging est le changement le plus attendu.
Le probleme jusqu'ici : integrer un UIViewController dans SwiftUI necessitait UIViewControllerRepresentable avec ses methodes makeUIViewController, updateUIViewController, un Coordinator pour les delegates... C'etait verbeux, fragile, et source de bugs subtils (cycles de vie differents, fuites memoire).
Scene bridging introduit une approche radicalement plus simple :
// AVANT : UIViewControllerRepresentable (Swift 6)
struct LegacyEditorView: UIViewControllerRepresentable {
@Binding var document: Document
func makeUIViewController(context: Context) -> EditorViewController {
let vc = EditorViewController()
vc.delegate = context.coordinator
return vc
}
func updateUIViewController(_ vc: EditorViewController, context: Context) {
vc.document = document
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, EditorDelegate {
var parent: LegacyEditorView
init(_ parent: LegacyEditorView) { self.parent = parent }
func didUpdate(_ doc: Document) {
parent.document = doc
}
}
}
// APRÈS : Scene Bridging (Swift 6.2)
WindowGroup {
SceneBridge {
EditorViewController()
}
.onDocumentChange { document in
// Binding automatique
}
}
Dans l'autre sens, integrer du SwiftUI dans une app UIKit est aussi simplifie :
// Intégrer SwiftUI dans une scène UIKit
let scene = UIHostingScene {
SettingsView()
.environment(\.colorScheme, .dark)
}
// Remplacer un UIViewController par du SwiftUI in-place
class LegacyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let hosting = UIHostingController(rootView: ModernView())
addChild(hosting)
view.addSubview(hosting.view)
hosting.view.frame = view.bounds
hosting.didMove(toParent: self)
}
}
L'avantage concret : tu peux migrer ton app UIKit/AppKit vers SwiftUI de maniere incrementale, ecran par ecran, sans les bricolages de Representable. C'est la strategie de migration que la plupart des equipes attendent depuis des annees.
Observation framework : la maturite
Introduit en Swift 5.9, le framework Observation remplace progressivement ObservableObject + @Published. Swift 6.2 apporte des ameliorations significatives.
// Observation framework (recommandé en Swift 6.2)
@Observable
class ProjectStore {
var projects: [Project] = []
var selectedProject: Project?
var isLoading = false
func load() async {
isLoading = true
projects = await api.fetchProjects()
isLoading = false
}
}
// Utilisation dans SwiftUI : plus simple qu'ObservableObject
struct ProjectListView: View {
@State private var store = ProjectStore()
var body: some View {
List(store.projects) { project in
ProjectRow(project: project)
}
.task { await store.load() }
}
}
Les gains par rapport a ObservableObject :
- Pas de
@Published. Toutes les proprietes sont automatiquement observees. - Granularite fine. SwiftUI ne re-rend que les vues qui dependent des proprietes qui ont change. Avec
ObservableObject, un changement sur n'importe quelle@Publisheddeclenchait un re-rendu de toutes les vues observant l'objet. - Pas de
@ObservedObject/@EnvironmentObject. Un simple@Stateou une reference directe suffit.
Swift 6.2 ameliore les performances de l'Observation framework et resout des edge cases autour de la concurrence et des collections observables.
Custom Controls SwiftUI
Autre ajout important : les Custom Controls. Jusqu'a present, creer un controle personnalise en SwiftUI qui se comportait comme un controle systeme etait un exercice penible. Il fallait gerer manuellement l'accessibilite, le Dynamic Type, les etats (pressed, disabled, focused), et maintenant Liquid Glass.
Swift 6.2 introduit un protocole Control qui te donne tout ca automatiquement :
struct RatingControl: Control {
@Binding var rating: Int
let maxRating: Int
var body: some ControlContent {
HStack {
ForEach(1...maxRating, id: \.self) { star in
Image(systemName: star <= rating ? "star.fill" : "star")
.controlAction {
rating = star
}
}
}
.controlLabel("Note")
.controlValue(Text("\(rating) sur \(maxRating)"))
}
}
Le controle herite automatiquement du support VoiceOver, du Dynamic Type, de l'adaptation a Liquid Glass, et des animations systeme. C'est le genre de chose qu'on attendait depuis le debut de SwiftUI.
Strategie de migration : par ou commencer
Si tu es encore sur Swift 5.x ou Swift 6.0, voici l'approche recommandee :
Etape 1 : Active les UpcomingFeature flags un par un.
// Package.swift - activation progressive
.swiftSettings([
.enableUpcomingFeature("StrictConcurrency"),
.enableUpcomingFeature("InferSendableFromCaptures"),
])
Commence par les fichiers les plus simples (modeles de donnees, utilitaires). Corrige les erreurs, comprends les patterns. Puis etends aux ViewModels et aux vues.
Etape 2 : Active le default actor isolation sur ton module principal. Ca elimine la majorite des annotations @MainActor et simplifie enormement le code.
Etape 3 : Migre de ObservableObject vers @Observable. Un fichier a la fois. Les deux peuvent coexister dans la meme app.
Etape 4 : Adopte scene bridging pour les composants UIKit/AppKit. Remplace les Representable les plus fragiles en premier.
Etape 5 : Explore les Custom Controls pour tes composants UI personnalises.
L'erreur a eviter : tout migrer d'un coup. Swift 6.2 est concu pour une migration incrementale. Profites-en.
En resume
Swift 6.2 est la version que Swift 6 aurait du etre. La strict concurrency passe de "theoriquement correcte mais pratiquement inutilisable" a "pragmatique et adoptable". Scene bridging et les Custom Controls repondent a des besoins reels des devs d'apps professionnelles. Et l'Observation framework atteint la maturite necessaire pour remplacer definitivement ObservableObject.
Si tu as repousse la migration vers Swift 6, c'est le moment. Swift 6.2 a fait le gros du travail pour toi.
Ressources
- Swift.org - Swift 6.2 Release -- notes de version et guide de migration officiel
- WWDC 2025 - What's new in Swift -- session video officielle
- Swift Evolution Proposals -- les proposals acceptees pour Swift 6.2
- Hacking with Swift - Swift 6.2 -- tutoriels pratiques par Paul Hudson
- Point-Free - Modern SwiftUI -- episodes sur Observation et la concurrence