Core Data — concetti chiave, NSManagedObject e architettura

Autore: IT Sectr Pubblicato: 2026-03-11 Tempo di lettura: 11 min

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 ORM per iOS/macOS che gestisce un grafo di oggetti e li persiste in un archivio.
  • NSManagedObjectModel è lo schema dati che descrive entità, attributi e relazioni in un modello Core Data.
  • NSManagedObjectContext è un'area di lavoro per creare, leggere, aggiornare ed eliminare oggetti con tracciamento delle modifiche.
  • NSPersistentContainer è un punto di ingresso unificato che incapsula modello, contesto e archivio in iOS 10+.
  • NSFetchedResultsController integra Core Data con UITableView/UICollectionView con aggiornamento automatico alle modifiche.

Cos'è Core Data?

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.

Core Data non è un database

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.

Architettura di Core Data: stack e componenti

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.

Tipi di archivio Core Data

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 archivioFormatoPrestazioniQuando usarlo
SQLite.sqliteAlteScelta standard per la produzione
Binary.binaryMedieSet di dati piccoli
In-MemoryRAMMassimeTest, cache, dati temporanei
CloudKitiCloudDipende dalla reteSincronizzazione 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 e NSManagedObjectContext

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.

Sicurezza dei thread dei contesti

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.

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

Archivio persistente e SQLite

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.

Migrazioni di Core Data

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.

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)") }
}

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.

Core Data in pratica: codice ed esempi

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

Esempio di operazioni CRUD

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.

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

Buone pratiche di Core Data

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.

Prestazioni: Prefetching e Faulting

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.

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

Core Data è un database?

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.

Core Data può essere utilizzato senza SQLite?

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

Come migrare Core Data a una nuova versione del modello?

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.

Qual è la differenza tra Core Data e SwiftData?

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.

Come debugare le query Core Data lente?

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

  • Core Data è un framework di gestione del grafo oggetti e persistenza per iOS e macOS, che utilizza SQLite come archivio predefinito.
  • Lo stack Core Data include NSManagedObjectModel, NSPersistentStoreCoordinator, NSManagedObjectContext e NSPersistentContainer per una configurazione unificata.
  • NSManagedObjectContext è un'area di lavoro con tracciamento delle modifiche, supporto per annullamento e unione automatica dai contesti in background.
  • NSFetchRequest con NSPredicate e prefetching delle relazioni è lo strumento di recupero principale, ottimizzato tramite batch size e faulting.
  • La migrazione leggera aggiorna automaticamente lo schema SQLite quando vengono aggiunti attributi ed entità in una nuova versione del modello.
  • NSPersistentCloudKitContainer aggiunge la sincronizzazione iCloud tra i dispositivi dell'utente con risoluzione automatica dei conflitti.
  • Raccomandazione — utilizzare Core Data per applicazioni iOS con modelli di oggetti gerarchici; per l'archiviazione locale semplice, considerare GRDB o SwiftData.

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.

Discuti il progetto

Leggi anche