← Wszystkie wpisy

Migracje SwiftData: lightweight kontra custom oraz kiedy V2 nie jest potrzebne

Sposób, w jaki SwiftData podchodzi do migracji schematu, stanowi strukturalne ulepszenie względem Core Data, z jedną pułapką, w którą zespoły wpadają raz po raz: deklarowanie nowego VersionedSchema dla zmian, które SwiftData obsłużyłaby automatycznie poprzez wartości domyślne podane w miejscu definicji. Skutkiem jest awaria na urządzeniu z komunikatem „Duplicate version checksums across stages detected”, mimo że kod wygląda poprawnie i kompiluje się bez zarzutu. Rzeczywisty model migracji frameworka korzysta z trzech elementów (VersionedSchema, MigrationStage, SchemaMigrationPlan) oraz trzech typów migracji (automatyczna lightweight, zadeklarowana lightweight, custom)1. Większość zmian schematu jest automatyczna. Niektóre wymagają zadeklarowanego etapu lightweight. Niewielka mniejszość wymaga etapu custom z domknięciami willMigrate i didMigrate.

Ten wpis omawia model migracji w odniesieniu do dokumentacji Apple, nazywa przypadki obsługiwane przez każdy typ migracji oraz porusza nowe wsparcie dziedziczenia klas w iOS 26. Punktem odniesienia jest pytanie „co deklaruję, a co SwiftData obsłuży za mnie”, ponieważ to właśnie ta decyzja przesądza o tym, czy migracja zostanie wdrożona bezproblemowo, czy też skończy się awarią przy pierwszym uruchomieniu.

TL;DR

  • Migracje SwiftData składają się z trzech protokołów: VersionedSchema (migawka typów modeli w danej wersji), MigrationStage (pojedyncze przejście fromVersion-to-toVersion z przypadkami .lightweight lub .custom) oraz SchemaMigrationPlan (uporządkowana lista etapów)1.
  • Dodanie nowej właściwości @Model z wartością domyślną podaną w miejscu definicji (var foo: Bool = false) nie wymaga nowego VersionedSchema. SwiftData obsługuje to dodanie automatycznie jako migrację lightweight. Zadeklarowanie dla niej V2 prowadzi do awarii „Duplicate version checksums across stages detected”.
  • Migracje lightweight obsługują: dodawanie/zmianę nazwy/usuwanie encji, atrybutów i relacji; zmianę typów relacji; deklarowanie @Attribute(originalName:) w celu śledzenia zmian nazw; określanie reguł usuwania. Większość zmian schematu mieści się w tej kategorii.
  • Migracje custom (MigrationStage.custom(fromVersion:toVersion:willMigrate:didMigrate:)) obsługują przekształcenia danych: rozdzielenie jednej kolumny na dwie, obliczanie pól pochodnych, przenoszenie danych między modelami. willMigrate dysponuje starym kontekstem; didMigrate dysponuje nowym kontekstem.
  • iOS 26 dodaje dziedziczenie klas dla typów @Model2. Schematy przyjmujące dziedziczenie przechodzą na nową wersję z etapem lightweight z poprzedniej wersji o płaskim modelu.

Model trójelementowy

Migracja SwiftData składa się z trzech elementów.

VersionedSchema

Migawka typów modeli w konkretnej wersji schematu1. Protokół wymaga:

  • static var versionIdentifier: Schema.Version. Trójka wersji semantycznej (Schema.Version(1, 0, 0)).
  • static var models: [any PersistentModel.Type]. Tablica typów @Model w danej wersji.
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
        }
    }
}

Wzorzec enuma z zagnieżdżonymi typami to przyjęta konwencja. Każdy VersionedSchema umieszcza swoje klasy modeli we własnej przestrzeni nazw, dzięki czemu wiele schematów o tej samej nazwie modelu może współistnieć w bazie kodu podczas migracji.

MigrationStage

Pojedyncze przejście między dwoma typami VersionedSchema3. Dwa przypadki:

  • .lightweight(fromVersion: any VersionedSchema.Type, toVersion: any VersionedSchema.Type). Deklaruje przejście, które SwiftData obsługuje bez kodu aplikacji. Parametrami są same typy VersionedSchema (np. SchemaV1.self), a nie surowe wartości Schema.Version.
  • .custom(fromVersion:toVersion:willMigrate:didMigrate:). Deklaruje przejście z kodem uruchamianym przed migracją danych i/lub po niej. Te same typy parametrów dla argumentów wersji co w .lightweight.

SchemaMigrationPlan

Uporządkowana lista etapów, która prowadzi schemat z dowolnej wcześniejszej wersji do bieżącej1.

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()
        }
    )
}

ModelContainer jest konfigurowany zarówno z bieżącym schematem, jak i planem migracji:

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

SwiftData odczytuje przy tworzeniu kontenera bieżącą wersję schematu trwałego magazynu, przechodzi przez etapy planu od tej wersji w przód do bieżącej i stosuje każdy etap po kolei.

Co migracje lightweight obsługują automatycznie

Większość zmian schematu nie wymaga etapu custom1:

  • Dodanie atrybutu z wartością domyślną. var foo: Bool = false w istniejącym @Model jest automatyczne.
  • Dodanie nowej encji (klasy modelu). Nowe typy pojawiają się, gdy ich VersionedSchema staje się bieżącym; istniejące dane są zachowywane.
  • Usunięcie atrybutu lub encji. SwiftData usuwa kolumnę lub tabelę.
  • Zmiana nazwy atrybutu lub encji. Dodaj @Attribute(originalName: "oldName") do właściwości, aby zachować dane; SwiftData mapuje starą nazwę na nową.
  • Zmiana typu relacji. Jeden-do-wielu, wiele-do-wielu itd.
  • Określenie reguł usuwania. @Relationship(deleteRule: .cascade) i podobne dodatki są lightweight.

W przypadku zmian z tej listy właściwym wzorcem jest całkowite zrezygnowanie z deklarowania nowego VersionedSchema, jeśli typy modeli poza tym pozostają niezmienione. SwiftData wykonuje migrację lightweight automatycznie względem istniejącego schematu.

Pułapka: dodanie pola nie wymaga V2

Najczęstszy błąd przy migracji SwiftData: programista dodaje nową właściwość z wartością domyślną podaną w miejscu definicji (var foo: Bool = false), a następnie deklaruje SchemaV2 odwołujący się do tych samych typów modeli co SchemaV1. Kompilacja przebiega bez błędów. Pierwsze uruchomienie na urządzeniu z istniejącymi danymi V1 kończy się awarią z komunikatem Duplicate version checksums across stages detected, ponieważ zarówno SchemaV1, jak i SchemaV2 sprowadzają się do tej samej sumy kontrolnej (typy modeli nie zmieniły się w sposób, który SwiftData zauważyłaby jako różnicę).

Poprawny wzorzec: pozostaw istniejący VersionedSchema bez zmian, dodaj nową właściwość do modelu z wartością domyślną podaną w miejscu definicji i pozwól, by automatyczna migracja lightweight SwiftData ją obsłużyła. Żaden MigrationPlan, żaden MigrationStage, żadne V2 nie są potrzebne.

// 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
    }
}

Zmiana var isFavorite: Bool = false jest wdrażana bez żadnej deklaracji MigrationStage. Inicjalizator ModelContainer, który nie przekazuje migrationPlan:, działa:

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

Schemat V2 jest wymagany wyłącznie wtedy, gdy zmiana nie może być lightweight (przekształcenie danych, rozdzielenie modelu, restrukturyzacja dziedziczenia wymagająca własnej logiki). W takich przypadkach V2 jest realne, a SchemaMigrationPlan koordynuje przejście.

Kiedy migracje custom są wymagane

Migracje custom uzasadniają swoją złożoność w trzech przypadkach:

1. Rozdzielenie jednego pola na wiele. Pole typu String przechowujące "Last, First" zamienia się na dwa pola, firstName i lastName. Migracja musi odczytać starą wartość, sparsować ją i zapisać nowe pola.

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()
    }
)

Domknięcie didMigrate działa względem kontekstu nowego schematu, dzięki czemu nowe pola są dostępne. Usunięcie starego fullName może wymagać odroczenia, aż nowe pola zostaną wypełnione; porządkowanie odbywa się w kolejnym etapie z V2 do V3.

2. Obliczanie pól pochodnych. Nowy @Attribute, który zależy od istniejących danych, musi zostać wypełniony w czasie migracji.

3. Przenoszenie danych między modelami. Reorganizacja, w której dane z Item są rozdzielane między Item a nowy model Tag, wymaga własnej logiki przypisującej tagi na podstawie starych danych.

Ogólny wzorzec: lightweight, gdy zmienia się kształt schematu; custom, gdy zmienia się kształt danych.

willMigrate kontra didMigrate

Etapy custom mają dwa domknięcia, wywoływane w różnych momentach4:

willMigrate działa przed zastosowaniem przez SwiftData migracji schematu. Kontekst modelu otrzymywany przez to domknięcie należy do starego schematu. Należy go używać do przechwytywania danych, ich denormalizacji lub przygotowania stanu pomocniczego, zanim schemat zmieni się pod spodem.

didMigrate działa po migracji schematu. Kontekst modelu należy do nowego schematu. Należy go używać do wypełniania nowych pól, obliczania danych pochodnych lub finalizowania migracji.

Każde z domknięć może mieć wartość nil, jeśli nie jest potrzebne. Większość migracji custom korzysta wyłącznie z didMigrate; willMigrate przydaje się, gdy migracja musi odczytać stare dane, które nie będą dostępne po zmianie schematu.

Domknięcie otrzymuje ModelContext i może pobierać, modyfikować oraz zapisywać dane. Domknięcie jest rzucające; błędy propagują się na zewnątrz migracji i ją przerywają.

iOS 26: dziedziczenie klas dla @Model

iOS 26 wprowadza dziedziczenie klas dla modeli SwiftData2. Modele mogą teraz mieć relacje rodzic-dziecko:

@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)
    }
}

Schematy przyjmujące dziedziczenie przechodzą na nową wersję z etapem migracji lightweight z poprzedniej wersji o płaskim modelu. Przejście jest automatyczne, jeśli dziedziczenie zachowuje istniejące właściwości; nowe pola w podklasie podążają za standardowym wzorcem wartości domyślnej podanej w miejscu definicji.

Wzorzec ten pasuje do przypadków, w których wiele typów @Model współdzieli cechy: rodzic Vehicle z dziećmi Car, Truck, Motorcycle; rodzic Account z dziećmi CheckingAccount, SavingsAccount. Wspólne właściwości żyją w klasie rodzica; szczegóły żyją w dzieciach.

Testowanie migracji

Migracja, która się kompiluje, to nie to samo co migracja, którą można wdrożyć. Trzy wzorce testowania warte wykonania przed wydaniem:

1. Test pełnego obiegu na kopii bazy produkcyjnej. Pobierz świeżą bazę o kształcie produkcyjnym (lub wygeneruj syntetyczne dane V1 za pomocą testów), otwórz ją kontenerem świadomym V2 i sprawdź, czy dane migrują poprawnie. Test wychwytuje błędy migracji custom, których kontrola typów nie jest w stanie wykryć.

2. Stara wersja nadal się uruchamia. Zbuduj poprzednią wersję aplikacji, uruchom ją raz, aby wytworzyć dane V1, następnie zbuduj nową wersję aplikacji i sprawdź, czy uruchamia się bez awarii. Test wychwytuje pułapkę „Duplicate version checksums” oraz podobne błędy deklaracji.

3. Odzyskiwanie po nieudanej migracji. Co się dzieje, jeśli migracja rzuci błąd? Zachowanie SwiftData zależy od konfiguracji kontenera; w aplikacjach produkcyjnych nieobsłużony błąd migracji nie powinien po cichu usuwać danych użytkownika. Przetestuj jawnie ścieżkę błędu i zdecyduj, co aplikacja robi (wycofanie, monit, odzyskanie z kopii zapasowej).

Wpis z tego klastra Single Source of Truth omawia pokrewne pytanie o to, co się dzieje, gdy magazyn SwiftData zostaje zastąpiony w wyniku synchronizacji międzyprocesowej. Migracje są lokalnym, ewolucyjnym odpowiednikiem tego wzorca.

Wdrażanie migracji między procesami i prezentowanie postępu

Dwa szczegóły operacyjne, których dokumentacja nie eksponuje, ale które zespół SwiftData podkreślił na WWDC 20265: gdzie uruchamiają się migracje, gdy aplikacja ma widgety lub rozszerzenia, oraz jak sterować interfejsem postępu, gdy taka migracja trwa.

Jeden proces jest właścicielem migracji. Widgety i rozszerzenia nie otrzymują takich samych zasobów uruchomieniowych jak główna aplikacja, więc nie mogą bezpiecznie wykonać migracji. Zalecenie brzmi: całkowicie trzymaj SchemaMigrationPlan1 poza targetami widgetu i rozszerzenia i nigdy nie migruj z ich poziomu. Wybierz jeden proces, zwykle główną aplikację, jako właściciela bazy danych. Jeśli widget otworzy kontener, a magazyn na dysku jest w schemacie bez wersji (starszym), otwarcie zwróci błąd. Potraktuj ten błąd jako sygnał, że migracja jest wymagana: pokaż interfejs proszący użytkownika o otwarcie głównej aplikacji, pozwól aplikacji wykonać migrację i spraw, by aplikacja zapisała zmigrowaną wersję schematu we współdzielonym UserDefault. Widget odczyta tę wartość przy następnym uruchomieniu i otworzy kontener w wersji, do której aplikacja już zmigrowała. Wzorzec ten utrzymuje jednego zapisującego u steru i unika sytuacji, w której dwa procesy ścigają się o rozwój tego samego pliku.

Postęp jest obliczany na podstawie liczby etapów, a nie czasu zegarowego. SwiftData nie udostępnia dedykowanego API postępu migracji5. Aby sterować wskaźnikiem postępu, zlicz całkowitą liczbę etapów migracji custom w planie i nadpisz dla każdego etapu uchwyt didMigrate4, tak aby każdy etap raportował swoją pozycję, „etap N z M”. Liczba ta odzwierciedla liczbę ukończonych etapów, a nie upływ czasu, więc pasek przesuwa się skokowo, a nie płynnie. Towarzysząca temu decyzja projektowa dotyczy tego, co aplikacja pokazuje podczas migracji: gołe kółko ładowania odbierane jest jako zawieszenie i użytkownicy z niego rezygnują. Utrzymuj aplikację częściowo użyteczną tam, gdzie pozwalają na to dane, lub przynajmniej opisz, co dodaje każdy etap (nowe funkcje, które odblokowuje migracja), tak aby oczekiwanie odbierane było jako postęp ku czemuś, a nie jako martwy czas.

Typowe tryby awarii

Trzy wzorce z dzienników awarii SwiftData:

Deklarowanie V2 dla zmiany, którą SwiftData obsłużyłaby automatycznie. Awaria „Duplicate version checksums”. Rozwiązanie: nie deklaruj nowego schematu dla dodawania właściwości z wartością domyślną; pozwól SwiftData obsłużyć je automatycznie.

Kod migracji custom, który nie zapisuje danych. Domknięcie didMigrate, które modyfikuje encje, ale nie wywołuje context.save(), tworzy migrację, która uruchamia się raz, porzuca swoją pracę i ponawia się przy każdym uruchomieniu (ponieważ migracja wydaje się niedokończona). Rozwiązanie: każde domknięcie modyfikujące dane musi przed zwróceniem wywołać try context.save().

Zmiana nazwy właściwości bez @Attribute(originalName:). SwiftData traktuje nową właściwość jako nową, a starą jako usuniętą; istniejące dane w starej właściwości zostają porzucone. Rozwiązanie: zadeklaruj @Attribute(originalName: "oldName") var newName: ..., tak aby SwiftData zmapowała dane poprzez zmianę nazwy.

Co ten wzorzec oznacza dla aplikacji iOS 26+

