[macOS][TUTORIEL]Introduction à CoreData

Un tuto pour macOS, j'en ai presque honte .. Je l'ai rédigé à la demande de Patyom qui a quelques difficultés pour démarrer avec CoreData sous macOS
http://forum.cocoacafe.fr/topic/15258-comment-remplir-une-scrollview/?p=147678
De toute manière, c'est exactement la même chose sous iOS. Et cela m'a permis de comprendre quelques trucs que je n'avais pas saisi initialement.
Le but de cet exercice est de créer une mini-base de données pour archiver des bulletins de note scolaire. Chaque fiche contient :
- le nom d'un élève
- son âge
- sa moyenne
L'application permet de créer des fiches, de les afficher et de les détruire. La génération d'une nouvelle fiche se fait de manière aléatoire pour éviter un code de saisie trop compliqué.
Première étape : création d'une Cocoa Application pour macOS
Réponses
Seconde étape, on indique à Xcode qu'il s'agit d'une application utilisant CoreData, en cochant l'option Use Core Data.
Le projet généré par Xcode contient un fichier avec l'extension xcdatamodeld. C'est là que les objets CoreData sont définis. Dans le techno jargon cupertinien, ce sont des Entités (Entity).
Pour créer une Entity, il faut :
1) sélectionner le fichier .xcdatamodeld
2) cliquer sur le bouton (+) Add Entity en bas de l'écran
3) sélectionner la nouvelle Entity
4) modifier son nom avec l'inspecteur de propriétés
Sans grande originalité, j'ai appelé mon Entity Fiche.
Une Entity CoreData peut avoir des attributs (nom, âge et moyenne pour mes fiches).
Pour définir un attribut, il faut sélectionner l'Entité, puis :
1) cliquer sur le bouton (+) Add Attribute en bas de la fenêtre
2) Définir le nom et le type de l'attribut
On ne peut pas mettre n'importe quoi dans une Entity. On ne peut utiliser que les types existants.
Mon premier attribut est un nom de type String.
Au final, j'ai défini 3 attributs pour mon objet (Entity) Fiche :
- un nom de type String
- un age de type Int32
- une moyenne de type Float
La gestion de CoreData s'effectue par le biais d'un objet appelé PersistentContainer, généré automatiquement par Xcode si l'option Use Core Data est coché à la création de l'application. Il est associé au délégué de l'Application. Il faut récupérer sa valeur à chaque accès à CoreData ou la stocker dans une variable.
Moi j'ai préféré lire sa valeur avec une petite routine, défini dans le viewController.
Il est de type NSManagedObjectContext?. Les NSManagedObject sont la clé pour utiliser CoreData sans (trop) de prise de tête. L'Entity Fiche que j'ai défini avec l'éditeur graphique de CoreData est traité automatiquement comme une classe de type NSManagedObject.
Pour créer un objet de type Fiche, il faut l'initialiser avec le context CoreData :
Et remplir les attribut avec les bonnes propriétés :
Attention, le contexte ne sauve pas automatiquement les données dans CoreData. Il faut lui demander de le faire.
Ce code sauve dans le fichier CoreData (le persistentContainer) toutes les modifications des objets liées au contexte depuis la dernière sauvegarde.
Voici tous les morceaux de code réunis dans une fonction :
Le template d'application Apple défini le contexte comme étant de type NSManagedObject?. Cela veut dire que (parfois, éventuellement, peut-être) il peut se produire un problème avec le persistentContainer (impossibilité de le créer par manque de mémoire disque, par exemple). Dans ce cas, il sera de valeur nil.
La plupart des tutos sur CoreData lise la valeur du contexte en le forçant avec l'opérateur "!". C'est MAL ! Et plantogéne. Bon d'accord, il est peu probable que le context soit nil, mais cela peut arriver sur un ordinateur/device n'ayant plus de mémoire disque. Et là , paf .. le plantage ! Et un mauvais commentaire sur le Store : "L'application iTruc plante sans cesse. c'est une honte de vendre une horreur pareille. BOUH !".
C'est pourquoi je récupère la valeur du contexte CoreData avec un guard, au début de la fonction.
Si le context est nil, la fonction ne s'exécute pas et ne PLANTE PAS !
Pour une application commerciale, j'aurais ajouté l'affichage d'un message à l'utilisateur. Quelque chose comme : "L'application ne parvient pas à écrire des données sur le disque. Il est possible que votre disque soit complètement remplis".
Un message d'alerte vaut toujours mille fois mieux qu'un PLANTAGE SEC !
EDIT : Le nom des élèves est généré aléatoirement à partir d'un tableau de String :
Etape suivante, comme lire le contenu des fiches ?
Nous avons déjà parlé de la manière de lire des données CoreData dans ce topic :
http://forum.cocoacafe.fr/topic/15239-introduction-à -coredata-swift-3/
Je me contente juste de donner le code nécessaire au chargement des Fiches :
La méthode chargementFiches() scanne le fichier CoreData, pour retourner un tableau contenant tous les objets de type Fiche.
Il suffit ensuite de parcourir le tableau pour consulter le contenu des fiches :
Pour détruire un objet CoreData, il suffit de le demander au context.
Mais attention, la destruction n'est pas immédiate. Et ne sera effective qu'après la prochaine mise à jour du contexte.
Voici une routine détruisant TOUS les objets de type Fiche contenus dans CoreData :
Elle commence par charger les Fiches, puis fait un test pour vérifier qu'il en existe au moins une. Un message d'erreur est affiché si aucune fiche n'existe.
J'ai écrit une petite application pour tester tout ça, mélangeant interface macOS et affichage console.
A la première utilisation, elle affiche qu'il n'y a aucune fiche.
Je presse sur le bouton "Affichage des fiches". Elle me répond que :
Puis, sur le bouton "Détruire toutes les fiches" :
Et pourquoi pas "créer nouvelle fiche aléatoire" ?. Il s'affiche alors :
Nouveau clic sur le bouton "Affichage des fiches" :
Je quitte l'application avant de la relancer. Il s'affiche :
Il y a bien eu pertinence des données, malgré l'arrêt du programme.
Pour en être certain, je clique sur le bouton "Affichage des Fiches".
Pas de doute, Jacques est vraiment un cancre !
Petit arrêt pour cause de résultats électorales.
Je détruit les fiches avec le bouton "Détruire toutes les fiches"
Nouvelle pression sur "Afficher les fiches" :
Je quitte l'application et je relance :
Persistence .. ok !
C'est a peu prés tout ce que je sais faire avec CoreData, du moins pour le moment.
Le code source peut être téléchargé ici :
Cool, je faire lire çà attentivement mais un peu plus tard, maintenant c'est à mon tour d'avoir un PB : avec un Congélateur qui refuse de se remettre en route et avec le temps que l'on a, c'est pas du gateau.
Merci A +
Très bien, là , je comprends nettement avec facilité. Tu te doutes que j'ai encore des questions dessus !
Peut-on visionner le fichier "Fiches", pour voir comment c'est stocké ?
Dans ma future BDD j'ai des dates à inclure et bien entendu quand je vais faire une requête avec mois ou année en recherche, dons il faut que j'ajoute des positions dans ma fiche avec par exemple : une pour le Mois, une autre pour l'Année ? c'est bien comme cela que ça fonctionne ?
Oui je le demande car dans ma jeunesse dans ma BDD, chaque fiche avait une clé (ID) qui était créée par programme dans laquelle j'incluais les valeurs "AAMM", au fait, y-a-t'il une clé d'enregistrement ?
en tout cas merci
Euh .. pas la moindre idée. Je ne connais pas grand chose aux bases de données, moi. Je découvre CoreData pratiquement en même temps que toi. Ma partie c'est plutôt le graphisme, les jeux vidéos, le game design, et l'écriture de tutos.
De toute façon, ce n'est pas la philosophie de Core Data: c'est un ORM (Object-Relationship Mapping), qui sert à traduire les Objets dans un stockage BdD relationnelle " ce qui est un problème en soi, si tu veux mon avis. Tu ne dois pas penser "BdD" mais "Objet".
J'ai un doute, il n'y a pas une type primitif Date dans Core Data ? (Pas le temps de vérifier).
Oui, mais tu n'y a pas accès.
Yes Sir !
Les dates se trient en CoreData. Il suffit de dire l'ordre dans un NSSortDescriptor lorsque tu demande les données.
Ce n'est pas nécessaire de séparer les composants
Petit exemple d'un modèle pour un de mes applis, qui gère un festival
Et le code pour récupérer la liste des Events dans un NSFetchedResultsController :
Peut-on rajouter CoreData à un projet ?
Une actualisation Swift 4, on dit que Core Data a bien évolué, un petit ou grand Tuto même en Anglais je suis preneur ?
Pourquoi je fais cette demande puisque le sujet est déjà a un Tuto et en français... Du coup je pense avoir tout compris (enfin dans le principe!)
merci a vous deux, en plus ça doit fonctionner avec Swift sans problème..
Cela fait longtemps que je n'ai pas planché sur CoreData, mais à priori c'est exactement la même chose avec Swift 4. Préviens-moi si quelque chose ne vas pas.
je patauge moins, mais je barbotte encore quand même...
Un truc qui me taraude si je créé un projet comme celui-ci en cochant "use Core Data" la ligne suivante fonctionne
var listes = Liste // donc créé un tableau
Je veux dire "Liste" est reconnu d'office
Si j'ajoute CoreData après coup rien a faire "Liste" n'est pas reconnu ?
J'ai beau chercher dans le code je ne trouve pas ce qu'il manque ?
Est-ce tout simplement le nom initial du fichier xdatamodel ? (qui a une importance ?)
Le téléchargement du projet source ne donne rien :-((
J'ai l'impression que sur mon projet (éducatif) la lecture ne se fait pas au même endroit que l'écriture sur ces 2 actions je n'ai aucune erreur ? Comment je peux vérifier ça ?
Euh .. quel projet source ? Quel projet éducatif ?