Core Data — kluczowe pojęcia, NSManagedObject i architektura

Autor: IT Sectr Opublikowano: 2026-03-11 Czas czytania: 11 min

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 — framework ORM dla iOS/macOS, zarządzający grafem obiektów i ich zapisem w trwałym magazynie.
  • NSManagedObjectModel — schemat danych opisujący encje, atrybuty i relacje między obiektami w modelu Core Data.
  • NSManagedObjectContext — obszar roboczy do tworzenia, odczytu, aktualizacji i usuwania obiektów ze śledzeniem zmian.
  • NSPersistentContainer — jednolity punkt wejścia, hermetyzujący model, kontekst i magazyn Core Data w iOS 10+.
  • NSFetchedResultsController — klasa do integracji Core Data z UITableView/UICollectionView z automatyczną aktualizacją przy zmianach.

Czym jest Core Data?

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.

Core Data to nie baza danych

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.

Architektura Core Data: stosy i komponenty

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.

Typy magazynów Core Data

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 magazynuFormatWydajnośćKiedy używać
SQLite.sqliteWysokaStandardowy wybór dla production
Binary.binaryŚredniaMałe zbiory danych
In-MemoryRAMMaksymalnaTesty, cache, dane tymczasowe
CloudKitiCloudZależy od sieciSynchronizacja 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 i NSManagedObjectContext

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.

Bezpieczeństwo wątkowe kontekstów

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.

swift
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.

Persistent Store i związek z SQLite

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ń.

Migracje Core Data

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.

swift
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.

Core Data w praktyce: kod i przykłady

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).

Przykład operacji CRUD

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.

swift
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.

Najlepsze praktyki Core Data

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.

Wydajność: prefetching i faulting

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.

swift
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

Core Data — czy to baza danych?

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.

Czy można używać Core Data bez SQLite?

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.

Jak przenieść Core Data do nowej wersji modelu?

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.

Czym Core Data różni się od SwiftData?

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.

Jak debugować wolne zapytania Core Data?

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

  • Core Data — framework zarządzania grafem obiektów i trwałością danych dla iOS i macOS, używający SQLite jako standardowego magazynu.
  • Stos Core Data obejmuje NSManagedObjectModel, NSPersistentStoreCoordinator, NSManagedObjectContext i NSPersistentContainer dla jednolitej konfiguracji.
  • NSManagedObjectContext — obszar roboczy ze śledzeniem zmian, obsługą cofania i automatycznym scalaniem z kontekstów tła.
  • NSFetchRequest z NSPredicate i prefetching relacji — podstawowe narzędzie pobierania z optymalizacją przez batch size i faulting.
  • Lightweight Migration automatycznie aktualizuje schemat SQLite przy dodawaniu atrybutów i encji Core Data w nowej wersji modelu.
  • NSPersistentCloudKitContainer dodaje synchronizację iCloud między urządzeniami użytkownika z automatycznym rozwiązywaniem konfliktów.
  • Zalecenie — używaj Core Data dla aplikacji iOS z hierarchicznymi modelami obiektowymi, a dla prostego lokalnego przechowywania rozważ GRDB lub SwiftData.

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.

Omów projekt

Przeczytaj również