← Tous les articles

Migrations SwiftData : légère ou personnalisée, et quand un V2 est inutile

La gestion des migrations de schéma par SwiftData constitue un progrès structurel par rapport à Core Data, avec un piège dans lequel les équipes tombent sans cesse : déclarer un nouveau VersionedSchema pour des changements que SwiftData traiterait automatiquement grâce aux valeurs par défaut déclarées en ligne. Le résultat est un plantage sur l’appareil, « Duplicate version checksums across stages detected », alors même que le code semblait correct et compilait proprement. Le véritable modèle de migration du framework repose sur trois briques (VersionedSchema, MigrationStage, SchemaMigrationPlan) et sur trois types de migration : automatique légère, légère déclarée, personnalisée1. La plupart des changements de schéma sont automatiques. Certains réclament une étape légère déclarée. Une petite minorité réclame une étape personnalisée dotée des closures willMigrate et didMigrate.

Cet article parcourt le modèle de migration à la lumière de la documentation d’Apple, nomme les cas que couvre chaque type de migration et traite de l’héritage de classes apporté par iOS 26 ainsi que du sort réservé aux migrations dans les bêtas d’iOS 27. Le cadre de lecture est « ce que je déclare face à ce que SwiftData prend en charge pour moi », car cette décision détermine si la migration part proprement en production ou plante au premier lancement. La question complémentaire, concevoir un schéma v1 de sorte que ces migrations restent peu coûteuses, fait l’objet de Le vrai coût de SwiftData, c’est la discipline de schéma.

En bref

  • Les migrations SwiftData combinent trois protocoles : VersionedSchema (un instantané des types de modèle à une version donnée), MigrationStage (une transition unique de fromVersion vers toVersion, avec les cas .lightweight ou .custom) et SchemaMigrationPlan (la liste ordonnée des étapes)1.
  • Ajouter une propriété @Model avec une valeur par défaut en ligne (var foo: Bool = false) n’exige aucun nouveau VersionedSchema. SwiftData traite l’ajout automatiquement, comme une migration légère. Déclarer un V2 pour cela produit des plantages « Duplicate version checksums across stages detected ».
  • Les migrations légères prennent en charge : l’ajout, le renommage et la suppression d’entités, d’attributs et de relations ; le changement de type de relation ; la déclaration de @Attribute(originalName:) pour suivre les renommages ; la définition de règles de suppression. La plupart des changements de schéma entrent dans ce cadre.
  • Les migrations personnalisées (MigrationStage.custom(fromVersion:toVersion:willMigrate:didMigrate:)) prennent en charge les transformations de données : scinder une colonne en deux, calculer des champs dérivés, déplacer des données entre modèles. willMigrate reçoit l’ancien contexte, didMigrate le nouveau.
  • iOS 26 ajoute l’héritage de classes pour les types @Model2. Les schémas qui adoptent l’héritage passent à une nouvelle version, avec une étape légère depuis la version précédente à modèles plats.

Le modèle en trois briques

Une migration SwiftData se compose de trois briques.

VersionedSchema

Un instantané des types de modèle à une version de schéma donnée1. Le protocole exige :

  • static var versionIdentifier: Schema.Version. Un triplet de version sémantique (Schema.Version(1, 0, 0)).
  • static var models: [any PersistentModel.Type]. Le tableau des types @Model présents dans cette version.
enum SchemaV1: VersionedSchema {
    static let versionIdentifier = Schema.Version(1, 0, 0)
    static var models: [any PersistentModel.Type] {
        [Item.self]
    }

    @Model
    final class Item {
        var name: String
        var createdAt: Date
        init(name: String, createdAt: Date) {
            self.name = name
            self.createdAt = createdAt
        }
    }
}

Le motif « énumération avec types imbriqués » est la convention établie. Chaque VersionedSchema donne un espace de noms propre à ses classes de modèle, de sorte que plusieurs schémas portant le même nom de modèle peuvent coexister dans le code pendant une migration.

MigrationStage

