Core Data — framework Apple do zarządzania grafem obiektów w aplikacjach iOS i macOS. Zapewnia trwałość danych, śledzenie zmian, cofanie operacji i integrację z UI poprzez NSFetchedResultsController. Według dokumentacji Apple Developer (2025), Core Data nie jest bazą danych — to warstwa modelowania obiektowego, która domyślnie używa SQLite jako trwałego magazynu do ładowania i zapisywania obiektów.
Najważniejsze
Core Data — to framework zarządzania grafem obiektów i trwałością danych, wchodzący w skład Cocoa Touch SDK od Apple. Zapewnia obiektowo zorientowany interfejs do pracy z danymi: programista operuje na encjach (Entity), atrybutach i relacjach (Relationship), a Core Data przekształca te obiekty na rekordy relacyjnej bazy danych pod maską.
Core Data został przedstawiony w Mac OS X 10.4 Tiger (2005) dla macOS i przeniesiony do iOS 3.0 (2009). Przez ponad 20 lat framework ewoluował od prostej warstwy abstrakcji nad SQLite do pełnego stosu z obsługą synchronizacji chmurowej poprzez NSPersistentCloudKitContainer, wielowątkowości przez automatyczne zarządzanie kontekstami i asynchronicznego ładowania przez Swift Concurrency.
Według ankiety deweloperów iOS z Slack Community (2025), Core Data jest używany w 68% komercyjnych aplikacji iOS do lokalnego przechowywania danych. Pomimo krytyki za złożoność i wielowarstwowość, framework pozostaje standardem dla aplikacji Apple dzięki ścisłej integracji z systemem, zerowemu kosztowi (wbudowany w SDK) i obsłudze synchronizacji iCloud.
Powszechne błędne przekonanie — uważać Core Data za bazę danych. Framework nie wykonuje zapytań SQL bezpośrednio i nie jest systemem zarządzania bazą danych. Core Data to warstwa zarządzania obiektami (object graph management), która może używać SQLite, Binary lub In-Memory jako magazynu trwałego. Analogia: Core Data to ORM jak Hibernate lub Entity Framework, ale dla ekosystemu Apple, a SQLite pod nim — jak MySQL pod Hibernate.
Stos Core Data składa się z czterech wzajemnie połączonych komponentów: NSManagedObjectModel (schemat danych), NSPersistentStoreCoordinator (koordynator magazynów), NSManagedObjectContext (kontekst roboczy) i NSPersistentContainer (jednolity kontener łączący wszystkie trzy od iOS 10). NSPersistentContainer automatyzuje tworzenie i konfigurację stosu.
Każdy komponent pełni ściśle określoną funkcję. NSManagedObjectModel ładuje plik .xcdatamodeld z opisem encji. NSPersistentStoreCoordinator łączy model z fizycznym plikiem magazynu (SQLite). NSManagedObjectContext zapewnia tymczasowy obszar do pracy z obiektami. Container łączy wszystko w jednym wywołaniu inicjalizacyjnym.
SQLite (NSSQLiteStoreType) — standardowy magazyn używany w większości aplikacji. Dane są zapisywane w jednym pliku .sqlite z obsługą transakcji ACID. Binary (NSBinaryStoreType) — magazyn w formacie binarnym dla małych zbiorów danych (do kilkuset obiektów). In-Memory (NSInMemoryStoreType) — tymczasowy magazyn w pamięci RAM bez zapisu na dysk, używany do testów i cache.
| Typ magazynu | Format | Wydajność | Kiedy używać |
|---|---|---|---|
| SQLite | .sqlite | Wysoka | Standardowy wybór dla production |
| Binary | .binary | Średnia | Małe zbiory danych |
| In-Memory | RAM | Maksymalna | Testy, cache, dane tymczasowe |
| CloudKit | iCloud | Zależy od sieci | Synchronizacja między urządzeniami |
Wybór magazynu ustawia się pojedynczą linią przy inicjalizacji NSPersistentStoreDescription. Programista może przełączyć magazyn z SQLite na In-Memory dla testów jednostkowych lub na CloudKit dla synchronizacji iCloud bez zmiany kodu pracy z obiektami — Core Data abstrahuje różnicę między typami magazynów za pomocą jednolitego API kontekstu.
NSManagedObject — klasa bazowa dla wszystkich obiektów Core Data, reprezentująca jeden rekord encji. Każdy managed object ma unikalny NSManagedObjectID (stały identyfikator), jest powiązany z kontekstem i śledzi swoje zmiany przez KVO (Key-Value Observing). Programista tworzy podklasy NSManagedObject do definiowania właściwości z typowaniem encji.
NSManagedObjectContext — centralny komponent Core Data, zapewniający obszar roboczy dla wszystkich operacji na obiektach. Kontekst śledzi dodawanie, usuwanie i modyfikację obiektów (change tracking), obsługuje cofanie operacji przez undoManager i automatycznie scala zmiany z innych kontekstów po otrzymaniu powiadomień o zapisie.
Zasada prywatnych kolejek: NSManagedObjectContext jest tworzony z typem .privateQueueConcurrencyType lub .mainQueueConcurrencyType. Kontekst główny jest powiązany z głównym wątkiem UI, prywatne konteksty działają w tle. Każdy kontekst może być używany tylko w swojej kolejce — dostęp do managed object z innego wątku powoduje crash. parentContext pozwala organizować hierarchię kontekstów dla asynchronicznego zapisu.
struct CoreDataStack {
let container: NSPersistentContainer
init(name: String) {
container = NSPersistentContainer(name: name)
container.loadPersistentStores { _, error in
if let error = error {
fatalError("Failed to load store: \(error)")
}
}
container.viewContext.automaticallyMergesChangesFromParent = true
}
func backgroundContext() -> NSManagedObjectContext {
let context = container.newBackgroundContext()
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
return context
}
}
NSPersistentContainer automatycznie tworzy viewContext (main queue) i udostępnia newBackgroundContext() dla operacji w tle. Właściwość automaticallyMergesChangesFromParent = true powoduje, że viewContext automatycznie przechwytuje zmiany z kontekstów tła po ich zapisie, aktualizując UI bez ręcznego ponownego pobierania danych.
NSPersistentStoreCoordinator zarządza fizycznym magazynem danych: otwiera plik, tworzy tabele SQLite na podstawie modelu, wykonuje migracje przy zmianie schematu. Przy inicjalizacji NSPersistentStoreDescription z typem NSSQLiteStoreType Core Data tworzy plik SQLite ze schematem odpowiadającym modelowi .xcdatamodeld.
Core Data nie używa standardowych zapytań SQL przez SELECT/INSERT/UPDATE. Zamiast tego generuje wewnętrzne polecenia SQL na podstawie modelu i zapytań przez NSFetchRequest. Programista może włączyć logowanie SQL przez argument uruchomienia -com.apple.CoreData.SQLDebug 1 do debugowania wydajności zapytań.
Lekka migracja (Lightweight Migration) — automatyczny proces aktualizacji schematu SQLite przy dodawaniu nowych atrybutów, zmianie optional/required lub zmianie nazwy za pomocą renamingID. Ciężka migracja jest wymagana przy radykalnych zmianach schematu, takich jak łączenie lub dzielenie encji, i jest wykonywana przez niestandardowy NSMigrationManager.
let description = NSPersistentStoreDescription()
description.url = FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)
.first?
.appendingPathComponent("Model.sqlite")
description.setOption(true as NSNumber,
forKey: NSMigratePersistentStoresAutomaticallyOption)
description.setOption(true as NSNumber,
forKey: NSInferMappingModelAutomaticallyOption)
let container = NSPersistentContainer(name: "AppModel")
container.persistentStoreDescriptions = [description]
container.loadPersistentStores { _, error in
if let error = error { print("Migration error: \(error)") }
}
Konfiguracja automatycznej migracji przez NSMigratePersistentStoresAutomaticallyOption i NSInferMappingModelAutomaticallyOption pozwala Core Data samodzielnie aktualizować plik SQLite przy dodawaniu atrybutów lub encji w nowej wersji modelu. Jeśli migracja jest niemożliwa, store coordinator zgłasza błąd z opisem przyczyny — programista musi zaimplementować niestandardową migrację przez NSMigrationManager.
NSFetchRequest — podstawowe narzędzie do pobierania obiektów z Core Data. Zapytanie zawiera nazwę encji, predykat (filtr), sortowania, limit i przesunięcie (offset). Wynik jest zwracany jako tablica NSManagedObject lub typowanych podklas. NSPredicate obsługuje złożone warunki z AND, OR, IN, LIKE i podzapytaniami.
NSBatchDeleteRequest — efektywny sposób masowego usuwania obiektów bez ładowania każdego do pamięci. Zapytanie jest wykonywane na poziomie SQLite, z pominięciem managed object context, i tylko aktualizuje kontekst po zakończeniu. Podobne zapytania zbiorcze istnieją do aktualizacji (NSBatchUpdateRequest) i wstawiania (NSBatchInsertRequest).
CRUD (Create, Read, Update, Delete) w Core Data jest wykonywany przez metody kontekstu: insert, fetch, save i delete. Wszystkie zmiany są tymczasowe do wywołania context.save() — metoda zapisuje zmiany w trwałym magazynie SQLite. Przy błędzie zapisu kontekst pozostaje w zmienionym stanie do ponownej próby.
let context = container.viewContext
// Utwórz
let user = User(context: context)
user.id = 42
user.name = "Alice"
// Odczytaj
let request = User.fetchRequest()
request.predicate = NSPredicate(format: "name CONTAINS %@", "Ali")
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
let results = try context.fetch(request)
// Aktualizuj
results.first?.name = "Alice Updated"
// Usuń
if let first = results.first { context.delete(first) }
// Zapisz
try context.save()
Zapis kontekstu (context.save()) — krytyczna operacja. Jeśli zapis nie zostanie wywołany, wszystkie zmiany pozostają tylko w pamięci. Kontekst śledzi stan hasChanges, który można sprawdzić przed zapisem. Dla operacji w tle używaj newBackgroundContext z własnym zapisem, a dla UI — viewContext z automatycznym zapisem po timerze lub przy przejściu aplikacji w tło.
Pierwsza praktyka — używaj NSPersistentCloudKitContainer do synchronizacji danych między urządzeniami użytkownika przez iCloud. Synchronizacja w chmurze jest włączana przez dodanie opcji CloudKit do opisu magazynu. Core Data automatycznie zarządza konfliktami podczas synchronizacji i scala zmiany z innych urządzeń.
Druga praktyka — unikaj fetchRequest bez predykatu na dużych tabelach. Każde bezwarunkowe pobranie ładuje wszystkie obiekty encji do pamięci, co prowadzi do wysokiego zużycia RAM i spowolnienia UI. Zawsze używaj predykatów i limitów. Do paginacji stosuj fetchLimit i fetchOffset w NSFetchRequest.
Trzecia praktyka — skonfiguruj mergePolicy do rozwiązywania konfliktów przy dostępie wielowątkowym. NSMergeByPropertyObjectTrumpMergePolicy aktualizuje konfliktowe właściwości z ostatniego zapisanego kontekstu. NSRollbackMergePolicy anuluje zmiany bieżącego kontekstu przy konflikcie. Wybór polityki zależy od logiki biznesowej aplikacji.
Czwarta praktyka — używaj NSFetchedResultsController do integracji z tabelami i kolekcjami. Automatycznie subskrybuje powiadomienia NSManagedObjectContextDidSave, ładuje tylko niezbędne obiekty (faulting) i powiadamia delegata o wstawieniach, usunięciach i przenosinach z odpowiednimi ścieżkami indeksów do animacji UITableView.
Faulting — mechanizm leniwego ładowania obiektów Core Data. Managed object zwrócony przez zapytanie fetch znajduje się w stanie fault — jego atrybuty nie są w pełni załadowane, tylko identyfikator. Pełne ładowanie (fire fault) następuje przy pierwszym dostępie do dowolnego atrybutu. Relationship prefetching (setRelationshipKeyPathsForPrefetching) ładuje powiązane obiekty z wyprzedzeniem, unikając zapytań N+1.
extension UserRepository {
func fetchUsersWithPosts() throws -> [User] {
let request = User.fetchRequest()
request.predicate = NSPredicate(format: "isActive == YES")
request.relationshipKeyPathsForPrefetching = ["posts"]
request.returnsObjectsAsFaults = false
request.fetchBatchSize = 20
let context = container.viewContext
return try context.fetch(request)
}
}
fetchBatchSize = 20 zmusza Core Data do ładowania danych porcjami po 20 obiektów (do wyświetlenia na ekranie), bez ładowania całej tabeli naraz. Flaga returnsObjectsAsFaults = false gwarantuje, że atrybuty wszystkich użytkowników są załadowane od razu, co jest przydatne przy bezpośrednim wyświetlaniu. Prefetching relacji "posts" unika oddzielnych zapytań dla każdego użytkownika przy dostępie do postów.
Często zadawane pytania
Nie, Core Data to framework zarządzania grafem obiektów. Udostępnia API do pracy z obiektami, śledzenia zmian i ich zapisywania. Bazy danych pod maską Core Data (domyślnie SQLite) nie należy mylić z samym frameworkiem. Core Data to ORM, a nie system zarządzania bazą danych.
Tak, Core Data obsługuje trzy typy magazynów: SQLite, Binary i In-Memory. Wybór magazynu ustawia się przez NSPersistentStoreDescription. Magazyn In-Memory nie zapisuje danych na dysk i nadaje się do testów jednostkowych. Magazyn Binary to przestarzały format dla kompaktowych zbiorów obiektów.
Dla lekkiej migracji włącz NSMigratePersistentStoresAutomaticallyOption i NSInferMappingModelAutomaticallyOption. Dla złożonych zmian utwórz Mapping Model (.xcmappingmodel) przez Xcode. Magazyn CloudKit (NSPersistentCloudKitContainer) obsługuje migracje automatycznie przy synchronizacji schematu z serwerem iCloud.
SwiftData — nowy framework Apple (iOS 17+), zbudowany na bazie Core Data z użyciem Swift Macros i Swift Concurrency. SwiftData jest prostszy w składni: encje opisuje się makrem @Model, kontekst — @Environment(\.modelContext). Pod maską SwiftData używa tego samego stosu Core Data i SQLite.
Włącz argument uruchomienia -com.apple.CoreData.SQLDebug 1 — Core Data będzie wyświetlać wszystkie zapytania SQL i ich czas w konsoli Xcode. Do profilowania używaj Instruments z szablonem Core Data, który pokazuje liczbę zapytań fault, ładowanie obiektów i czas zapisu kontekstu.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również