Passer au contenu principal
RM
Retour au blog

Concurrency sûre, scene bridging SwiftUI, Xcode AI — les nouveautés Swift 6.2 pour les devs natifs.

Radnoumane Mossabely8 min read
Swift 6.2
Swift
SwiftUI
macOS
iOS
Concurrency
0 vues

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.

hljs swift
// 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.

hljs swift
// 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 :

  1. Par defaut, tout tourne sur le MainActor (si tu actives defaultIsolation).
  2. Pour du travail en arriere-plan, tu marques explicitement avec nonisolated ou tu utilises un acteur dedie.
  3. 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 :

hljs swift
// 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 :

hljs swift
// 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.

hljs swift
// 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 @Published declenchait un re-rendu de toutes les vues observant l'objet.
  • Pas de @ObservedObject / @EnvironmentObject. Un simple @State ou 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 :

hljs swift
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.

hljs swift
// 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

Partager: