Section 9/181 menit
9. Migrasi Schema
9. Migrasi Schema
Setiap kali schema berubah (tambah properti, ubah nama, hapus model), SwiftData harus diberitahu agar bisa memigrasikan data lama.
Lightweight Migration (Otomatis)
Untuk perubahan sederhana — menambah properti opsional atau dengan default value — SwiftData dapat memigrasi secara otomatis tanpa konfigurasi tambahan:
swift
// v1: model awal
@Model final class Item {
var name: String
init(name: String) { self.name = name }
}
// v2: tambah properti dengan default — lightweight migration otomatis
@Model final class Item {
var name: String
var createdAt: Date = Date() // default value → safe untuk migrate
var notes: String? // optional → safe untuk migrate
init(name: String) { self.name = name }
}
Custom Migration dengan VersionedSchema
Untuk perubahan yang lebih kompleks (rename properti, ubah tipe, hapus dan re-create relasi):
swift
// MARK: - Schema Versions
enum AppSchema: VersionedSchema {
static var versionIdentifier = Schema.Version(1, 0, 0)
static var models: [any PersistentModel.Type] { [AppSchema.Task.self] }
@Model
final class Task {
var title: String
var done: Bool // nama lama
init(title: String) {
self.title = title
self.done = false
}
}
}
enum AppSchemaV2: VersionedSchema {
static var versionIdentifier = Schema.Version(2, 0, 0)
static var models: [any PersistentModel.Type] { [AppSchemaV2.Task.self] }
@Model
final class Task {
var title: String
var isCompleted: Bool // nama baru — perlu custom migration
var priority: Int
init(title: String) {
self.title = title
self.isCompleted = false
self.priority = 0
}
}
}
// MARK: - Migration Plan
enum AppMigrationPlan: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] {
[AppSchema.self, AppSchemaV2.self]
}
static var stages: [MigrationStage] {
[migrateV1toV2]
}
static let migrateV1toV2 = MigrationStage.custom(
fromVersion: AppSchema.self,
toVersion: AppSchemaV2.self,
willMigrate: nil,
didMigrate: { context in
// Setelah migrasi struktural selesai, migrasikan data
let descriptor = FetchDescriptor<AppSchemaV2.Task>()
let tasks = try context.fetch(descriptor)
// isCompleted sudah di-set dari 'done' oleh Core Data migration engine
// karena kita register mapping di originalName
try context.save()
}
)
}
// MARK: - Penggunaan di App
@main
struct MyApp: App {
let container: ModelContainer
init() {
do {
container = try ModelContainer(
for: AppSchemaV2.Task.self,
migrationPlan: AppMigrationPlan.self
)
} catch {
fatalError("Failed to create container: \(error)")
}
}
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(container)
}
}