Core Data ile çalışmak hiç kolay değildi. NSManagedObjectContext, NSFetchRequest, NSPredicate... Tüm bu karmaşık API'lar sadece "bir veriyi kaydetmek" için fazla. Apple bunu biliyordu ve WWDC23'te SwiftData'yı duyurarak oyunu değiştirdi. SwiftData, Core Data'nın 20 yıllık gücünü modern Swift syntax'ı ile birleştiriyor.
İçindekiler
- SwiftData Nedir?
- Model Tanımlama: @Model Macro
- ModelContainer ve ModelContext
- CRUD İşlemleri
- Queries ve Filtering: @Query Macro
- Relationships
- Migration Stratejileri
- CloudKit Synchronization
- Core Data vs SwiftData
- Production Best Practices
SwiftData Nedir? {#swiftdata-nedir}
SwiftData, iOS 17+ ile gelen modern persistence framework'ü. Core Data üzerine kurulu ama çok daha basit bir API sunuyor.
Özellik | Core Data | SwiftData |
|---|---|---|
**Model tanımı** | .xcdatamodeld + NSManagedObject | Swift class + @Model |
**Query** | NSFetchRequest + NSPredicate | @Query + #Predicate |
**Context** | NSManagedObjectContext | ModelContext |
**Setup** | 30+ satır boilerplate | 1 satır: .modelContainer |
**SwiftUI** | Manuel entegrasyon | Native entegrasyon |
Dış Kaynaklar:
Model Tanımlama: @Model Macro {#model-tanimlama}
swift
1import SwiftData2 3// ✅ SwiftData Model - sadece @Model macro yeterli!4@Model5final class Task {6 var title: String7 var notes: String8 var isCompleted: Bool9 var priority: Priority10 var dueDate: Date?11 var createdAt: Date12 13 // Relationship14 var category: Category?15 var tags: [Tag]16 17 // Computed property (persist edilmez)18 var isOverdue: Bool {19 guard let dueDate else { return false }20 return !isCompleted && dueDate < Date()21 }22 23 init(title: String, notes: String = "", priority: Priority = .medium) {24 self.title = title25 self.notes = notes26 self.isCompleted = false27 self.priority = priority28 self.createdAt = Date()29 self.tags = []30 }31 32 enum Priority: Int, Codable, CaseIterable {33 case low = 0, medium = 1, high = 2, critical = 334 35 var label: String {36 switch self {37 case .low: return "Düşük"38 case .medium: return "Orta"39 case .high: return "Yüksek"40 case .critical: return "Kritik"41 }42 }43 }44}45 46// @Attribute ile özelleştirme47@Model48final class User {49 @Attribute(.unique) var email: String // Unique constraint50 var name: String51 52 @Attribute(.externalStorage) var profileImage: Data? // Büyük veri dışarıda53 54 @Attribute(.transformable(by: URLTransformer.self))55 var website: URL?56 57 @Relationship(deleteRule: .cascade) // Cascade delete58 var posts: [Post]59 60 init(email: String, name: String) {61 self.email = email62 self.name = name63 self.posts = []64 }65}ModelContainer ve ModelContext {#container-context}
swift
1// SwiftUI App'de setup - tek satır!2@main3struct MyApp: App {4 var body: some Scene {5 WindowGroup {6 ContentView()7 }8 .modelContainer(for: [Task.self, Category.self, Tag.self])9 }10}11 12// Custom configuration13let config = ModelConfiguration(14 "MyAppDB",15 schema: Schema([Task.self, Category.self]),16 isStoredInMemoryOnly: false,17 allowsSave: true,18 groupContainer: .identifier("group.com.myapp") // App Group19)20 21let container = try ModelContainer(22 for: Task.self, Category.self,23 configurations: config24)CRUD İşlemleri {#crud}
swift
1struct TaskManagerView: View {2 @Environment(\.modelContext) private var context3 4 // CREATE5 func addTask(title: String) {6 let task = Task(title: title, priority: .medium)7 context.insert(task)8 // Otomatik save! (autosave enabled by default)9 }10 11 // READ - @Query ile (aşağıda detaylı)12 13 // UPDATE - doğrudan property'yi değiştir14 func toggleComplete(_ task: Task) {15 task.isCompleted.toggle()16 // Otomatik save!17 }18 19 // DELETE20 func deleteTask(_ task: Task) {21 context.delete(task)22 }23 24 // BATCH DELETE25 func deleteCompleted() throws {26 try context.delete(model: Task.self, where: #Predicate {27 $0.isCompleted == true28 })29 }30}Queries ve Filtering: @Query Macro {#queries}
swift
1struct TaskListView: View {2 // Temel query - tüm task'ları getir3 @Query var allTasks: [Task]4 5 // Sıralı query6 @Query(sort: \Task.createdAt, order: .reverse)7 var recentTasks: [Task]8 9 // Filtrelenmiş query10 @Query(filter: #Predicate<Task> { $0.isCompleted == false })11 var activeTasks: [Task]12 13 // Karmaşık query14 @Query(15 filter: #Predicate<Task> {16 $0.isCompleted == false &&17 $0.priority.rawValue >= 218 },19 sort: [20 SortDescriptor(\Task.priority, order: .reverse),21 SortDescriptor(\Task.dueDate)22 ],23 animation: .default24 )25 var urgentTasks: [Task]26 27 // Dinamik query28 init(showCompleted: Bool) {29 let predicate: Predicate<Task>30 if showCompleted {31 predicate = #Predicate { _ in true }32 } else {33 predicate = #Predicate { $0.isCompleted == false }34 }35 _activeTasks = Query(filter: predicate, sort: \Task.createdAt)36 }37 38 var body: some View {39 List {40 ForEach(activeTasks) { task in41 TaskRow(task: task)42 }43 }44 }45}Relationships {#relationships}
swift
1@Model2final class Category {3 var name: String4 var color: String5 6 @Relationship(deleteRule: .cascade, inverse: \Task.category)7 var tasks: [Task]8 9 init(name: String, color: String) {10 self.name = name11 self.color = color12 self.tasks = []13 }14}15 16// Kullanım17func addTaskToCategory(task: Task, category: Category) {18 task.category = category19 // İlişki otomatik kurulur, iki taraf da güncellenir20}Migration Stratejileri {#migration}
SwiftData'da schema değişikliklerini yönetmek için VersionedSchema ve SchemaMigrationPlan kullanılır:
swift
1// Versiyon 1 - İlk schema2enum SchemaV1: VersionedSchema {3 static var versionIdentifier: Schema.Version = .init(1, 0, 0)4 static var models: [any PersistentModel.Type] { [TaskV1.self] }5 6 @Model final class TaskV1 {7 var title: String8 var isCompleted: Bool9 init(title: String) {10 self.title = title11 self.isCompleted = false12 }13 }14}15 16// Versiyon 2 - priority field eklendi17enum SchemaV2: VersionedSchema {18 static var versionIdentifier: Schema.Version = .init(2, 0, 0)19 static var models: [any PersistentModel.Type] { [TaskV2.self] }20 21 @Model final class TaskV2 {22 var title: String23 var isCompleted: Bool24 var priority: Int // Yeni field!25 init(title: String, priority: Int = 0) {26 self.title = title27 self.isCompleted = false28 self.priority = priority29 }30 }31}32 33// Migration planı tanımla34enum TaskMigrationPlan: SchemaMigrationPlan {35 static var schemas: [any VersionedSchema.Type] {36 [SchemaV1.self, SchemaV2.self]37 }38 static var stages: [MigrationStage] { [migrateV1toV2] }39 40 static let migrateV1toV2 = MigrationStage.lightweight(41 fromVersion: SchemaV1.self,42 toVersion: SchemaV2.self43 )44}45 46// Container'da migration planını aktif et47let container = try ModelContainer(48 for: SchemaV2.TaskV2.self,49 migrationPlan: TaskMigrationPlan.self50)Migration Türleri Karşılaştırma
Migration Türü | Ne Zaman Kullanılır | Örnek | Karmaşıklık |
|---|---|---|---|
**Lightweight** | Basit schema değişiklikleri | Property ekleme/silme, rename | Düşük |
**Custom** | Veri dönüşümü gerektiğinde | Format değişikliği, veri birleştirme | Orta |
**Staged** | Çoklu versiyon geçişi | V1 → V2 → V3 zincirleme | Yüksek |
⚠️ Migration İpucu: Lightweight migration sadece additive değişiklikler (yeni property, default değerli property) için çalışır. Property tipini değiştirme veya relationship yapısını değiştirme custom migration gerektirir.
CloudKit Synchronization {#cloudkit}
SwiftData, CloudKit ile entegre çalışarak kullanıcı verilerini tüm Apple cihazlarında senkronize eder:
swift
1// CloudKit-enabled container oluştur2let cloudConfig = ModelConfiguration(3 cloudKitDatabase: .private("iCloud.com.myapp.data")4)5 6let container = try ModelContainer(7 for: Task.self, Category.self,8 configurations: cloudConfig9)10 11// CloudKit uyumlu model kuralları12@Model13final class CloudTask {14 var title: String15 var notes: String? // Optional olmalı ✅16 var isCompleted: Bool // Default değer var ✅17 var category: Category? // Relationship optional ✅18 var createdAt: Date19 20 init(title: String) {21 self.title = title22 self.isCompleted = false23 self.createdAt = Date()24 }25}CloudKit Kuralları ve Limitleri
Kural | Açıklama |
|---|---|
Optional property'ler | CloudKit sync için property'ler optional veya default değerli olmalı |
@Attribute(.unique) yok | Unique constraint CloudKit ile uyumsuz |
Record boyutu | Max 1MB per record |
Asset boyutu | Max 250MB per asset |
Büyük dosyalar | `@Attribute(.externalStorage)` kullan |
Core Data vs SwiftData {#karsilastirma}
swift
1// ❌ Core Data - Çok fazla boilerplate2let request = NSFetchRequest<TaskEntity>(entityName: "Task")3request.predicate = NSPredicate(format: "isCompleted == %@ AND priority >= %d", NSNumber(false), 2)4request.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: false)]5let results = try context.fetch(request)6let tasks = results.map { $0.toDomain() }7 8// ✅ SwiftData - Temiz ve type-safe9@Query(10 filter: #Predicate<Task> { !$0.isCompleted && $0.priority.rawValue >= 2 },11 sort: \Task.createdAt, order: .reverse12)13var tasks: [Task]Production Best Practices {#best-practices}
🔑 Çıkarımlar
- @Model ile model tanımla - .xcdatamodeld'e gerek yok
- @Query ile SwiftUI'da reactive data fetching
- #Predicate ile type-safe filtreleme
- Autosave varsayılan - manuel save genelde gereksiz
- @Attribute(.unique) ile duplicate önle
- Migration için VersionedSchema kullan
Easter Egg
Gizli bir bilgi buldun!
Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?
Okuyucu Ödülü
Tebrikler! Bu yazıyı sonuna kadar okuduğun için sana özel bir hediyem var:
ALTIN İPUCU
Bu yazının en değerli bilgisi
Bu ipucu, yazının en önemli çıkarımını içeriyor.

