Section 2/181 menit
2. Masalah yang Dipecahkan
2. Masalah yang Dipecahkan
Boilerplate Core Data yang Masif
Core Data mengharuskan developer mendefinisikan model di dua tempat: di .xcdatamodeld editor dan di kode Swift sebagai NSManagedObject subclass. Perubahan sekecil apapun di model harus disinkronkan manual di keduanya.
swift
// ❌ TANPA SwiftData: definisi model Core Data yang verbose
import CoreData
@objc(Task)
class Task: NSManagedObject {
@NSManaged var id: UUID
@NSManaged var title: String
@NSManaged var isCompleted: Bool
@NSManaged var createdAt: Date
@NSManaged var priority: Int16
}
extension Task {
@nonobjc class func fetchRequest() -> NSFetchRequest<Task> {
return NSFetchRequest<Task>(entityName: "Task")
}
}
// Setup stack di AppDelegate
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "MyApp")
container.loadPersistentStores { _, error in
if let error { fatalError("Core Data error: \(error)") }
}
return container
}()
// Fetch dengan NSFetchRequest
func fetchTasks() -> [Task] {
let request = Task.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: false)]
request.predicate = NSPredicate(format: "isCompleted == %@", false as CVarArg)
return (try? context.fetch(request)) ?? []
}
swift
// ✓ DENGAN SwiftData: satu file, satu sumber kebenaran
import SwiftData
@Model
final class Task {
var id: UUID
var title: String
var isCompleted: Bool
var createdAt: Date
var priority: Int
init(title: String, priority: Int = 0) {
self.id = UUID()
self.title = title
self.isCompleted = false
self.createdAt = Date()
self.priority = priority
}
}
// Fetch type-safe di SwiftUI
@Query(sort: \Task.createdAt, order: .reverse)
private var tasks: [Task]
Predicate yang Tidak Type-Safe
NSPredicate menggunakan format string — error tidak terdeteksi sampai runtime.
swift
// ❌ TANPA SwiftData: runtime crash jika ada typo di format string
let predicate = NSPredicate(format: "titlee == %@", searchText) // typo, crash saat runtime
request.predicate = predicate
// ❌ Tidak ada autocomplete, tidak ada compile-time check
let predicate2 = NSPredicate(format: "priority > %d AND isComplted == YES", 2)
// ^ typo tidak terdeteksi compiler
swift
// ✓ DENGAN SwiftData: #Predicate ter-compile dan type-safe
let searchPredicate = #Predicate<Task> { task in
task.title.contains(searchText) // compiler langsung tahu 'title' adalah String
&& task.priority > 2 // compiler tahu 'priority' adalah Int
&& !task.isCompleted // compiler tahu 'isCompleted' adalah Bool
}
// Typo di nama property → compile error, bukan runtime crash
Thread Safety yang Rumit
Core Data mengharuskan developer mengelola thread secara manual — NSManagedObjectContext tidak boleh diakses dari thread yang salah.
swift
// ❌ TANPA SwiftData: rawan data race
func processTasksBackground() {
let context = persistentContainer.viewContext // viewContext: main thread only!
DispatchQueue.global().async {
// ⚠️ BAHAYA: akses context dari background thread
let tasks = try? context.fetch(Task.fetchRequest())
tasks?.forEach { $0.isCompleted = true }
try? context.save() // crash atau data corruption
}
}
swift
// ✓ DENGAN SwiftData + @ModelActor: isolation jelas, Swift concurrency-safe
@ModelActor
actor TaskProcessor {
func markAllCompleted() async throws {
let descriptor = FetchDescriptor<Task>(predicate: #Predicate { !$0.isCompleted })
let tasks = try modelContext.fetch(descriptor)
tasks.forEach { $0.isCompleted = true }
try modelContext.save()
}
}