Une transition unique entre deux types VersionedSchema3. Deux cas :

  • .lightweight(fromVersion: any VersionedSchema.Type, toVersion: any VersionedSchema.Type). Déclare une transition que SwiftData réalise sans code applicatif. Les paramètres sont les types VersionedSchema eux-mêmes (par exemple SchemaV1.self), et non des valeurs Schema.Version brutes.
  • .custom(fromVersion:toVersion:willMigrate:didMigrate:). Déclare une transition assortie de code exécuté avant et/ou après la migration des données. Les arguments de version ont les mêmes types que pour .lightweight.

SchemaMigrationPlan

La liste ordonnée des étapes qui mène le schéma de n’importe quelle version antérieure à la version courante1.

enum AppMigrationPlan: SchemaMigrationPlan {
    static var schemas: [any VersionedSchema.Type] {
        [SchemaV1.self, SchemaV2.self, SchemaV3.self]
    }

    static var stages: [MigrationStage] {
        [migrateV1toV2, migrateV2toV3]
    }

    static let migrateV1toV2 = MigrationStage.lightweight(
        fromVersion: SchemaV1.self,
        toVersion: SchemaV2.self
    )

    static let migrateV2toV3 = MigrationStage.custom(
        fromVersion: SchemaV2.self,
        toVersion: SchemaV3.self,
        willMigrate: { context in
            // Pre-migration: read old data, prepare it
            try context.save()
        },
        didMigrate: { context in
            // Post-migration: backfill new fields
            let descriptor = FetchDescriptor<SchemaV3.Item>()
            let items = try context.fetch(descriptor)
            for item in items {
                item.computedField = computeFromExisting(item)
            }
            try context.save()
        }
    )
}

Le ModelContainer est configuré à la fois avec le schéma courant et avec le plan de migration :

let container = try ModelContainer(
    for: SchemaV3.Item.self,
    migrationPlan: AppMigrationPlan.self,
    configurations: ModelConfiguration(...)
)

À la création du conteneur, SwiftData lit la version de schéma courante du magasin persistant, parcourt les étapes du plan depuis cette version jusqu’à la version courante, et applique chaque étape dans l’ordre.

Ce que les migrations légères prennent en charge automatiquement

La plupart des changements de schéma ne réclament aucune étape personnalisée1 :

  • Ajouter un attribut doté d’une valeur par défaut. var foo: Bool = false sur un @Model existant est automatique.
  • Ajouter une nouvelle entité (classe de modèle). Les nouveaux types apparaissent dès que leur VersionedSchema devient le schéma courant ; les données existantes sont préservées.
  • Supprimer un attribut ou une entité. SwiftData abandonne la colonne ou la table.
  • Renommer un attribut ou une entité. Ajoutez @Attribute(originalName: "oldName") sur la propriété pour préserver les données ; SwiftData fait correspondre l’ancien au nouveau.
  • Changer le type d’une relation. De un-à-plusieurs à plusieurs-à-plusieurs, etc.
  • Définir des règles de suppression. @Relationship(deleteRule: .cascade) et les ajouts analogues sont légers.

Pour les changements de cette liste, le bon réflexe consiste à ne déclarer aucun nouveau VersionedSchema dès lors que les types de modèle restent inchangés par ailleurs. SwiftData exécute la migration légère automatiquement, face au schéma existant.

Le piège : ajouter un champ n’exige pas de V2

L’erreur la plus courante en matière de migration SwiftData : une développeuse ajoute une propriété avec une valeur par défaut en ligne (var foo: Bool = false), puis déclare un SchemaV2 qui référence les mêmes types de modèle que SchemaV1. La compilation est propre. Le premier lancement sur un appareil contenant des données V1 plante avec Duplicate version checksums across stages detected, parce que SchemaV1 et SchemaV2 produisent la même somme de contrôle (les types de modèle n’ont pas changé d’une manière que SwiftData perçoit comme différente).

Le bon motif : laisser le VersionedSchema existant tranquille, ajouter la nouvelle propriété au modèle avec une valeur par défaut en ligne, et laisser la migration légère automatique de SwiftData s’en charger. Aucun MigrationPlan, aucune MigrationStage, aucun V2 n’est nécessaire.

// V1 schema
enum SchemaV1: VersionedSchema {
    @Model
    final class Item {
        var name: String
        // BEFORE: just these two properties
        var createdAt: Date
        // AFTER: add a third with inline default
        var isFavorite: Bool = false   // Lightweight, automatic
    }
}

Le changement var isFavorite: Bool = false part en production sans la moindre déclaration de MigrationStage. L’initialiseur de ModelContainer qui ne passe pas migrationPlan: fonctionne :

let container = try ModelContainer(
    for: SchemaV1.Item.self,
    configurations: ModelConfiguration(...)
)

Le schéma V2 n’est requis que lorsqu’un changement ne peut pas être léger : une transformation de données, une scission de modèle, une restructuration par héritage qui réclame une logique dédiée. Dans ces cas-là, le V2 est réel et un SchemaMigrationPlan orchestre la transition.

Quand les migrations personnalisées s’imposent

Les migrations personnalisées justifient leur complexité dans trois cas.

1. Scinder un champ en plusieurs. Un champ String qui contient "Last, First" devient deux champs, firstName et lastName. La migration doit lire l’ancienne valeur, l’analyser, puis écrire les nouveaux champs.

static let migrateV1toV2 = MigrationStage.custom(
    fromVersion: SchemaV1.self,
    toVersion: SchemaV2.self,
    willMigrate: nil,
    didMigrate: { context in
        let descriptor = FetchDescriptor<SchemaV2.Person>()
        let people = try context.fetch(descriptor)
        for person in people {
            let parts = person.fullName.split(separator: ", ", maxSplits: 1)
            person.lastName = String(parts.first ?? "")
            person.firstName = String(parts.dropFirst().first ?? "")
        }
        try context.save()
    }
)

La closure didMigrate s’exécute face au contexte du nouveau schéma : les nouveaux champs sont donc accessibles. La suppression de l’ancien fullName devra peut-être attendre que les nouveaux champs soient remplis ; le nettoyage devient alors une étape V2 vers V3 ultérieure.

2. Calculer des champs dérivés. Un nouvel @Attribute qui dépend de données existantes doit être rempli au moment de la migration.

3. Déplacer des données entre modèles. Une réorganisation où les données d’Item se répartissent entre Item et un nouveau modèle Tag réclame une logique dédiée pour attribuer les étiquettes à partir des anciennes données.

Le principe général : léger quand la forme du schéma change ; personnalisé quand la forme des données change.

willMigrate face à didMigrate

Les étapes personnalisées comportent deux closures, appelées à des moments différents4.

willMigrate s’exécute avant que SwiftData n’applique la migration de schéma. Le contexte de modèle reçu par la closure est celui de l’ancien schéma. Servez-vous-en pour capturer des données, les dénormaliser ou préparer un état auxiliaire avant que le schéma ne change sous vos pieds.

didMigrate s’exécute après la migration de schéma. Le contexte de modèle est celui du nouveau schéma. Servez-vous-en pour remplir les nouveaux champs, calculer des données dérivées ou finaliser la migration.

Chacune des closures peut valoir nil si elle est inutile. La plupart des migrations personnalisées n’emploient que didMigrate ; willMigrate devient utile quand la migration doit lire d’anciennes données qui ne seront plus accessibles après le changement de schéma.

La closure reçoit un ModelContext et peut interroger, modifier et enregistrer. Elle est déclarée throwing : les erreurs remontent hors de la migration et l’interrompent.

iOS 26 : héritage de classes pour @Model

iOS 26 introduit l’héritage de classes pour les modèles SwiftData2. Les modèles peuvent désormais entretenir des relations parent-enfant :

@Model
class Vehicle {
    var make: String
    var year: Int
    init(make: String, year: Int) {
        self.make = make
        self.year = year
    }
}

@Model
final class Car: Vehicle {
    var doorCount: Int
    init(make: String, year: Int, doorCount: Int) {
        self.doorCount = doorCount
        super.init(make: make, year: year)
    }
}

Les schémas qui adoptent l’héritage passent à une nouvelle version avec une étape de migration légère depuis la version précédente à modèles plats. La transition est automatique si l’héritage préserve les propriétés existantes ; les nouveaux champs de la sous-classe suivent le motif habituel des valeurs par défaut en ligne.

Ce motif convient aux cas où plusieurs types @Model partagent des caractéristiques : un parent Vehicle avec les enfants Car, Truck, Motorcycle ; un parent Account avec les enfants CheckingAccount et SavingsAccount. Les propriétés communes vivent sur le parent, les spécificités sur les enfants.

iOS 27 : le modèle de migration tient, le magasin devient observable

Les bêtas d’iOS 27 ne changent rien à la mécanique de migration elle-même. VersionedSchema, MigrationStage et SchemaMigrationPlan sont repris tels quels, et tous les motifs ci-dessus s’appliquent à la lettre. Ce qu’iOS 27 ajoute se situe à côté des migrations plutôt qu’à l’intérieur : une nouvelle surface « Data store observation » avec deux types, ResultsObserver et HistoryObserver, ainsi qu’une option @Attribute(.codable) qui stocke une propriété via sa représentation Codable6.

Deux de ces ajouts méritent d’être mentionnés dans un guide consacré aux migrations.

@Attribute(.codable) réduit la pression migratoire future. Un type valeur Codable stocké de façon déclarative, ce sont autant de cas en moins où il aurait fallu aplatir une structure en colonnes parallèles puis écrire plus tard une étape personnalisée pour la reconstituer. Les schémas qui adoptent l’option sur de nouvelles propriétés suivent les règles de valeur par défaut en ligne vues plus haut : il s’agit d’une option d’attribut, pas d’un changement de forme du schéma6.

HistoryObserver referme la boucle après une migration personnalisée. Un remplissage effectué dans didMigrate écrit des lignes dont le reste de l’application (et tout widget ou extension à l’écoute du magasin) doit être informé. Sous iOS 27, un observateur qui suit l’historique persistant via HistoryObserver voit arriver les transactions de la migration et peut appeler ModelContext.fetchHistory pour lire exactement ce qui a changé, filtré par type de modèle et par auteur de transaction, plutôt que de tout recharger6. Le récit complet de l’observation est traité dans SwiftData sous iOS 27 : observation et historique.

Ce qu’il faut en retenir côté planification : rien dans les bêtas d’iOS 27 n’impose de monter d’une version de schéma, et aucun code de migration n’est à réécrire. Adoptez les nouveaux types d’observation là où votre logique de réconciliation post-migration interrogeait ou rechargeait en boucle.

Tester les migrations

Une migration qui compile n’est pas une migration prête à partir. Trois pratiques de test valent la peine avant la mise en production.

1. Test aller-retour sur une copie de la base de production. Récupérez une base récente ayant la forme de la production (ou produisez des données V1 synthétiques par des tests), ouvrez-la avec le conteneur qui connaît le V2 et vérifiez que les données migrent correctement. Ce test attrape les bugs de migration personnalisée que le vérificateur de types ne peut pas voir.

2. L’ancienne version démarre toujours. Compilez la version précédente de l’application, lancez-la une fois pour produire des données V1, puis compilez la nouvelle version et vérifiez qu’elle démarre sans planter. Ce test attrape le piège « Duplicate version checksums » et les erreurs de déclaration voisines.

3. Reprise après échec de migration. Que se passe-t-il si la migration lève une erreur ? Le comportement de SwiftData dépend de la configuration du conteneur ; dans une application de production, une erreur de migration non traitée ne doit jamais supprimer silencieusement les données de l’utilisateur. Testez explicitement le chemin d’échec et décidez de ce que fait l’application : retour arrière, message, restauration depuis une sauvegarde.

L’article Une seule source de vérité, issu du même ensemble, traite la question voisine de ce qui arrive quand un magasin SwiftData est remplacé par une synchronisation entre processus. Les migrations sont l’équivalent local de ce motif.

Livrer des migrations entre processus et rendre la progression visible

Deux détails opérationnels que la documentation ne met pas en avant, mais que l’équipe SwiftData a soulignés à la WWDC 20265 : où s’exécutent les migrations quand une application possède des widgets ou des extensions, et comment piloter une interface de progression pendant qu’une migration tourne.

Un seul processus est propriétaire de la migration. Les widgets et les extensions ne disposent pas des mêmes ressources d’exécution que l’application principale : ils ne peuvent donc pas réaliser une migration en toute sécurité. La consigne est de tenir le SchemaMigrationPlan1 entièrement hors des cibles widget et extension, et de ne jamais migrer depuis celles-ci. Choisissez un processus, normalement l’application principale, comme propriétaire de la base. Si un widget ouvre le conteneur alors que le magasin sur disque se trouve à un schéma non versionné (plus ancien), l’ouverture échoue. Traitez cette erreur comme le signal qu’une migration s’impose : affichez une interface qui invite l’utilisateur à ouvrir l’application principale, laissez celle-ci réaliser la migration, puis écrire la version de schéma migrée dans un UserDefault partagé. Le widget lira cette valeur la fois suivante et ouvrira le conteneur à la version vers laquelle l’application a déjà migré. Ce motif garde un seul écrivain aux commandes et évite que deux processus ne se disputent l’évolution du même fichier.

La progression se calcule à partir du nombre d’étapes, pas du temps écoulé. SwiftData n’expose aucune API dédiée à la progression d’une migration5. Pour alimenter un indicateur de progression, comptez le nombre total d’étapes de migration personnalisées du plan et surchargez le gestionnaire didMigrate de chaque étape4 afin que chacune signale sa position : « étape N sur M ». Le chiffre reflète les étapes accomplies, non le temps écoulé : la barre avance donc par paliers discrets plutôt que de façon continue. La décision de conception qui l’accompagne porte sur ce que l’application affiche pendant la migration : un simple indicateur d’activité se lit comme un blocage, et les utilisateurs décrochent. Gardez l’application partiellement utilisable là où les données le permettent, ou décrivez au minimum ce que chaque étape apporte (les nouvelles fonctionnalités que la migration débloque), pour que l’attente se lise comme une progression vers quelque chose et non comme du temps mort.

Modes de défaillance courants

Trois motifs tirés des journaux d’erreurs SwiftData.

Déclarer un V2 pour un changement que SwiftData traiterait automatiquement. Le plantage « Duplicate version checksums ». Correctif : ne déclarez pas de nouveau schéma pour l’ajout de propriétés à valeur par défaut en ligne ; laissez SwiftData s’en occuper automatiquement.

Du code de migration personnalisée qui n’enregistre pas. Une closure didMigrate qui modifie des entités sans appeler context.save() produit une migration qui s’exécute une fois, jette son travail et recommence à chaque lancement (parce qu’elle paraît inachevée). Correctif : toute closure qui modifie des données doit appeler try context.save() avant de rendre la main.

Renommer une propriété sans @Attribute(originalName:). SwiftData considère la nouvelle propriété comme nouvelle et l’ancienne comme supprimée : les données présentes sur l’ancienne propriété disparaissent. Correctif : déclarez @Attribute(originalName: "oldName") var newName: ... pour que SwiftData fasse suivre les données à travers le renommage.

Ce que ce motif implique pour les applications iOS 26 et suivantes

Trois enseignements.

  1. Par défaut, pas d’échelle de VersionedSchema. Ajouter des propriétés avec valeurs par défaut en ligne, supprimer des champs inutilisés, renommer avec @Attribute(originalName:) : tout cela est léger et automatique. L’échelle de VersionedSchema est réservée aux changements que SwiftData ne peut réellement pas traiter seul : transformations de données, logique dédiée, restructurations par héritage.

  2. Employez MigrationStage.custom pour les transformations de données, pas pour les changements de forme du schéma. Les closures willMigrate et didMigrate servent à du code qui opère sur les données, non à déclarer que le schéma a changé. Les changements de forme passent par des étapes légères.

  3. Testez les migrations avec de vraies données V1, pas seulement des données synthétiques. Des migrations qui réussissent sur des allers-retours synthétiques peuvent échouer sur des données de forme réelle avec leurs cas limites : champs nullables que le schéma ne couvrait pas, volumes qui atteignent le délai d’expiration, etc. Le coût du test est faible ; le coût d’un plantage de migration au premier lancement est bien réel.

L’ensemble complet consacré à l’écosystème Apple : les App Intents typés ; les serveurs MCP ; la question de l’aiguillage ; les Foundation Models ; la distinction entre LLM d’exécution et LLM d’outillage ; les trois surfaces ; le motif de la source unique de vérité ; Deux serveurs MCP ; les hooks pour le développement Apple ; les Live Activities ; le contrat d’exécution de watchOS ; les entrailles de SwiftUI ; le modèle mental spatial de RealityKit ; la discipline de schéma dans SwiftData ; les motifs Liquid Glass ; la livraison multiplateforme ; la matrice des plateformes ; le framework Vision ; les Symbol Effects ; l’inférence Core ML ; l’API Writing Tools ; Swift Testing ; le Privacy Manifest en profondeur ; l’accessibilité comme fonction de plateforme ; la typographie SF Pro ; les motifs spatiaux de visionOS ; le framework Speech ; ce sur quoi je refuse d’écrire. Le point de rassemblement est la série Écosystème Apple. Pour un contexte plus large sur iOS et les agents IA, consultez le guide du développement d’agents iOS.

Questions

Ai-je toujours besoin d’un SchemaMigrationPlan ?

Non. Les applications qui n’ont qu’une seule version de schéma (première publication, ou applications qui n’ont jamais fait que des changements légers) n’ont pas besoin de SchemaMigrationPlan. L’initialiseur de ModelContainer accepte directement les modèles du schéma. Le paramètre migrationPlan: devient nécessaire la première fois qu’une étape de migration personnalisée est déclarée (ou la première fois que l’on souhaite déclarer une échelle de versions explicite).

Comment savoir si mon changement est léger ?

La liste des changements éligibles au traitement léger, chez Apple1 : ajouter des entités, des attributs et des relations, les supprimer, renommer avec @Attribute(originalName:), changer la cardinalité d’une relation, définir des règles de suppression. Si le changement entre dans l’une de ces catégories et que la structure des classes de modèle reste inchangée par ailleurs, la migration est automatique et aucune échelle de VersionedSchema n’est requise. Si le changement réclame une transformation de données (calculer, scinder, déplacer), il est personnalisé.

willMigrate et didMigrate peuvent-elles être définies toutes les deux ?

Oui. Les deux closures sont facultatives individuellement, mais elles peuvent être fournies ensemble. willMigrate s’exécute face au contexte de l’ancien schéma avant la migration ; didMigrate s’exécute face au contexte du nouveau schéma après. Elles couvrent respectivement la préparation et la finalisation.

Que se passe-t-il si une migration lève une erreur ?

L’erreur remonte hors de l’initialisation du ModelContainer. Le conteneur ne s’ouvre pas. Le comportement de l’application dépend de la façon dont l’erreur est traitée : certaines applications affichent une interface de récupération, d’autres tentent une restauration depuis une sauvegarde, d’autres encore suppriment le magasin corrompu et repartent de zéro. SwiftData ne supprime pas silencieusement les données de l’utilisateur en cas d’échec de migration : la gestion de l’échec revient à l’application.

Comment tester une migration sans toucher aux données de production ?

Construisez une cible de test qui crée un ModelContainer pointant vers une URL de fichier temporaire, la remplit de données V1, puis l’ouvre avec le nouveau conteneur incluant le plan de migration. Vérifiez que les données migrées correspondent aux attentes. Le motif fonctionne aussi bien en tests unitaires qu’en tests d’intégration ; pour un résultat aussi réaliste que possible, utilisez une copie d’une véritable base ayant la forme de la production.

L’héritage de classes d’iOS 26 fonctionne-t-il avec des schémas existants ?

Oui, moyennant une migration légère. Les applications qui adoptent l’héritage passent à une nouvelle version de schéma (V4, par exemple) et déclarent un MigrationStage.lightweight(fromVersion: V3.self, toVersion: V4.self). Les propriétés plates de la classe parente demeurent, et les propriétés propres à la sous-classe sont ajoutées avec des valeurs par défaut en ligne. La migration légère de SwiftData absorbe le changement structurel.

Références


  1. Documentation Apple Developer : références de protocole VersionedSchema et SchemaMigrationPlan. Le modèle de migration. Voir aussi le guide connexe Adopting SwiftData for a Core Data app pour le récit complet de l’évolution de schéma. 

  2. Apple Developer : SwiftData : Dive into inheritance and schema migration (WWDC 2025, session 291). L’introduction de l’héritage de classes SwiftData dans iOS 26. 

  3. Documentation Apple Developer : MigrationStage, avec les cas .lightweight(fromVersion:toVersion:) et .custom(fromVersion:toVersion:willMigrate:didMigrate:)

  4. Documentation Apple Developer : MigrationStage.custom(fromVersion:toVersion:willMigrate:didMigrate:) pour la signature du cas. La sémantique selon laquelle willMigrate s’exécute face à l’ancien contexte et didMigrate face au nouveau est documentée dans la session 291 de la WWDC 2025, SwiftData : Dive into inheritance and schema migration, la session même qui sert de référence pour l’ajout de l’héritage dans iOS 26. 

  5. WWDC 2026, SwiftData Group Lab (session 8017). Paraphrase d’un enregistrement du SwiftData Group Lab de la WWDC 2026 transcrit localement ; Apple ne publie pas de sous-titres officiels pour les labs. Le cadrage des migrations depuis les widgets et les extensions (un seul processus est propriétaire de la migration, le chemin d’erreur constitue le signal de migration, la version migrée est conservée dans un UserDefault) ainsi que la technique de progression fondée sur le nombre d’étapes (surcharger le gestionnaire didMigrate de chaque étape pour signaler l’étape N sur M, faute d’API de progression dédiée) ont été décrits par le panel d’ingénierie SwiftData. Les symboles SchemaMigrationPlan et didMigrate de MigrationStage.custom sont confirmés par la documentation Apple Developer citée en 1 et 4 ; l’absence d’API de progression dédiée reflète le cadrage donné par le panel lors du lab. 

  6. Documentation Apple Developer : ResultsObserver et HistoryObserver (tous deux en bêta iOS 27.0, sous le thème « Data store observation » de SwiftData), ainsi que Schema.Attribute.Option.codable (bêta iOS 27.0), « uses the property’s codable representation to store the property ». D’après la session 274 de la WWDC26, What’s new in SwiftData, HistoryObserver expose un eventCounter observable qui s’incrémente à l’arrivée de nouvelles transactions, et le code y réagit en appelant ModelContext.fetchHistory avec des filtres sur le type de modèle et l’auteur de la transaction. 

Articles connexes

Le vrai coût de SwiftData, c'est la discipline du schéma

L'API de SwiftData se résume à deux macros. Le coût, c'est ce qui arrive après la mise en production. Les champs optionn…

27 min de lecture

SwiftData dans iOS 27 : observation et historique

iOS 27 ajoute à SwiftData ResultsObserver pour les changements, HistoryObserver pour l'historique persistant et le stock…

16 min de lecture

Installer et mettre à jour Codex CLI : Mac, Linux, Windows

Toutes les façons d'installer, de mettre à jour, d'épingler et de désinstaller la CLI OpenAI Codex -- script d'installat…

22 min de lecture