Core Data & SwiftUI
Module 10 Notes de cours · Persistance des données avec Core Data
01 Introduction
Dans presque toutes les applications iOS, on a besoin de sauvegarder des données pour qu'elles survivent à la fermeture de l'app. Par exemple : une liste de tâches, un carnet de contacts, un historique d'achats.
Une première approche consiste à utiliser directement SQLite, la base de données embarquée sur iOS. Ça fonctionne, mais ça oblige à écrire du SQL un langage qui n'est pas orienté objet et qui ajoute de la complexité.
Core Data est un framework d'Apple qui sert d'intermédiaire entre votre code Swift et la base de données. Il vous permet de travailler avec vos données comme des objets Swift normaux sans écrire une seule ligne de SQL.
02 La Stack Core Data
Core Data n'est pas un composant unique c'est un ensemble de couches qui travaillent ensemble. On appelle cet ensemble la Core Data Stack. Votre code n'interagit qu'avec la couche du haut ; les couches inférieures fonctionnent de façon transparente.
Diagramme de la Core Data Stack
Votre code n'interagit qu'avec le Persistent Container et le Managed Object Context les couches inférieures sont transparentes.
Persistent Container
C'est la porte d'entrée de Core Data. Il crée et configure toute la Stack en une seule opération.
- Crée le modèle de données depuis le fichier
.xcdatamodeld - Charge la base de données SQLite sur disque
- Expose le
viewContextpour travailler avec les données
Managed Object Context (MOC)
C'est votre espace de travail. Toutes vos opérations sur les données (créer, lire, modifier, supprimer) passent par ici.
- Contient les objets en mémoire vive (RAM)
- Accumule les changements sans les sauvegarder immédiatement
- Appel à
.save()= écriture vers le store
Managed Object Model
Définit la structure de vos données les entités, leurs attributs et les relations entre elles.
- Contenu dans le fichier
.xcdatamodeld - Une entité ≈ une table SQL / une classe Swift
- Un attribut ≈ une colonne SQL / une propriété
- Une relation ≈ une clé étrangère SQL
Persistent Store Coordinator
Fait le lien entre le MOC et le fichier de stockage. En tant que développeur, vous n'interagissez presque jamais directement avec lui.
- Gère potentiellement plusieurs stores à la fois
- Gère les migrations lors des changements de modèle
- Présente plusieurs stores comme un seul au MOC
Persistent Store Object
C'est le support de stockage physique. Par défaut sur iOS, c'est un fichier SQLite. Core Data supporte aussi XML, binaire et mémoire vive (en RAM, non persistant).
Le choix du type de store est transparent votre code Swift appelle toujours les mêmes méthodes Core Data, quelle que soit la technologie de stockage sous-jacente.
03 Cycle de vie des données
Comprendre le cycle de vie des données est essentiel pour ne pas se perdre. Voici les quatre étapes que suit chaque donnée :
.save() sur le MOCviewContext.save() n'est pas appelé,
les changements n'existent qu'en mémoire RAM. Si l'app se ferme avant la sauvegarde, les données
sont perdues.04 Modèle d'entités (.xcdatamodeld)
Le fichier .xcdatamodeld est l'éditeur visuel où vous définissez la structure de
vos données. Xcode le génère automatiquement si vous cochez l'option Core Data à la création du projet,
mais on peut aussi le créer manuellement (ce qu'on fait dans le laboratoire).
NSPersistentContainer(name:).Définir une entité
Une entité dans Core Data est l'équivalent d'une classe en Swift ou d'une table dans une base de données. Xcode génère automatiquement la classe Swift correspondante.
- Ouvrez le fichier
.xcdatamodeld - Cliquez sur Add Entity en bas du panneau
- Double-cliquez sur le nom pour le changer (ex. :
Product) - Ajoutez des attributs en cliquant sur + dans la section Attributes
- Choisissez le type de chaque attribut dans le menu déroulant
Types d'attributs disponibles
| Type | Usage typique | Exemple |
|---|---|---|
| String | Texte libre | Nom, description, email |
| Integer 16/32/64 | Nombres entiers 16 = petit (-32 768 à 32 767) · 32 = standard · 64 = très grand |
Quantité, âge, score |
| Double / Float | Nombres décimaux | Prix, coordonnées GPS |
| Boolean | Vrai / Faux | Tâche complétée, en stock |
| Date | Date et heure | Date de création, échéance |
| Binary Data | Données brutes | Image, fichier PDF |
| UUID | Identifiant unique | Clé primaire unique |
| Transformable | Tout objet Swift conforme à Codable |
Structures complexes personnalisées |
Relations entre entités
Les relations permettent de lier deux entités entre elles, comme des clés étrangères en SQL. Il existe trois types de relations :
Category → products et Product → category).
Ça permet à Core Data de maintenir la cohérence des données automatiquement.05 Utilisation en Swift
Voici les trois éléments Swift que vous utiliserez le plus souvent avec Core Data.
1 Initialiser la Stack : PersistenceController
On crée une structure PersistenceController (généralement dans un fichier
Persistence.swift) qui initialise le NSPersistentContainer et expose
le viewContext au reste de l'application.
import CoreData struct PersistenceController { // Singleton : une seule instance dans toute l'app static let shared = PersistenceController() let container: NSPersistentContainer init() { // Nom du fichier .xcdatamodeld (sans l'extension) container = NSPersistentContainer(name: "Products") container.loadPersistentStores { _, error in if let error { fatalError("Erreur : \(error)") } } // Synchronise le contexte si des données changent en arrière-plan container.viewContext.automaticallyMergesChangesFromParent = true } } // Dans l'App : on injecte le viewContext dans l'environnement SwiftUI // → Toutes les vues peuvent y accéder via @Environment(\.managedObjectContext)
2 Lire des données : @FetchRequest
@FetchRequest est un property wrapper SwiftUI qui lit automatiquement
les données depuis Core Data et met à jour la vue dès qu'un changement est détecté.
Pas besoin de recharger manuellement.
@FetchRequest vous notifie chaque fois que les
données changent. La vue se re-dessine automatiquement.// Récupère tous les produits, sans tri @FetchRequest(sortDescriptors: []) private var products: FetchedResults<Product> // Récupère tous les produits, triés alphabétiquement par nom // SortDescriptor(\.name) est vérifié à la compilation (plus sûr que "name" en String) @FetchRequest(sortDescriptors: [SortDescriptor(\.name)]) private var products: FetchedResults<Product> // Récupère les produits par quantité, ordre décroissant @FetchRequest(sortDescriptors: [SortDescriptor(\.quantity, order: .reverse)]) private var products: FetchedResults<Product>
3 Filtrer les données : NSPredicate
Un NSPredicate définit des critères de filtrage l'équivalent de
la clause WHERE en SQL. On peut l'utiliser avec @FetchRequest ou dans
une requête ponctuelle.
// Égalité exacte produits dont le nom est exactement "Pomme" NSPredicate(format: "name == %@", "Pomme") // Contient (insensible à la casse et aux accents grâce à [cd]) NSPredicate(format: "name CONTAINS[cd] %@", searchText) // Commence par NSPredicate(format: "name BEGINSWITH[cd] %@", "A") // Comparaison numérique quantité supérieure à 10 NSPredicate(format: "quantity > %d", 10) // Combinaison de critères avec AND / OR NSPredicate(format: "name CONTAINS[cd] %@ AND quantity > %d", searchText, 5)
@FetchRequest, vous pouvez faire une requête unique dans
un .task ou une fonction, en construisant un NSFetchRequest et en
appelant viewContext.fetch() directement.@Environment(\.managedObjectContext) private var viewContext @State private var results: [Product] = [] // Exécutée une seule fois à l'apparition de la vue .task { let request: NSFetchRequest<Product> = Product.fetchRequest() request.predicate = NSPredicate( format: "name CONTAINS[cd] %@", searchText ) request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)] results = (try? viewContext.fetch(request)) ?? [] }
Passer au laboratoire
Module 10 Créer un projet SwiftUI avec Core Data de A à Z