Trzy wnioski.

  1. Domyślnie rezygnuj z drabiny VersionedSchema. Dodawanie właściwości z wartościami domyślnymi podanymi w miejscu definicji, usuwanie nieużywanych pól, zmiana nazw z @Attribute(originalName:). Wszystkie są lightweight i automatyczne. Drabina VersionedSchema jest przeznaczona dla zmian, których SwiftData naprawdę nie potrafi obsłużyć automatycznie (przekształcenia danych, własna logika, restrukturyzacje dziedziczenia).

  2. Używaj MigrationStage.custom do przekształceń danych, a nie do zmian kształtu schematu. Domknięcia willMigrate i didMigrate służą do kodu operującego na danych, a nie do deklarowania, że schemat się zmienił. Zmiany kształtu schematu przepływają przez etapy lightweight.

  3. Testuj migracje na prawdziwych danych V1, a nie tylko na danych syntetycznych. Migracje przechodzące syntetyczne testy pełnego obiegu mogą wciąż zawodzić na danych o kształcie produkcyjnym z przypadkami brzegowymi (pola dopuszczające null, których schemat nie uwzględnił, duże zbiory danych przekraczające limit czasu itd.). Koszt testowania jest niewielki; koszt awarii migracji przy pierwszym uruchomieniu jest realny.

Pełny klaster Apple Ecosystem: typowane App Intents; serwery MCP; kwestia routingu; Foundation Models; rozróżnienie między LLM uruchomieniowym a narzędziowym; trzy powierzchnie; wzorzec single source of truth; Two MCP Servers; hooki w rozwoju aplikacji Apple; Live Activities; środowisko uruchomieniowe watchOS; wnętrze SwiftUI; przestrzenny model myślowy RealityKit; dyscyplina schematu SwiftData; wzorce Liquid Glass; wdrażanie wieloplatformowe; macierz platform; framework Vision; Symbol Effects; wnioskowanie Core ML; Writing Tools API; Swift Testing; Privacy Manifest; dostępność jako platforma; typografia SF Pro; przestrzenne wzorce visionOS; framework Speech; o czym odmawiam pisać. Centrum znajduje się w serii Apple Ecosystem. Aby uzyskać szerszy kontekst dotyczący iOS z agentami AI, zobacz przewodnik po rozwoju agentów iOS.

FAQ

Czy zawsze potrzebuję SchemaMigrationPlan?

Nie. Aplikacje z pojedynczą wersją schematu (pierwsze wydanie lub aplikacje, które wprowadzały wyłącznie zmiany lightweight) nie potrzebują SchemaMigrationPlan. Inicjalizator ModelContainer przyjmuje modele schematu bezpośrednio. Parametr migrationPlan: staje się konieczny przy pierwszym zadeklarowaniu etapu migracji custom (lub gdy programista po raz pierwszy chce zadeklarować jawną drabinę wersji).

Skąd mam wiedzieć, czy moja zmiana jest lightweight?

Lista zmian kwalifikujących się do lightweight według Apple1: dodawanie encji/atrybutów/relacji, ich usuwanie, zmiana nazw z @Attribute(originalName:), zmiana liczebności relacji, określanie reguł usuwania. Jeśli zmiana pasuje do jednej z tych pozycji, a struktura klasy modelu poza tym pozostaje niezmieniona, migracja jest automatyczna i drabina VersionedSchema nie jest wymagana. Jeśli zmiana wymaga przekształcenia danych (obliczenie, rozdzielenie, przeniesienie danych), jest custom.

Czy willMigrate i didMigrate mogą być oba ustawione?

Tak. Oba domknięcia są opcjonalne z osobna, ale można podać oba. willMigrate działa względem kontekstu starego schematu, zanim SwiftData wykona migrację; didMigrate działa względem kontekstu nowego schematu po niej. Oba pokrywają odpowiednio przygotowanie i finalizację.

Co się dzieje, jeśli migracja rzuci błąd?

Błąd propaguje się na zewnątrz inicjalizacji ModelContainer. Kontener nie otwiera się. Zachowanie aplikacji zależy od tego, jak programista obsługuje błąd: niektóre aplikacje wyświetlają interfejs odzyskiwania, niektóre próbują przywrócić dane z kopii zapasowej, niektóre usuwają uszkodzony magazyn i zaczynają od nowa. SwiftData nie usuwa po cichu danych użytkownika przy nieudanej migracji; obsługa awarii należy do aplikacji.

Jak przetestować migrację bez wpływu na dane produkcyjne?

Zbuduj target testowy, który tworzy ModelContainer wskazujący na tymczasowy adres URL pliku, wypełnia go danymi V1, a następnie otwiera go nowym kontenerem zawierającym plan migracji. Sprawdź, czy zmigrowane dane odpowiadają oczekiwaniom. Wzorzec działa zarówno w testach jednostkowych, jak i integracyjnych; dla najbardziej realistycznych wyników użyj kopii rzeczywistej bazy o kształcie produkcyjnym.

Czy dziedziczenie klas w iOS 26 działa z istniejącymi schematami?

Tak, z migracją lightweight. Aplikacje przyjmujące dziedziczenie przechodzą na nową wersję schematu (np. V4) i deklarują MigrationStage.lightweight(fromVersion: V3.self, toVersion: V4.self). Płaskie właściwości klasy rodzica pozostają, a właściwości specyficzne dla podklasy są dodawane z wartościami domyślnymi podanymi w miejscu definicji. Migracja lightweight SwiftData obsługuje tę zmianę strukturalną.

Źródła


  1. Dokumentacja Apple Developer: referencje protokołów VersionedSchema oraz SchemaMigrationPlan. Model migracji. Zobacz też powiązany przewodnik Adopting SwiftData for a Core Data app, opisujący pełną narrację ewolucji schematu. 

  2. Apple Developer: SwiftData: Dive into inheritance and schema migration (sesja WWDC 2025 nr 291). Wprowadzenie dziedziczenia klas SwiftData w iOS 26. 

  3. Dokumentacja Apple Developer: MigrationStage z przypadkami .lightweight(fromVersion:toVersion:) oraz .custom(fromVersion:toVersion:willMigrate:didMigrate:)

  4. Dokumentacja Apple Developer: MigrationStage.custom(fromVersion:toVersion:willMigrate:didMigrate:) dla sygnatury przypadku. Semantyka willMigrate-działa-względem-starego-kontekstu i didMigrate-działa-względem-nowego-kontekstu jest udokumentowana w sesji WWDC 2025 nr 291 SwiftData: Dive into inheritance and schema migration, tej samej sesji, do której odnosi się dodanie dziedziczenia w iOS 26. 

  5. WWDC 2026 SwiftData Group Lab (sesja 8017). Sparafrazowane z lokalnie sporządzonej transkrypcji nagrania WWDC 2026 SwiftData Group Lab; Apple nie publikuje oficjalnych napisów do labów. Bramkowanie migracji w widgetach i rozszerzeniach (jeden proces jest właścicielem migracji, ścieżka błędu jest sygnałem migracji, zmigrowana wersja jest przechowywana w UserDefault) oraz technika postępu opartego na liczbie etapów (nadpisanie uchwytu didMigrate dla każdego etapu, aby raportować etap N z M, ponieważ nie istnieje dedykowane API postępu) zostały opisane przez panel inżynieryjny SwiftData. Symbole SchemaMigrationPlan oraz didMigrate z MigrationStage.custom są potwierdzone względem dokumentacji Apple Developer cytowanej w 1 i 4; brak dedykowanego API postępu odzwierciedla sposób ujęcia tematu przez sam panel podczas labu. 

Powiązane artykuły

Prawdziwym kosztem SwiftData jest dyscyplina schematu

API SwiftData to dwa makra. Koszt to to, co dzieje się po wydaniu aplikacji. Pola opcjonalne to tania migracja; dodanie …

14 min czytania

SwiftData w iOS 27: Observation i historia

iOS 27 daje SwiftData pełnoprawną obserwację zmian dzięki ResultsObserver, obserwację trwałej historii dzięki HistoryObs…

10 min czytania

To asystenci są teraz odbiorcami

Dane z własnego serwera brzegowego: asystenci AI żądają moich stron około 66 razy częściej, niż odwiedzają je ludzie, a …

12 min czytania