Section 8/181 menit
8. Schema Versioning & Migration
8. Schema Versioning & Migration
Struktur VersionedSchema
swift
import SwiftData
// Setiap versi schema didefinisikan dalam enum terpisah
// Model di dalam enum menggunakan nama yang sama tapi tipe berbeda
// Versi 1: User hanya punya name
enum SchemaV1: VersionedSchema {
static var versionIdentifier = Schema.Version(1, 0, 0)
static var models: [any PersistentModel.Type] { [User.self, Task.self] }
@Model final class User {
var name: String
var email: String
init(name: String, email: String) {
self.name = name
self.email = email
}
}
@Model final class Task {
var title: String
var isCompleted: Bool = false
init(title: String) { self.title = title }
}
}
// Versi 2: User punya firstName + lastName (split dari name)
// Task punya dueDate yang baru
enum SchemaV2: VersionedSchema {
static var versionIdentifier = Schema.Version(2, 0, 0)
static var models: [any PersistentModel.Type] { [User.self, Task.self] }
@Model final class User {
var firstName: String
var lastName: String
var email: String
init(firstName: String, lastName: String, email: String) {
self.firstName = firstName
self.lastName = lastName
self.email = email
}
var fullName: String { "\(firstName) \(lastName)" }
}
@Model final class Task {
var title: String
var isCompleted: Bool = false
var dueDate: Date?
var priority: String = "medium"
init(title: String) { self.title = title }
}
}
// Versi 3: User punya profileImageData (external storage)
enum SchemaV3: VersionedSchema {
static var versionIdentifier = Schema.Version(3, 0, 0)
static var models: [any PersistentModel.Type] { [User.self, Task.self] }
@Model final class User {
var firstName: String
var lastName: String
var email: String
@Attribute(.externalStorage)
var profileImageData: Data?
init(firstName: String, lastName: String, email: String) {
self.firstName = firstName
self.lastName = lastName
self.email = email
}
}
@Model final class Task {
var title: String
var isCompleted: Bool = false
var dueDate: Date?
var priority: String = "medium"
init(title: String) { self.title = title }
}
}
SchemaMigrationPlan
swift
// Definisikan migration path dari satu versi ke versi berikutnya
enum AppMigrationPlan: SchemaMigrationPlan {
// Daftar semua versi schema yang pernah ada (urutan kronologis)
static var schemas: [any VersionedSchema.Type] {
[SchemaV1.self, SchemaV2.self, SchemaV3.self]
}
// Stage migration yang perlu logika khusus
static var stages: [MigrationStage] {
[migrateV1toV2, migrateV2toV3]
}
// V1 → V2: split "name" menjadi "firstName" dan "lastName"
static let migrateV1toV2 = MigrationStage.custom(
fromVersion: SchemaV1.self,
toVersion: SchemaV2.self,
willMigrate: nil,
didMigrate: { context in
// Setelah schema diupdate, isi nilai baru dari data lama
// CATATAN: di sini kita akses SchemaV2.User (versi baru)
let users = try context.fetch(FetchDescriptor<SchemaV2.User>())
for user in users {
// Data nama lama sudah di-migrate ke firstName oleh SwiftData
// (karena nama kolom sama, SwiftData map otomatis)
// Tapi lastName perlu diisi:
if user.lastName.isEmpty {
// Split dari firstName jika memungkinkan
let parts = user.firstName.split(separator: " ", maxSplits: 1)
if parts.count == 2 {
user.firstName = String(parts[0])
user.lastName = String(parts[1])
} else {
user.lastName = "" // Tidak ada last name
}
}
}
try context.save()
}
)
// V2 → V3: tambah profileImageData (nullable, default nil — tidak butuh logika khusus)
static let migrateV2toV3 = MigrationStage.lightweight(
fromVersion: SchemaV2.self,
toVersion: SchemaV3.self
)
}
// Gunakan di ModelContainer
let container = try ModelContainer(
for: SchemaV3.User.self, SchemaV3.Task.self,
migrationPlan: AppMigrationPlan.self
)
Lightweight vs Custom Migration
| Perubahan | Jenis Migration |
|---|---|
| Tambah property nullable baru | Lightweight |
| Tambah property dengan default value | Lightweight |
| Hapus property | Lightweight |
Rename property (dengan originalName) |
Lightweight |
Rename entity (dengan originalName di @Model) |
Lightweight |
| Split/merge property | Custom (butuh logic) |
| Ubah tipe property | Custom (butuh logic) |
| Transformasi data non-trivial | Custom (butuh logic) |