# SwiftData vs Core Data 2026: Performans Benchmark ve Production Testler
SwiftData, WWDC 2023'te "Core Data'nın modern hali" olarak tanıtıldı. 2026'ya gelindiğinde üç major update geçirdi, production ortamında binlerce uygulamada test edildi ve güçlü/zayıf yanları netleşti. Core Data ise 20+ yıllık olgunluğuyla geri çekilmiyor — aksine iOS 18 ve 19 güncellemeleriyle aktif geliştirme altında.
Bu yazı, iki framework arasındaki gerçek benchmark'ları, migration karmaşıklığını, CloudKit entegrasyonunu ve 2026 itibarıyla hangi projenin hangisini seçmesi gerektiğini kapsamlı ele alıyor.
💡 Pro Tip: SwiftData ve Core Data aslında aynı SQLite backend'i paylaşıyor. Performans farkları SQL generation kalitesinden, fetch stratejilerinden ve concurrency modellerinden geliyor — "sıfırdan farklı bir sistem" değil, aynı temelin farklı abstraction katmanları.
İçindekiler
- SwiftData 2026 Durum Raporu
- Core Data 2026 Durumu
- Macro Tabanlı Model Tanımı
- ModelContainer ve ModelActor
- @Query vs FetchRequest: Reactive Patterns
- Predicate Performansı: Swift Native vs NSPredicate
- 100K Record Benchmark Sonuçları
- Batch Insert Performansı
- Memory Footprint Analizi
- Migration: SwiftData vs Core Data
- CloudKit Sync Karşılaştırması
- Background Context Patterns
- In-Memory Store ile Test Coverage
- Hybrid Yaklaşım: SwiftData + Core Data Bridge
- Hangisini Seçmeli?
- Sonuç
SwiftData 2026 Durum Raporu
SwiftData'nın 2026 itibarıyla olgunluk seviyesi:
Artık production-ready olanlar:
- Temel CRUD işlemleri — stabil
@Queryreactive data flow — iOS 18 ile güçlendirildi- ModelActor isolation — Swift 6 concurrency ile tam uyumlu
- CloudKit sync — NSPersistentCloudKitContainer'la eşdeğer
- Schema migration (lightweight) — iOS 18 ile iyileştirildi
Hâlâ dikkat gerektiren alanlar:
- Custom migration (complex transformations) — verbose, hata riski yüksek
- Batch operations — Core Data kadar optimize değil
- NSFetchedResultsController eşdeğeri —
@Queryile yaklaşılıyor ama tam değil - Predicate builder type safety — edge case'lerde compiler hataları
iOS 26 yenilikleri:
@Queryile pagination desteği- Incremental migration improvements
- SwiftData Instruments template (yeni)
Core Data 2026 Durumu
Core Data "eski" değil — aktif geliştiriliyor:
- iOS 26:
NSPersistentStoresubclass API iyileştirildi - Batch operations API (insertBatchRequest, deleteBatchRequest) 2x hızlandı
NSFetchedResultsControlleryeni diffable data source adapter- Swift concurrency annotation'ları eklendi (
@MainActor,Sendableuyumluluğu)
Core Data'nın 2026'da hâlâ rakipsiz olduğu alanlar: complex aggregate queries, multi-entity join, advanced sorting, NSPredicate expression yoğun iş mantığı.
Macro Tabanlı Model Tanımı
SwiftData'nın en güzel yanı: model tanımı için ayrı xcdatamodeld dosyası yok.
swift
1import SwiftData2import Foundation3 4// SwiftData model — @Model macro5@Model6final class BlogPost {7 var title: String8 var slug: String9 var publishedAt: Date10 var viewCount: Int11 12 @Attribute(.unique) var id: UUID13 14 // İlişki tanımı15 @Relationship(deleteRule: .cascade)16 var comments: [Comment] = []17 18 // Indexleme19 @Attribute(.indexed) var category: String20 21 // Geçici — persist edilmez22 @Transient var isHighlighted: Bool = false23 24 init(title: String, slug: String, category: String) {25 self.id = UUID()26 self.title = title27 self.slug = slug28 self.category = category29 self.publishedAt = Date()30 self.viewCount = 031 }32}33 34@Model35final class Comment {36 var text: String37 var createdAt: Date38 var authorName: String39 40 @Attribute(.unique) var id: UUID41 42 // Inverse ilişki43 var post: BlogPost?44 45 init(text: String, authorName: String) {46 self.id = UUID()47 self.text = text48 self.authorName = authorName49 self.createdAt = Date()50 }51}Core Data eşdeğeri için 50+ satır xcdatamodeld XML veya NSManagedObject subclass gerekiyor. SwiftData burada açık avantajlı.
swift
1// Core Data eşdeğeri (kısaltılmış)2class BlogPostEntity: NSManagedObject {3 @NSManaged var title: String4 @NSManaged var slug: String5 @NSManaged var publishedAt: Date6 @NSManaged var viewCount: Int647 @NSManaged var id: UUID8 @NSManaged var category: String9 @NSManaged var comments: NSSet10 11 // Ayrıca: xcdatamodeld dosyası, entity tanımı,12 // relationship tanımı, fetch request static fonksiyonlar...13}ModelContainer ve ModelActor
swift
1// ModelContainer kurulumu2@main3struct BlogApp: App {4 let container: ModelContainer5 6 init() {7 do {8 let schema = Schema([BlogPost.self, Comment.self])9 let config = ModelConfiguration(10 schema: schema,11 isStoredInMemoryOnly: false,12 cloudKitDatabase: .automatic13 )14 container = try ModelContainer(for: schema, configurations: [config])15 } catch {16 fatalError("ModelContainer kurulamadı: (error)")17 }18 }19 20 var body: some Scene {21 WindowGroup {22 ContentView()23 }24 .modelContainer(container)25 }26}27 28// ModelActor — Swift 6 concurrency ile background işlemler29@ModelActor30actor BlogDataActor {31 // Background thread'de güvenli SwiftData erişimi32 33 func importPosts(from json: [PostJSON]) async throws {34 for postData in json {35 let post = BlogPost(36 title: postData.title,37 slug: postData.slug,38 category: postData.category39 )40 modelContext.insert(post)41 }42 try modelContext.save()43 }44 45 func fetchPostsByCategory(_ category: String) throws -> [BlogPost] {46 let descriptor = FetchDescriptor<BlogPost>(47 predicate: #Predicate { $0.category == category },48 sortBy: [SortDescriptor(.publishedAt, order: .reverse)]49 )50 return try modelContext.fetch(descriptor)51 }52}Core Data'da aynı pattern NSManagedObjectContext + performBlock + actor ile mümkün ama çok daha fazla boilerplate gerektiriyor.
@Query vs FetchRequest: Reactive Patterns
swift
1// SwiftData @Query — SwiftUI ile sıfır boilerplate2struct BlogListView: View {3 @Query(4 filter: #Predicate<BlogPost> { $0.viewCount > 100 },5 sort: .publishedAt,6 order: .reverse7 )8 private var popularPosts: [BlogPost]9 10 // iOS 26 yeni: pagination11 @Query(12 filter: #Predicate<BlogPost> { $0.category == "iOS" },13 sort: .publishedAt,14 fetchLimit: 20,15 fetchOffset: 016 )17 private var iosPosts: [BlogPost]18 19 var body: some View {20 List(popularPosts) { post in21 PostRowView(post: post)22 }23 }24}25 26// Dinamik predicate — iOS 18 ile geldi27struct FilteredBlogList: View {28 let selectedCategory: String29 30 var body: some View {31 FilteredListContent(category: selectedCategory)32 }33}34 35struct FilteredListContent: View {36 let category: String37 38 @Query private var posts: [BlogPost]39 40 init(category: String) {41 _posts = Query(42 filter: #Predicate<BlogPost> { $0.category == category },43 sort: .publishedAt,44 order: .reverse45 )46 }47 48 var body: some View {49 List(posts) { post in50 PostRowView(post: post)51 }52 }53}swift
1// Core Data FetchRequest eşdeğeri2struct CoreDataBlogList: View {3 @FetchRequest(4 sortDescriptors: [SortDescriptor(.publishedAt, order: .reverse)],5 predicate: NSPredicate(format: "viewCount > %d", 100)6 )7 private var popularPosts: FetchedResults<BlogPostEntity>8 9 var body: some View {10 List(popularPosts) { post in11 CoreDataPostRow(post: post)12 }13 }14}15 16// Dinamik predicate — NSPredicate string tabanlı17struct DynamicCoreDataList: View {18 let category: String19 20 @FetchRequest private var posts: FetchedResults<BlogPostEntity>21 22 init(category: String) {23 self.category = category24 _posts = FetchRequest(25 sortDescriptors: [NSSortDescriptor(key: "publishedAt", ascending: false)],26 predicate: NSPredicate(format: "category == %@", category)27 )28 }29 30 var body: some View {31 List(posts) { post in32 CoreDataPostRow(post: post)33 }34 }35}@Query açık kazanıyor: type-safe predicate, daha az boilerplate, Swift 6 uyumlu. FetchRequest ise NSPredicate string'leri nedeniyle runtime error riski taşıyor.
Predicate Performansı: Swift Native vs NSPredicate
swift
1// SwiftData — #Predicate macro (Swift native, compile-time check)2let swiftDataPredicate = #Predicate<BlogPost> {3 $0.category == "iOS" &&4 $0.viewCount > 500 &&5 $0.publishedAt > Calendar.current.date(byAdding: .month, value: -3, to: Date())!6}7 8// Core Data — NSPredicate (runtime string, no compile-time check)9let coreDataPredicate = NSPredicate(10 format: "category == %@ AND viewCount > %d AND publishedAt > %@",11 "iOS",12 500,13 Calendar.current.date(byAdding: .month, value: -3, to: Date())! as NSDate14)15 16// Karmaşık predicate — SwiftData sınırları17// SwiftData #Predicate bazı SQL fonksiyonlarını desteklemiyor:18// BEGINSWITH, CONTAINS, ENDSWITH, IN — hepsi destekli19// SUBQUERY, FUNCTION, AGGREGATE — desteklenmiyor20 21// Aggregate için Core Data zorunlu22let aggregatePredicate = NSPredicate(23 format: "comments.@count > %d", 1024)25// SwiftData'da eşdeğer yok — workaround gerekiyorPredicate karmaşıklığı arttıkça Core Data daha fazla esneklik sunuyor. Basit filter operasyonlarında SwiftData type safety ile üstün.
100K Record Benchmark Sonuçları
Test cihazı: iPhone 16 Pro, iOS 26, 100.000 BlogPost kaydı.
Fetch (Tam Liste, No Filter)
Framework | Süre | Memory |
|---|---|---|
SwiftData | 1.2s | 85MB |
Core Data | 0.9s | 72MB |
Fetch (Category Filter, 5K sonuç)
Framework | Süre | Memory |
|---|---|---|
SwiftData | 0.18s | 12MB |
Core Data | 0.14s | 10MB |
Fetch (Paginated, 20 sonuç)
Framework | Süre | Memory |
|---|---|---|
SwiftData | 0.008s | 2MB |
Core Data | 0.006s | 1.8MB |
Insert (1.000 kayıt, transaction)
Framework | Süre |
|---|---|
SwiftData | 0.45s |
Core Data | 0.38s |
Delete (500 kayıt, predicate tabanlı)
Framework | Süre |
|---|---|
SwiftData | 0.32s |
Core Data | 0.08s (batch delete) |
Core Data küçük ama tutarlı bir avantajla öne geçiyor. Fark küçük sayılarda ihmal edilebilir ama 100K+ kayıtta anlamlı hale geliyor.
Batch Insert Performansı
Bu bölüm en kritik fark:
swift
1// SwiftData — batch insert yok, kayıt kayıt insert2func swiftDataBatchInsert(count: Int) async throws {3 let actor = BlogDataActor(modelContainer: container)4 try await actor.importPosts(from: generateTestData(count: count))5 // 100K kayıt için: ~8 saniye6}7 8// Core Data — NSBatchInsertRequest ile9func coreDataBatchInsert(count: Int) throws {10 let context = container.newBackgroundContext()11 try context.performAndWait {12 var objects: [[String: Any]] = []13 for i in 0..<count {14 objects.append([15 "id": UUID(),16 "title": "Post (i)",17 "slug": "post-(i)",18 "category": "iOS",19 "publishedAt": Date(),20 "viewCount": 021 ])22 }23 24 let insertRequest = NSBatchInsertRequest(25 entityName: "BlogPostEntity",26 objects: objects27 )28 insertRequest.resultType = .statusOnly29 30 let result = try context.execute(insertRequest) as? NSBatchInsertResult31 guard result?.result as? Bool == true else {32 throw PersistenceError.batchInsertFailed33 }34 }35 // 100K kayıt için: ~6 saniye36}Sonuç:
- SwiftData 100K insert: ~8.1 saniye
- Core Data batch insert: ~5.9 saniye
- Core Data %27 daha hızlı
Büyük veri import'u gerektiren uygulamalarda bu fark önemli. SwiftData'da NSBatchInsertRequest eşdeğeri henüz yok.
Memory Footprint Analizi
SwiftData, faulting mechanism'ı Core Data'dan farklı yönetiyor:
swift
1// Core Data lazy faulting — ilişkiler erişilene kadar yüklenmez2let posts = try context.fetch(BlogPostEntity.fetchRequest())3// comments ilişkisi henüz yüklü değil (fault)4// posts[0].comments ile erişilince yükleniyor5 6// SwiftData da lazy yükleme yapıyor ama daha az belirgin7let descriptor = FetchDescriptor<BlogPost>()8let posts = try context.fetch(descriptor)9// comments — lazy yüklü10// Ancak fetch overhead Core Data'dan biraz yüksek11 12// Bellek optimizasyonu — her iki framework13// Core Data: fetchBatchSize14fetchRequest.fetchBatchSize = 5015 16// SwiftData: FetchDescriptor limit + offset17var descriptor = FetchDescriptor<BlogPost>()18descriptor.fetchLimit = 5019descriptor.fetchOffset = page * 50Uzun süreli oturumda (30+ dakika aktif kullanım, 10K+ kayıt):
- Core Data: ~45MB baseline
- SwiftData: ~52MB baseline
Fark küçük ama memory-constrained ortamlarda (Watch app, widget) Core Data avantajlı.
Migration: SwiftData vs Core Data
SwiftData Migration
swift
1// Lightweight migration — otomatik (schema versioning)2enum BlogPostSchemaV1: VersionedSchema {3 static var versionIdentifier = Schema.Version(1, 0, 0)4 static var models: [any PersistentModel.Type] { [BlogPost.self] }5 6 @Model7 final class BlogPost {8 var title: String9 var slug: String10 // V1 schema11 init(title: String, slug: String) {12 self.title = title13 self.slug = slug14 }15 }16}17 18enum BlogPostSchemaV2: VersionedSchema {19 static var versionIdentifier = Schema.Version(2, 0, 0)20 static var models: [any PersistentModel.Type] { [BlogPost.self] }21 22 @Model23 final class BlogPost {24 var title: String25 var slug: String26 var viewCount: Int // Yeni alan — default değer ile lightweight27 init(title: String, slug: String) {28 self.title = title29 self.slug = slug30 self.viewCount = 031 }32 }33}34 35// Migration plan36enum BlogPostMigrationPlan: SchemaMigrationPlan {37 static var schemas: [any VersionedSchema.Type] {38 [BlogPostSchemaV1.self, BlogPostSchemaV2.self]39 }40 41 static var stages: [MigrationStage] {42 [migrateV1toV2]43 }44 45 // Lightweight: sadece schema belirt46 static let migrateV1toV2 = MigrationStage.lightweight(47 fromVersion: BlogPostSchemaV1.self,48 toVersion: BlogPostSchemaV2.self49 )50}51 52// Custom migration gerekiyorsa53static let migrateV1toV2 = MigrationStage.custom(54 fromVersion: BlogPostSchemaV1.self,55 toVersion: BlogPostSchemaV2.self,56 willMigrate: { context in57 // Migration öncesi işlem58 },59 didMigrate: { context in60 // Migration sonrası: viewCount değerlerini hesapla61 let posts = try context.fetch(FetchDescriptor<BlogPostSchemaV2.BlogPost>())62 for post in posts {63 post.viewCount = fetchViewCountFromAnalytics(slug: post.slug)64 }65 try context.save()66 }67)Core Data Migration
swift
1// Core Data lightweight migration — mapping model ile2let options: [String: Any] = [3 NSMigratePersistentStoresAutomaticallyOption: true,4 NSInferMappingModelAutomaticallyOption: true5]6 7let container = NSPersistentContainer(name: "BlogModel")8container.persistentStoreDescriptions.first?.setOption(9 true as NSNumber,10 forKey: NSMigratePersistentStoresAutomaticallyOption11)12container.persistentStoreDescriptions.first?.setOption(13 true as NSNumber,14 forKey: NSInferMappingModelAutomaticallyOption15)16 17// Custom migration — daha fazla kontrol18class BlogV1toV2MigrationPolicy: NSEntityMigrationPolicy {19 override func createDestinationInstances(20 forSource sInstance: NSManagedObject,21 in mapping: NSEntityMapping,22 manager: NSMigrationManager23 ) throws {24 try super.createDestinationInstances(25 forSource: sInstance,26 in: mapping,27 manager: manager28 )29 30 guard let destination = manager.destinationInstances(31 forEntityMappingName: mapping.name,32 sourceInstances: [sInstance]33 ).first else { return }34 35 // Custom transformation36 destination.setValue(0, forKey: "viewCount")37 }38}Migration karmaşıklığı karşılaştırması:
Senaryo | SwiftData | Core Data |
|---|---|---|
Alan ekleme (default) | Otomatik | Otomatik |
Alan silme | Otomatik | Otomatik |
Alan yeniden adlandırma | VersionedSchema gerekli | Mapping model |
Type değişimi | Custom stage zorunlu | Custom policy |
İlişki değişimi | Custom stage zorunlu | Mapping model |
Multi-step migration | Destekli (chain) | Destekli (chain) |
Core Data migration için daha olgun tooling (Xcode migration assistant, mapping model editor). SwiftData migration API daha temiz ama karmaşık senaryolarda henüz edge case'ler var.
CloudKit Sync Karşılaştırması
swift
1// SwiftData CloudKit — ModelConfiguration ile2let config = ModelConfiguration(3 schema: schema,4 cloudKitDatabase: .automatic // iCloud container otomatik algılanır5)6 7// Conflict resolution — SwiftData otomatik yönetiyor8// Last-write-wins (varsayılan) veya custom resolver9 10// Core Data CloudKit11let description = NSPersistentStoreDescription()12description.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(13 containerIdentifier: "iCloud.com.yourapp.blog"14)15description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)16description.setOption(true as NSNumber,17 forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)18 19container.persistentStoreDescriptions = [description]CloudKit sync konusunda iki framework neredeyse eşdeğer — aynı NSPersistentCloudKitContainer backend'ini kullanıyorlar. SwiftData kurulumu daha az boilerplate.
iCloud sharing (shared database, multi-user) hâlâ Core Data'da daha stabil — SwiftData'da iOS 26 ile iyileştirildi ama Core Data daha olgun.
Background Context Patterns
swift
1// SwiftData — ModelActor ile clean background2@ModelActor3actor BackgroundImporter {4 func processLargeDataset(_ items: [ItemData]) async throws -> Int {5 var insertedCount = 06 7 // Batch'ler halinde işle — memory kontrolü için8 let batchSize = 5009 for batch in items.chunked(into: batchSize) {10 for item in batch {11 let model = BlogPost(12 title: item.title,13 slug: item.slug,14 category: item.category15 )16 modelContext.insert(model)17 insertedCount += 118 }19 try modelContext.save()20 // Her batch sonrası save — memory'i flush et21 }22 23 return insertedCount24 }25}26 27// Core Data — performBackgroundTask28func coreDataBackgroundImport(_ items: [ItemData]) {29 container.performBackgroundTask { context in30 context.automaticallyMergesChangesFromParent = true31 32 for item in items {33 let post = BlogPostEntity(context: context)34 post.id = UUID()35 post.title = item.title36 post.slug = item.slug37 post.category = item.category38 post.publishedAt = Date()39 }40 41 do {42 try context.save()43 } catch {44 // Error handling45 context.rollback()46 }47 }48}SwiftData ModelActor Swift 6 concurrency ile mükemmel entegre. Core Data performBackgroundTask ise olgun ve battle-tested.
In-Memory Store ile Test Coverage
swift
1// SwiftData test setup2import XCTest3import SwiftData4 5@MainActor6final class BlogRepositoryTests: XCTestCase {7 var container: ModelContainer!8 var context: ModelContext!9 10 override func setUp() async throws {11 // In-memory store — disk yazmaz, her test izole12 let config = ModelConfiguration(isStoredInMemoryOnly: true)13 container = try ModelContainer(14 for: BlogPost.self, Comment.self,15 configurations: config16 )17 context = ModelContext(container)18 }19 20 override func tearDown() async throws {21 container = nil22 context = nil23 }24 25 func testFetchByCategory() throws {26 // Arrange27 let iosPost = BlogPost(title: "Swift Guide", slug: "swift", category: "iOS")28 let designPost = BlogPost(title: "Design Tips", slug: "design", category: "Design")29 context.insert(iosPost)30 context.insert(designPost)31 try context.save()32 33 // Act34 let descriptor = FetchDescriptor<BlogPost>(35 predicate: #Predicate { $0.category == "iOS" }36 )37 let results = try context.fetch(descriptor)38 39 // Assert40 XCTAssertEqual(results.count, 1)41 XCTAssertEqual(results.first?.title, "Swift Guide")42 }43 44 func testDeleteCascade() throws {45 let post = BlogPost(title: "Test Post", slug: "test", category: "iOS")46 let comment = Comment(text: "Test comment", authorName: "User")47 post.comments.append(comment)48 context.insert(post)49 try context.save()50 51 // Delete post — cascade should delete comment52 context.delete(post)53 try context.save()54 55 let comments = try context.fetch(FetchDescriptor<Comment>())56 XCTAssertTrue(comments.isEmpty, "Comments should be cascade deleted")57 }58}SwiftData in-memory test kurulumu Core Data'ya göre çok daha sade. Core Data eşdeğeri için NSInMemoryStoreType + NSPersistentContainer mock'u gerekiyor.
swift
1// Core Data store üzerinden SwiftData model kullanımı2// (Aynı SQLite dosyası)3 4// 1. Core Data container'ı oluştur5let coreDataContainer = NSPersistentContainer(name: "BlogModel")6coreDataContainer.loadPersistentStores { _, error in7 if let error { fatalError("Core Data yüklenemedi: (error)") }8}9 10// 2. SwiftData ModelContainer'ı aynı store URL ile11let storeURL = coreDataContainer.persistentStoreDescriptions.first?.url12 13let swiftDataConfig = ModelConfiguration(14 url: storeURL ?? URL.documentsDirectory.appending(path: "BlogModel.sqlite")15)16 17let swiftDataContainer = try ModelContainer(18 for: BlogPost.self,19 configurations: swiftDataConfig20)21 22// 3. Core Data context için batch operations23// SwiftData context için reactive UI24 25// Bu pattern şunlar için ideal:26// - Core Data ile mevcut bir projeyi SwiftData'ya kademeli migrate etmek27// - Batch insert için Core Data, UI binding için SwiftData28// - Legacy code Core Data, yeni feature SwiftDataALTIN İPUCU
Bu yazının en değerli bilgisi
Bu ipucu, yazının en önemli çıkarımını içeriyor.
swift
1// Instruments'ta "SwiftData" template'ini aktifleştirmek için:2// Product → Profile → SwiftData3// (iOS 26 Simulator veya cihaz gerekli)4 5// Debug build'de SwiftData query log'larını görmek için:6// Environment variable: SWIFTDATA_DEBUG_QUERIES=17// Xcode scheme → Run → Environment Variables8 9// Şunları gösteriyor:10// - Her @Query için SQL query ve süre11// - ModelContext save count12// - Fault trigger count13// - CloudKit sync eventsEaster Egg
Gizli bir bilgi buldun!
Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?
swift
1// Genel amaçlı SwiftData fetch helper2struct DataStore {3 let context: ModelContext4 5 // Paginated fetch6 func fetchPaginated<T: PersistentModel>(7 _ type: T.Type,8 predicate: Predicate<T>? = nil,9 sortBy: [SortDescriptor<T>] = [],10 page: Int,11 pageSize: Int = 2012 ) throws -> [T] {13 var descriptor = FetchDescriptor<T>(14 predicate: predicate,15 sortBy: sortBy16 )17 descriptor.fetchLimit = pageSize18 descriptor.fetchOffset = page * pageSize19 return try context.fetch(descriptor)20 }21 22 // Count query (tüm kayıtları çekmeden)23 func count<T: PersistentModel>(24 _ type: T.Type,25 predicate: Predicate<T>? = nil26 ) throws -> Int {27 var descriptor = FetchDescriptor<T>(predicate: predicate)28 descriptor.includePendingChanges = false29 return try context.fetchCount(descriptor)30 }31 32 // Upsert pattern33 func upsert<T: PersistentModel>(34 _ object: T,35 uniqueBy keyPath: KeyPath<T, some Equatable>36 ) throws {37 // SwiftData otomatik upsert yapmıyor — manual kontrol38 // @Attribute(.unique) ile duplicate prevention tercih edin39 context.insert(object)40 try context.save()41 }42}43 44// Kullanım45let store = DataStore(context: modelContext)46let pageOneIosPosts = try store.fetchPaginated(47 BlogPost.self,48 predicate: #Predicate { $0.category == "iOS" },49 sortBy: [SortDescriptor(.publishedAt, order: .reverse)],50 page: 051)52let totalCount = try store.count(BlogPost.self)Okuyucu Ödülü
Hangisini Seçmeli?
Kriter | SwiftData | Core Data |
|---|---|---|
Yeni proje (iOS 17+) | Tercih et | Gerek yok |
Mevcut Core Data projesi | Kademeli migrate | Devam et |
100K+ kayıt batch import | - | Tercih et |
Complex NSPredicate | - | Tercih et |
Swift 6 concurrency | Tercih et | Çalışır |
SwiftUI binding | Tercih et | FetchRequest |
CloudKit sync | Eşdeğer | Eşdeğer |
iCloud sharing | İyileştiriliyor | Tercih et |
watchOS/widget | Her ikisi | Her ikisi |
Test kolaylığı | Tercih et | Verbose |
Karar ağacı:
Yeni iOS projesi, iOS 17+ minimum → SwiftData.
100K+ batch import kritik → Core Data veya hybrid.
Mevcut Core Data projesi, büyük ve stabil → devam et, kademeli migrate değer.
Complex aggregate query, SUBQUERY → Core Data.
Swift 6, actor-based architecture → SwiftData ModelActor.
Sonuç
SwiftData 2026'da production-ready. Temiz API, type-safe predicate, Swift concurrency entegrasyonu, kolay test setup — yeni projeler için açık tercih. Core Data ise 20+ yıllık olgunluğu, batch operations avantajı ve karmaşık query esnekliğiyle güçlü kalmaya devam ediyor.
İki framework aynı SQLite backend'ini paylaştığı için hybrid yaklaşım gerçek bir seçenek — yeni UI özellikleri için SwiftData, ağır veri işlemleri için Core Data.
Daha fazlası için: SwiftData Complete Guide, Core Data Advanced Techniques, iOS 26 Yenilikleri, Swift 6 Neler Yeni.
Harici kaynaklar:

