Core Data è un framework Apple per la gestione di un grafo di oggetti in applicazioni iOS e macOS. Fornisce persistenza dei dati, tracciamento delle modifiche, operazioni di annullamento e integrazione con l'UI attraverso NSFetchedResultsController. Secondo la documentazione Apple Developer (2025), Core Data non è un database — è un livello di modellazione degli oggetti che per impostazione predefinita utilizza SQLite come archivio persistente per caricare e salvare oggetti.
Punti chiave
Core Data è un framework di gestione del grafo oggetti e persistenza che fa parte dell'SDK Cocoa Touch di Apple. Fornisce un'interfaccia orientata agli oggetti per lavorare con i dati: lo sviluppatore opera con entità, attributi e relazioni, mentre Core Data trasforma questi oggetti in record di database relazionale sotto il cofano.
Core Data è stato introdotto in Mac OS X 10.4 Tiger (2005) per macOS e portato su iOS 3.0 (2009). In oltre 20 anni, il framework si è evoluto da un semplice livello di astrazione su SQLite a uno stack completo con supporto per la sincronizzazione cloud tramite NSPersistentCloudKitContainer, multithreading tramite gestione automatica dei contesti e caricamento asincrono via Swift Concurrency.
Secondo un sondaggio tra sviluppatori iOS della Slack Community (2025), Core Data è utilizzato nel 68% delle applicazioni iOS commerciali per l'archiviazione locale dei dati. Nonostante le critiche per la complessità e l'architettura a strati, il framework rimane lo standard per le applicazioni Apple grazie all'integrazione stretta con il sistema, al costo zero (integrato nell'SDK) e al supporto per la sincronizzazione iCloud.
Un equivoco comune è considerare Core Data un database. Il framework non esegue query SQL direttamente e non è un DBMS. Core Data è un livello di gestione del grafo oggetti che può utilizzare archivi SQLite, Binary o In-Memory per la persistenza. Analogia: Core Data è come Hibernate o Entity Framework ma per l'ecosistema Apple, e SQLite sotto è come MySQL sotto Hibernate.
Lo stack Core Data è costituito da quattro componenti interconnessi: NSManagedObjectModel (schema dati), NSPersistentStoreCoordinator (coordinatore dell'archivio), NSManagedObjectContext (contesto di lavoro) e NSPersistentContainer (un contenitore unificato che combina tutti e tre da iOS 10). NSPersistentContainer automatizza la creazione e la configurazione dello stack.
Ogni componente svolge una funzione strettamente definita. NSManagedObjectModel carica il file .xcdatamodeld con le descrizioni delle entità. NSPersistentStoreCoordinator collega il modello al file fisico dell'archivio (SQLite). NSManagedObjectContext fornisce un'area temporanea per lavorare con gli oggetti. Il contenitore unifica tutto in una singola chiamata di inizializzazione.
SQLite (NSSQLiteStoreType) è l'archivio standard utilizzato nella maggior parte delle applicazioni. I dati vengono salvati in un unico file .sqlite con supporto per transazioni ACID. Binary (NSBinaryStoreType) è un archivio in formato binario per set di dati piccoli (fino a poche centinaia di oggetti). In-Memory (NSInMemoryStoreType) è un archivio temporaneo in RAM senza persistenza su disco, utilizzato per test e cache.
| Tipo di archivio | Formato | Prestazioni | Quando usarlo |
|---|---|---|---|
| SQLite | .sqlite | Alte | Scelta standard per la produzione |
| Binary | .binary | Medie | Set di dati piccoli |
| In-Memory | RAM | Massime | Test, cache, dati temporanei |
| CloudKit | iCloud | Dipende dalla rete | Sincronizzazione tra dispositivi |
Il tipo di archivio viene impostato con una singola riga durante l'inizializzazione di NSPersistentStoreDescription. Lo sviluppatore può passare da SQLite a In-Memory per test unitari o a CloudKit per la sincronizzazione iCloud senza modificare il codice di manipolazione degli oggetti — Core Data astrae le differenze tra i tipi di archivio tramite un'API di contesto unificata.
NSManagedObject è la classe base per tutti gli oggetti Core Data, che rappresenta un singolo record di entità. Ogni managed object ha un NSManagedObjectID univoco (identificatore persistente), è legato a un contesto e tiene traccia delle sue modifiche tramite KVO (Key-Value Observing). Gli sviluppatori creano sottoclassi di NSManagedObject per definire proprietà tipizzate dell'entità.
NSManagedObjectContext è il componente centrale di Core Data che fornisce un'area di lavoro per tutte le operazioni sugli oggetti. Il contesto tiene traccia di aggiunte, eliminazioni e modifiche (change tracking), supporta l'annullamento delle operazioni tramite undoManager e unisce automaticamente le modifiche da altri contesti quando riceve notifiche di salvataggio.
La regola delle code private: NSManagedObjectContext viene creato con .privateQueueConcurrencyType o .mainQueueConcurrencyType. Il contesto principale è legato al thread principale dell'UI, mentre i contesti privati vengono eseguiti su code di background. Ogni contesto deve essere utilizzato solo sulla propria coda — accedere a un managed object da un altro thread provoca un crash. parentContext consente di organizzare una gerarchia di contesti per scritture asincrone.
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 crea automaticamente viewContext (coda principale) e fornisce newBackgroundContext() per le operazioni in background. Impostare automaticallyMergesChangesFromParent = true fa sì che viewContext raccolga automaticamente le modifiche dai contesti in background quando salvano, aggiornando l'UI senza dover recuperare nuovamente i dati manualmente.
NSPersistentStoreCoordinator gestisce l'archivio dati fisico: apre il file, crea le tabelle SQLite basate sul modello ed esegue migrazioni quando lo schema cambia. Durante l'inizializzazione di NSPersistentStoreDescription con NSSQLiteStoreType, Core Data crea un file SQLite con uno schema corrispondente al modello .xcdatamodeld.
Core Data non utilizza query SQL standard tramite SELECT/INSERT/UPDATE. Invece, genera comandi SQL interni basati sul modello e sulle query effettuate tramite NSFetchRequest. Lo sviluppatore può attivare la registrazione SQL con l'argomento di avvio -com.apple.CoreData.SQLDebug 1 per il debug delle prestazioni delle query.
Migrazione leggera (Lightweight Migration) è un processo automatico di aggiornamento dello schema SQLite quando si aggiungono nuovi attributi, si modificano i flag optional/required o si rinomina con renamingID. La migrazione pesante è necessaria per modifiche radicali dello schema come unire o dividere entità e viene eseguita tramite un NSMigrationManager personalizzato.
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)") }
}
La configurazione della migrazione automatica tramite NSMigratePersistentStoresAutomaticallyOption e NSInferMappingModelAutomaticallyOption consente a Core Data di aggiornare autonomamente il file SQLite quando vengono aggiunti attributi o entità in una nuova versione del modello. Se la migrazione non è possibile, il coordinatore dell'archivio genera un errore con la descrizione del motivo — lo sviluppatore deve quindi implementare una migrazione personalizzata tramite NSMigrationManager.
NSFetchRequest è lo strumento principale per recuperare oggetti da Core Data. Una richiesta contiene il nome dell'entità, un predicato (filtro), descrittori di ordinamento, limite e offset. Il risultato viene restituito come array di NSManagedObject o sottoclassi tipizzate. NSPredicate supporta condizioni complesse con AND, OR, IN, LIKE e sottoquery.
NSBatchDeleteRequest è un modo efficiente per eliminare oggetti in blocco senza caricare ciascuno in memoria. La richiesta viene eseguita a livello SQLite, bypassando il managed object context, e aggiorna il contesto solo dopo il completamento. Esistono richieste batch simili per l'aggiornamento (NSBatchUpdateRequest) e l'inserimento (NSBatchInsertRequest).
CRUD (Create, Read, Update, Delete) in Core Data viene eseguito tramite metodi del contesto: insert, fetch, save e delete. Tutte le modifiche sono temporanee fino alla chiamata di context.save() — questo metodo salva le modifiche nell'archivio persistente SQLite. In caso di errore di salvataggio, il contesto rimane nel suo stato modificato per un nuovo tentativo.
let context = container.viewContext
// Crea
let user = User(context: context)
user.id = 42
user.name = "Alice"
// Leggi
let request = User.fetchRequest()
request.predicate = NSPredicate(format: "name CONTAINS %@", "Ali")
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
let results = try context.fetch(request)
// Aggiorna
results.first?.name = "Alice Updated"
// Elimina
if let first = results.first { context.delete(first) }
// Salva
try context.save()
Salvare il contesto (context.save()) è un'operazione critica. Se save non viene chiamato, tutte le modifiche rimangono solo in memoria. Il contesto tiene traccia dello stato hasChanges, che può essere verificato prima del salvataggio. Per le operazioni in background, utilizzare newBackgroundContext con il proprio salvataggio e per l'UI utilizzare viewContext con salvataggio automatico tramite timer o quando l'app entra in background.
Prima pratica — utilizzare NSPersistentCloudKitContainer per sincronizzare i dati tra i dispositivi dell'utente tramite iCloud. La sincronizzazione cloud viene attivata aggiungendo l'opzione CloudKit alla descrizione dell'archivio persistente. Core Data gestisce automaticamente i conflitti di sincronizzazione e unisce le modifiche da altri dispositivi.
Seconda pratica — evitare fetchRequest senza predicato su tabelle grandi. Ogni recupero incondizionato carica tutti gli oggetti dell'entità in memoria, portando a un elevato consumo di RAM e rallentamento dell'UI. Utilizzare sempre predicati e limiti. Per la paginazione, utilizzare fetchLimit e fetchOffset in NSFetchRequest.
Terza pratica — configurare mergePolicy per risolvere i conflitti nell'accesso multithread. NSMergeByPropertyObjectTrumpMergePolicy aggiorna le proprietà in conflitto dall'ultimo contesto salvato. NSRollbackMergePolicy scarta le modifiche del contesto corrente in caso di conflitto. La scelta della politica dipende dalla logica di business dell'applicazione.
Quarta pratica — utilizzare NSFetchedResultsController per l'integrazione con tabelle e collezioni. Si iscrive automaticamente alle notifiche NSManagedObjectContextDidSave, carica solo gli oggetti necessari (faulting) e notifica il delegato su inserimenti, eliminazioni e spostamenti con i percorsi indice appropriati per l'animazione di UITableView.
Faulting è il meccanismo di caricamento lazy di Core Data. Un managed object restituito da una fetch request è in stato fault — i suoi attributi non sono completamente caricati, solo l'identificatore. Il caricamento completo (fire fault) avviene al primo accesso a qualsiasi attributo. Relationship prefetching (setRelationshipKeyPathsForPrefetching) carica gli oggetti correlati in anticipo, evitando query 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 fa sì che Core Data carichi i dati in lotti di 20 oggetti (per la visualizzazione a schermo), senza caricare l'intera tabella in una volta. Il flag returnsObjectsAsFaults = false garantisce che tutti gli attributi dell'utente vengano caricati immediatamente, utile per la visualizzazione diretta. Il prefetching della relazione “posts” evita query separate per ogni utente quando si accede ai post.
Domande frequenti
No, Core Data è un framework di gestione del grafo oggetti. Fornisce un'API per lavorare con gli oggetti, tracciare le modifiche e persisterli. Il database sotto il cofano di Core Data (SQLite per impostazione predefinita) non deve essere confuso con il framework stesso. Core Data è un ORM, non un DBMS.
Sì, Core Data supporta tre tipi di archivio: SQLite, Binary e In-Memory. Il tipo di archivio viene impostato tramite NSPersistentStoreDescription. L'archivio In-Memory non persiste i dati su disco ed è adatto per test unitari. L'archivio Binary è un formato legacy per set di oggetti compatti.
Per la migrazione leggera, abilitare NSMigratePersistentStoresAutomaticallyOption e NSInferMappingModelAutomaticallyOption. Per modifiche complesse, creare un Mapping Model (.xcmappingmodel) tramite Xcode. L'archivio CloudKit (NSPersistentCloudKitContainer) supporta le migrazioni automaticamente durante la sincronizzazione dello schema con il server iCloud.
SwiftData è un nuovo framework Apple (iOS 17+) costruito su Core Data utilizzando Swift Macros e Swift Concurrency. SwiftData ha una sintassi più semplice: le entità sono descritte con la macro @Model, il contesto con @Environment(\.modelContext). Sotto il cofano, SwiftData utilizza lo stesso stack Core Data e SQLite.
Abilitare l'argomento di avvio -com.apple.CoreData.SQLDebug 1 — Core Data mostrerà tutte le query SQL e la loro durata nella console di Xcode. Per la profilazione, utilizzare Instruments con il template Core Data, che mostra il numero di fault request, i tempi di caricamento degli oggetti e la durata del salvataggio del contesto.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche