Core Data — concepts clés, NSManagedObject et architecture

Auteur : IT Sectr Publié le : 2026-03-11 Temps de lecture : 11 min

Core Data est un framework Apple pour la gestion d'un graphe d'objets dans les applications iOS et macOS. Il assure la persistance des données, le suivi des modifications, les opérations d'annulation et l'intégration avec l'UI via NSFetchedResultsController. Selon la documentation Apple Developer (2025), Core Data n'est pas une base de données — c'est une couche de modélisation d'objets qui utilise par défaut SQLite comme stockage persistant pour charger et sauvegarder des objets.

Points clés

  • Core Data est un framework ORM pour iOS/macOS qui gère un graphe d'objets et les persiste dans un stockage.
  • NSManagedObjectModel est le schéma de données décrivant les entités, attributs et relations dans un modèle Core Data.
  • NSManagedObjectContext est un espace de travail pour créer, lire, mettre à jour et supprimer des objets avec suivi des modifications.
  • NSPersistentContainer est un point d'entrée unifié encapsulant le modèle, le contexte et le stockage dans iOS 10+.
  • NSFetchedResultsController intègre Core Data avec UITableView/UICollectionView avec mise à jour automatique lors des modifications.

Qu'est-ce que Core Data ?

Core Data est un framework de gestion de graphe d'objets et de persistance qui fait partie du SDK Cocoa Touch d'Apple. Il fournit une interface orientée objet pour travailler avec les données : le développeur opère avec des entités, des attributs et des relations, tandis que Core Data transforme ces objets en enregistrements de base de données relationnelle sous le capot.

Core Data a été présenté dans Mac OS X 10.4 Tiger (2005) pour macOS et porté sur iOS 3.0 (2009). En plus de 20 ans, le framework a évolué d'une simple couche d'abstraction sur SQLite vers un stack complet avec prise en charge de la synchronisation cloud via NSPersistentCloudKitContainer, du multithreading via la gestion automatique des contextes et du chargement asynchrone via Swift Concurrency.

Selon une enquête de développeurs iOS de Slack Community (2025), Core Data est utilisé dans 68 % des applications iOS commerciales pour le stockage local de données. Malgré les critiques pour sa complexité et son architecture en couches, le framework reste la norme pour les applications Apple grâce à une intégration étroite avec le système, un coût nul (intégré au SDK) et la prise en charge de la synchronisation iCloud.

Core Data n'est pas une base de données

Une idée fausse courante est de considérer Core Data comme une base de données. Le framework n'exécute pas de requêtes SQL directement et n'est pas un SGBD. Core Data est une couche de gestion de graphe d'objets qui peut utiliser le stockage SQLite, Binary ou In-Memory pour la persistance. Analogie : Core Data est comme Hibernate ou Entity Framework mais pour l'écosystème Apple, et SQLite en dessous est comme MySQL sous Hibernate.

Architecture de Core Data : stacks et composants

Le stack Core Data se compose de quatre composants interconnectés : NSManagedObjectModel (schéma de données), NSPersistentStoreCoordinator (coordinateur de stockage), NSManagedObjectContext (contexte de travail) et NSPersistentContainer (un conteneur unifié combinant les trois depuis iOS 10). NSPersistentContainer automatise la création et la configuration du stack.

Chaque composant remplit une fonction strictement définie. NSManagedObjectModel charge le fichier .xcdatamodeld avec les descriptions des entités. NSPersistentStoreCoordinator relie le modèle au fichier de stockage physique (SQLite). NSManagedObjectContext fournit une zone temporaire pour travailler avec les objets. Le conteneur unifie tout en un seul appel d'initialisation.

Types de stockage Core Data

SQLite (NSSQLiteStoreType) est le stockage standard utilisé dans la plupart des applications. Les données sont sauvegardées dans un seul fichier .sqlite avec prise en charge des transactions ACID. Binary (NSBinaryStoreType) est un stockage au format binaire pour les petits ensembles de données (jusqu'à quelques centaines d'objets). In-Memory (NSInMemoryStoreType) est un stockage temporaire en RAM sans persistance sur disque, utilisé pour les tests et le cache.

Type de stockageFormatPerformancesQuand l'utiliser
SQLite.sqliteÉlevéesChoix standard pour la production
Binary.binaryMoyennesPetits ensembles de données
In-MemoryRAMMaximalesTests, cache, données temporaires
CloudKitiCloudDépend du réseauSynchronisation entre appareils

Le type de stockage est défini par une seule ligne lors de l'initialisation de NSPersistentStoreDescription. Le développeur peut passer de SQLite à In-Memory pour les tests unitaires ou à CloudKit pour la synchronisation iCloud sans modifier le code de manipulation des objets — Core Data abstrait les différences entre les types de stockage via une API de contexte unifiée.

NSManagedObject et NSManagedObjectContext

NSManagedObject est la classe de base pour tous les objets Core Data, représentant un seul enregistrement d'entité. Chaque objet géré possède un NSManagedObjectID unique (identifiant persistant), est lié à un contexte et suit ses modifications via KVO (Key-Value Observing). Les développeurs créent des sous-classes de NSManagedObject pour définir des propriétés typées d'entité.

NSManagedObjectContext est le composant central de Core Data qui fournit un espace de travail pour toutes les opérations sur les objets. Le contexte suit les ajouts, suppressions et modifications (change tracking), prend en charge l'annulation d'opérations via undoManager et fusionne automatiquement les modifications des autres contextes lors de la réception de notifications de sauvegarde.

Sécurité des threads des contextes

La règle des files d'attente privées : NSManagedObjectContext est créé avec .privateQueueConcurrencyType ou .mainQueueConcurrencyType. Le contexte principal est lié au thread principal de l'UI, tandis que les contextes privés s'exécutent sur des files d'attente d'arrière-plan. Chaque contexte doit être utilisé uniquement sur sa propre file d'attente — accéder à un objet géré depuis un autre thread provoque un crash. parentContext permet d'organiser une hiérarchie de contextes pour les écritures asynchrones.

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 crée automatiquement viewContext (file principale) et fournit newBackgroundContext() pour les opérations d'arrière-plan. Définir automaticallyMergesChangesFromParent = true fait que viewContext récupère automatiquement les modifications des contextes d'arrière-plan lors de leur sauvegarde, mettant à jour l'UI sans nécessiter de réinterrogation manuelle des données.

Stockage persistant et SQLite

NSPersistentStoreCoordinator gère le stockage physique des données : il ouvre le fichier, crée les tables SQLite basées sur le modèle et effectue des migrations lorsque le schéma change. Lors de l'initialisation de NSPersistentStoreDescription avec NSSQLiteStoreType, Core Data crée un fichier SQLite avec un schéma correspondant au modèle .xcdatamodeld.

Core Data n'utilise pas de requêtes SQL standard via SELECT/INSERT/UPDATE. Au lieu de cela, il génère des commandes SQL internes basées sur le modèle et les requêtes effectuées via NSFetchRequest. Le développeur peut activer la journalisation SQL avec l'argument de lancement -com.apple.CoreData.SQLDebug 1 pour déboguer les performances des requêtes.

Migrations Core Data

Migration légère (Lightweight Migration) est un processus automatique de mise à jour du schéma SQLite lors de l'ajout de nouveaux attributs, du changement de flags optional/required ou du renommage avec renamingID. Une migration lourde est nécessaire pour les changements radicaux de schéma comme la fusion ou la division d'entités, et est effectuée via un NSMigrationManager personnalisé.

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 configuration de la migration automatique via NSMigratePersistentStoresAutomaticallyOption et NSInferMappingModelAutomaticallyOption permet à Core Data de mettre à jour indépendamment le fichier SQLite lors de l'ajout d'attributs ou d'entités dans une nouvelle version du modèle. Si la migration n'est pas possible, le coordinateur de stockage lance une erreur avec une description de la raison — le développeur doit alors implémenter une migration personnalisée via NSMigrationManager.

Core Data en pratique : code et exemples

NSFetchRequest est l'outil principal pour récupérer des objets depuis Core Data. Une requête contient le nom de l'entité, un prédicat (filtre), des descripteurs de tri, une limite et un décalage. Le résultat est retourné sous forme de tableau de NSManagedObject ou de sous-classes typées. NSPredicate prend en charge des conditions complexes avec AND, OR, IN, LIKE et des sous-requêtes.

NSBatchDeleteRequest est un moyen efficace de supprimer des objets en masse sans charger chacun en mémoire. La requête s'exécute au niveau SQLite, contournant le contexte d'objet géré, et ne met à jour le contexte qu'après son achèvement. Des requêtes par lots similaires existent pour la mise à jour (NSBatchUpdateRequest) et l'insertion (NSBatchInsertRequest).

Exemple d'opérations CRUD

CRUD (Create, Read, Update, Delete) dans Core Data est effectué via les méthodes du contexte : insert, fetch, save et delete. Toutes les modifications sont temporaires jusqu'à l'appel de context.save() — cette méthode sauvegarde les modifications dans le stockage persistant SQLite. En cas d'erreur de sauvegarde, le contexte reste dans son état modifié pour une nouvelle tentative.

swift
let context = container.viewContext

// Créer
let user = User(context: context)
user.id = 42
user.name = "Alice"

// Lire
let request = User.fetchRequest()
request.predicate = NSPredicate(format: "name CONTAINS %@", "Ali")
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
let results = try context.fetch(request)

// Mettre à jour
results.first?.name = "Alice Updated"

// Supprimer
if let first = results.first { context.delete(first) }

// Sauvegarder
try context.save()

Sauvegarder le contexte (context.save()) est une opération critique. Si save n'est pas appelé, toutes les modifications restent uniquement en mémoire. Le contexte suit l'état hasChanges, qui peut être vérifié avant la sauvegarde. Pour les opérations d'arrière-plan, utilisez newBackgroundContext avec sa propre sauvegarde, et pour l'UI, utilisez viewContext avec une sauvegarde automatique sur minuteur ou lorsque l'application passe en arrière-plan.

Bonnes pratiques Core Data

Première pratique — utilisez NSPersistentCloudKitContainer pour synchroniser les données entre les appareils de l'utilisateur via iCloud. La synchronisation cloud est activée en ajoutant l'option CloudKit à la description du stockage persistant. Core Data gère automatiquement les conflits de synchronisation et fusionne les modifications des autres appareils.

Deuxième pratique — évitez fetchRequest sans prédicat sur les grandes tables. Chaque récupération inconditionnelle charge tous les objets de l'entité en mémoire, entraînant une consommation élevée de RAM et un ralentissement de l'UI. Utilisez toujours des prédicats et des limites. Pour la pagination, utilisez fetchLimit et fetchOffset dans NSFetchRequest.

Troisième pratique — configurez mergePolicy pour résoudre les conflits lors de l'accès multithread. NSMergeByPropertyObjectTrumpMergePolicy met à jour les propriétés en conflit à partir du dernier contexte sauvegardé. NSRollbackMergePolicy annule les modifications du contexte actuel en cas de conflit. Le choix de la politique dépend de la logique métier de l'application.

Quatrième pratique — utilisez NSFetchedResultsController pour l'intégration avec les tableaux et les collections. Il s'abonne automatiquement aux notifications NSManagedObjectContextDidSave, charge uniquement les objets nécessaires (faulting) et notifie le délégué des insertions, suppressions et déplacements avec les index paths appropriés pour l'animation UITableView.

Performances : Prefetching et Faulting

Faulting est le mécanisme de chargement paresseux de Core Data. Un objet géré retourné par une requête fetch est dans un état fault — ses attributs ne sont pas complètement chargés, seulement l'identifiant. Le chargement complet (fire fault) se produit au premier accès à un attribut. Relationship prefetching (setRelationshipKeyPathsForPrefetching) charge les objets associés à l'avance, évitant les requêtes 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 force Core Data à charger les données par lots de 20 objets (pour l'affichage à l'écran), sans charger la totalité du tableau à la fois. Le flag returnsObjectsAsFaults = false garantit que tous les attributs de l'utilisateur sont chargés immédiatement, ce qui est utile pour l'affichage direct. Le prefetching de la relation “posts” évite des requêtes séparées pour chaque utilisateur lors de l'accès aux publications.

Questions fréquentes

Core Data est-il une base de données ?

Non, Core Data est un framework de gestion de graphe d'objets. Il fournit une API pour travailler avec les objets, suivre les modifications et les persister. La base de données sous le capot de Core Data (SQLite par défaut) ne doit pas être confondue avec le framework lui-même. Core Data est un ORM, pas un SGBD.

Core Data peut-il être utilisé sans SQLite ?

Oui, Core Data prend en charge trois types de stockage : SQLite, Binary et In-Memory. Le type de stockage est défini via NSPersistentStoreDescription. Le stockage In-Memory ne persiste pas les données sur le disque et convient aux tests unitaires. Le stockage Binary est un format hérité pour les ensembles d'objets compacts.

Comment migrer Core Data vers une nouvelle version du modèle ?

Pour la migration légère, activez NSMigratePersistentStoresAutomaticallyOption et NSInferMappingModelAutomaticallyOption. Pour les modifications complexes, créez un Mapping Model (.xcmappingmodel) via Xcode. Le stockage CloudKit (NSPersistentCloudKitContainer) prend en charge les migrations automatiquement lors de la synchronisation du schéma avec le serveur iCloud.

Quelle est la différence entre Core Data et SwiftData ?

SwiftData est un nouveau framework Apple (iOS 17+) construit sur Core Data utilisant Swift Macros et Swift Concurrency. SwiftData a une syntaxe plus simple : les entités sont décrites avec la macro @Model, le contexte avec @Environment(\.modelContext). Sous le capot, SwiftData utilise le même stack Core Data et SQLite.

Comment déboguer les requêtes Core Data lentes ?

Activez l'argument de lancement -com.apple.CoreData.SQLDebug 1 — Core Data affichera toutes les requêtes SQL et leur durée dans la console Xcode. Pour le profilage, utilisez Instruments avec le modèle Core Data, qui montre le nombre de fault requests, les temps de chargement des objets et la durée de sauvegarde du contexte.

Résumé

  • Core Data est un framework de gestion de graphe d'objets et de persistance pour iOS et macOS, utilisant SQLite comme stockage par défaut.
  • Le stack Core Data inclut NSManagedObjectModel, NSPersistentStoreCoordinator, NSManagedObjectContext et NSPersistentContainer pour une configuration unifiée.
  • NSManagedObjectContext est un espace de travail avec suivi des modifications, prise en charge de l'annulation et fusion automatique depuis les contextes d'arrière-plan.
  • NSFetchRequest avec NSPredicate et le prefetching de relations est l'outil de récupération principal, optimisé via la taille de lot et le faulting.
  • La migration légère met automatiquement à jour le schéma SQLite lors de l'ajout d'attributs et d'entités dans une nouvelle version du modèle.
  • NSPersistentCloudKitContainer ajoute la synchronisation iCloud entre les appareils de l'utilisateur avec résolution automatique des conflits.
  • Recommandation — utilisez Core Data pour les applications iOS avec des modèles d'objets hiérarchiques ; pour un stockage local simple, envisagez GRDB ou SwiftData.

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi