[Rà‰SOLU] Initialiser CoreData avec des valeurs par défaut

BenjoBenjo Membre
décembre 2014 modifié dans Dev. iOS, watchOS, tvOS #1

Bonjour à  tous,


 


Dans un de mes projets je me trouve à  utiliser CoreData. Seulement, j'aimerais qu'au premier lancement de l'application, ma base de donnée soit initialisée avec certaines valeurs dedans. Par exemple, admettons que j'ai une Entité "Personnes" avec comme attributs "nom" et "prénom". J'aimerais qu'au premier lancement de mon app, ma base ai les valeurs "Homer Simpson" et "Marge Simpson".


 


Alors, pour ce genre de situation j'aurais pu faire "simple", c'est à  dire effectuer une boucle en faisant un save sur mon managedObjectContext à  chaque fois. Sauf que ma base va être initialisée avec une centaine de valeurs, donc la boucle ne se révèle pas bien pratique ici...


 


Comment puis-je simplement insérer des valeurs par défaut à  ma base de donnée CoreData ? J'ai pas mal cherché sur le web et je n'ai pas trouvé ce que je cherchais.


 


Merci d'avance pour vos réponse :)


Mots clés:

Réponses

  • Bonjour,


     


    L'article suivant peut peut-être vous donner une solution


     


    http://www.raywenderlich.com/12170/core-data-tutorial-how-to-preloadimport-existing-data-updated


  • Tu peux utiliser les préférences pour déterminer le premier lancement de ton application. Ensuite il te suffit d'ajouter tes valeurs uniquement s'ils satisfont tes conditions.


  • Merci à  tous pour vos réponses.


     




    Bonjour,


     


    L'article suivant peut peut-être vous donner une solution


     


    http://www.raywenderlich.com/12170/core-data-tutorial-how-to-preloadimport-existing-data-updated




     


    Merci pour le lien ça m'a été très utile. J'ai testé cette solution qui marche bien pour moi. Mais est-ce que ce n'est pas un peu barbare comme méthode ?


     


     




    Tu peux utiliser les préférences pour déterminer le premier lancement de ton application. Ensuite il te suffit d'ajouter tes valeurs uniquement s'ils satisfont tes conditions.




     


    Je n'ai pas bien compris ce que tu veux dire :/


     


    Sinon je n'y avais pas pensé, mais je peux aussi créer une base de donnée SQL, l'importer dans mon projet. Puis au premier lancement de l'app, je copie les données sur CoreData. J'ai essayé aussi ça marche tout aussi bien, sachant qu'il est possible d'importer un fichier CSV dans une base SQL.


  • Joanna CarterJoanna Carter Membre, Modérateur


    Sinon je n'y avais pas pensé, mais je peux aussi créer une base de donnée SQL, l'importer dans mon projet. Puis au premier lancement de l'app, je copie les données sur CoreData. J'ai essayé aussi ça marche tout aussi bien, sachant qu'il est possible d'importer un fichier CSV dans une base SQL.




     


    Mais CoreData est, au fond, une base de données SQLite. Tu pourrais la créer et initialiser dans un petit app OS X et copier le BDD résultant dans ton projet

  • AliGatorAliGator Membre, Modérateur

    Mais CoreData est, au fond, une base de données SQLite. Tu pourrais la créer et initialiser dans un petit app OS X et copier le BDD résultant dans ton projet

    Oui nous c'est ce qu'on fait aussi. C'est bête de créer une base SQL qui aura au final sensiblement la même structure / le même schéma que la base CoreData, tout ça pour copier une par une les données. Autant directement mettre la base CoreData (le fichier .sqlite utilisé par CoreData) dans le bundle et le copier au premier lancement ! (c'est ce que l'article explique, je crois).

    C'est pas bien compliqué de faire un nouveau Target (de type OSX / Command Line), de rajouter dans ce target le xcdatamodel et toutes les classes de NSManagedObject associées, et dans le main.m de ce nouveau target faire un [MagicalRecord setupCoreDataStack] puis une création & insertion par code des NSManagerObject que tu voudras avoir comme jeu de données de départ. A la sortie ce target te génère une base CoreData remplie comme tu veux et tu n'as plus qu'à  copier le .sqlite dans ton projet principal.
  • L'outil SQLite Professional peut également t'aider à  construire ton contenu sans trop d'effort...



  •  


     


    Je n'ai pas bien compris ce que tu veux dire :/

     


    Tu peux utiliser les préférences pour stocker une valeur sur le device, par exemple un boolean qui détermine si c'est le premier lancement ou non. Si c'est le cas tu ajoutes tes valeurs.


  • Une autre solution, peut-être un peu tordue pour l'instant, mais à  garder en tête pour le futur, c'est l'utilisation de la public database de CloudKit.


    Elle est accessible sans compte iCloud et peut permettre des mises à  jours des données initiales après coup.

    Cela peut-être une solution si tu as des données initiales conditionnées par la langue ou la localisation de l'utilisateur.


    Bon le gros inconvénient, c'est qu'on est sur du iOS 8 only.


  • Mais CoreData est, au fond, une base de données SQLite. Tu pourrais la créer et initialiser dans un petit app OS X et copier le BDD résultant dans ton projet




     




    Oui nous c'est ce qu'on fait aussi. C'est bête de créer une base SQL qui aura au final sensiblement la même structure / le même schéma que la base CoreData, tout ça pour copier une par une les données. Autant directement mettre la base CoreData (le fichier .sqlite utilisé par CoreData) dans le bundle et le copier au premier lancement ! (c'est ce que l'article explique, je crois).


    C'est pas bien compliqué de faire un nouveau Target (de type OSX / Command Line), de rajouter dans ce target le xcdatamodel et toutes les classes de NSManagedObject associées, et dans le main.m de ce nouveau target faire un [MagicalRecord setupCoreDataStack] puis une création & insertion par code des NSManagerObject que tu voudras avoir comme jeu de données de départ. A la sortie ce target te génère une base CoreData remplie comme tu veux et tu n'as plus qu'à  copier le .sqlite dans ton projet principal.




    Effectivement je n'y avait pas du tout pensé et c'est au final ce que je compte faire car cela me semble bien plus pratique. Merci à  vous.


     


     




    L'outil SQLite Professional peut également t'aider à  construire ton contenu sans trop d'effort...




     


    Merci pour le lien. L'avantage par rapport aux autres c'est de créer une BDD semblable à  celle créé par CoreData c'est ça ?


     




    Tu peux utiliser les préférences pour stocker une valeur sur le device, par exemple un boolean qui détermine si c'est le premier lancement ou non. Si c'est le cas tu ajoutes tes valeurs.




    Oui dans la solution que j'ai faite au début, c'est ce que j'ai fait. Je ne récupère les données qu'au premier lancement de l'app.


     




    Une autre solution, peut-être un peu tordue pour l'instant, mais à  garder en tête pour le futur, c'est l'utilisation de la public database de CloudKit.


    Elle est accessible sans compte iCloud et peut permettre des mises à  jours des données initiales après coup.

    Cela peut-être une solution si tu as des données initiales conditionnées par la langue ou la localisation de l'utilisateur.


    Bon le gros inconvénient, c'est qu'on est sur du iOS 8 only.




    Oui j'y ai également pensé néanmoins il faut un accès à  internet pour aller écrire ou chercher des données. Mais oui c'est une bonne solution pour gérer une BDD à  plusieurs par exemple etc.


     


    Merci à  tous pour vos réponses cela m'a bien aidé dans mon projet. Merci beaucoup

  • Pas mal de réponses ont été données ici, d'autres idées par ici : http://blog.atwam.com/blog/2012/05/11/multiple-persistent-stores-and-seed-data-with-core-data/


     


    En gros ce que tu cherche s'appel l'initial seed. Tu as pas mal d'article et d'idée sur le sujet, regarde entre autre les vidéos WWDC se rapportant à  Core Data. Cela avait été abordé (je ne sais plus quand).


     


    L'idée là  plus intéressante est d'embarquer dans ton app un fichier de base CoreData avec tes données de démo. Le fait que ce soit des fichiers CoreData te simplifie la vie pour le chargement.


     


    Attention à  désactiver toutes les optimisations SQLite si tu retiens se format pour la création du seed. Sinon tu va avoir 2 ou 3 fichier pour la base ce qui peut poser problème.


  • Joanna CarterJoanna Carter Membre, Modérateur

    @yoann - l'article dans le lien me paraà®t trop compliqué pour ce qui intéresse Benjo. Il n'a que demandé comment initialiser une base de données et la solution est bien facile et pratique - la créer dans un petit app OS X et ajouter le fichier SQLite au projet.  8--)


     


    Mais, quand même, je rangerai l'article pour le futur 




  • @yoann - l'article dans le lien me paraà®t trop compliqué pour ce qui intéresse Benjo. Il n'a que demandé comment initialiser une base de données et la solution est bien facile et pratique - la créer dans un petit app OS X et ajouter le fichier SQLite au projet.  8--)


     


    Mais, quand même, je rangerai l'article pour le futur 




     


    C'est à  dire que l'exemple de lien donné est la bonne solution.


     


    Quand on a des modèles de données très simple ont peut s'amuser à  bidouiller en re-créan une autre base avec le même modèle ou presque, mais c'est débile, ça fait faire le travail en double et ça fait faire un importer de ta version spécial seed vers la version finale.


     


    La solution recommandé, même si elle demande un peu plus de connaissance CoreData, te demande aucune base supplémentaire et aucun code d'import.


     


    D'autre part, la solution du double store (un en lecture seule pour le seed et l'autre en lecture écriture) est la seule acceptable sur iOS si la base CoreData est à  sauvegarder. En effet, Apple refuse normalement une application qui crée des données de sauvegarde au premier lancement sans ajout de l'utilisateur.


     


    De fait, on est censé utiliser deux persistant store et des configuration pour afficher des données d'exemples qui ne sont pas sauvegardable.

  • Je fais remonter ce sujet pour plusieurs raisons. La première est que j'ai trouvé une petite vidéo qui pourrait être utile à  des gens qui auraient le même problème que moi.


    La seconde est que j'ai un autre léger problème, mais qui est bien embêtant. Dans ma base CoreData, j'utilise des relations. Or, ce n'est pas possible de créer des relations à  partir d'une base de donnée SQLite non ? Et du coup je suis bien embêté pour importer ma base...


     


    Est-ce possible d'importer des données pour les mettre dans mes relations ?


     


    Merci d'avance :)


  • AliGatorAliGator Membre, Modérateur
    Mais pourquoi importer des données de SQLite ? Pourquoi ne pas les importer d'une base CoreData existante ?

    Quand CoreData génère son backing store au format SQLite, il gère tout seul tous ces mécanismes de relations entre les tables qu'il fait à  sa manière (avec des tables d'associations intermédiaires, comme on est obligé de le faire quand on fait de la BDD et non pas du modèle objet comme en CoreData, heureusement que CoreData est là  pour ça d'ailleurs car ça serait bien la galère de le faire à  la main!).
    Donc autant le laisser gérer tout ça tout seul. Tu t'en fous au final que le fichier soit une base SQLite, tu génères ton store avec du code CoreData, tu copies le fichier, et t'as une base initialisée, basta..

  •  


     


    Mais pourquoi importer des données de SQLite ? Pourquoi ne pas les importer d'une base CoreData existante ?

    Parce que mon client me donne des données au format sqlite donc comme la base de donnée est volumineuse, je n'ai pas vraiment le choix...


     


     




    Quand CoreData génère son backing store au format SQLite, il gère tout seul tous ces mécanismes de relations entre les tables qu'il fait à  sa manière (avec des tables d'associations intermédiaires, comme on est obligé de le faire quand on fait de la BDD et non pas du modèle objet comme en CoreData, heureusement que CoreData est là  pour ça d'ailleurs car ça serait bien la galère de le faire à  la main!).

    Donc autant le laisser gérer tout ça tout seul. Tu t'en fous au final que le fichier soit une base SQLite, tu génères ton store avec du code CoreData, tu copies le fichier, et t'as une base initialisée, basta..




     


    Je suis désolé AliGator mais je n'ai pas bien compris la seconde partie de ton message. Pourrais-tu me re-expliquer en détaillant un petit peu plus s'il te plais ?


    Car je sais que CoreData génère son backing store en SQLite et qu'il gère tout seul les histoire de relations et tout qui sont complexes à  gérer en SQLite. Mais après, je ne saisis pas vraiment ce que tu veux dire :/


  • Joanna CarterJoanna Carter Membre, Modérateur

    Parce que mon client me donne des données au format sqlite donc comme la base de donnée est volumineuse, je n'ai pas vraiment le choix...




    Si les données d'initialisation ne sont pas susceptibles à  changements, tu pourrais écrire un petit app OS X pour créer le Core Data Store de ces données et, puis, ajouter le Store à  ton projet.
  • J'ai l'impression que ça part dans tous les sens ici.


     


    Benjo', c'est quoi ton besoin de base ? C'est trouver une méthode de seed ou trouver une méthode de seed pour un format bien précis apporté par ton client ?


     


    En SQLite comme dans n'importe quel format, il est possible de représenter des relations. C'est bien pour cela que ça s'appel un SGBDR (système de gestion de base de donnée relationnelle).


     


    Ton problème, si le format est imposé, c'est simplement de comprendre le schéma SQL d'origine pour être capable de l'interpréter et le convertir en base objet.


     


    Si ce format est imposé par le client, IMHO c'est inutile de se dire de faire une application Mac pour convertir le SQL en CoreData. ça ne t'épargnera pas l'écriture d'un code de conversion et ça te rajoutera une étape de plus, assez lourde et non scriptable, dans la génération d'un IPA.


     


    Si tu dois faire un importeur SQLite, tu le fais dans l'app finale tant qu'à  faire.


     


    La seule raison valable pour s'emmerder à  le faire en deux apps, c'est les perf au premier lancement.


     


    Mais encore une fois, revoir ton besoin réel permettrait de mieux t'aider.


  • BenjoBenjo Membre
    décembre 2014 modifié #19

    Merci à  tous pour vos réponses. Je vais reposer mon problème avec plus de détail.


    J'ai un client. Ce client me demande de lui faire une application avec une base de donnée dans l'app par défaut. Les données me sont fournies au format CSV (voir les deux pièces jointes). Comme vous pouvez le voir, j'ai donc un fichier "Dotations.csv" dans lequel figure tout une liste de dotation avec un code "Article" (ex : 122124). J'ai un autre fichier "Articles.csv". Dedans il y a la liste des article avec leur code.


     


    Dans mon application, j'ai donc une entité "Article" et une entité "Dotations". Dans "Dotations", j'ai une relation One To, vers "Articles". Et j'aimerais, qu'au premier lancement de l'app, je créer mon entité "Article", puis j'ajoute les articles à  mon entité "Dotation".


     


    Quel est le moyen le plus simple et le plus efficace pour faire ça ?


  • Joanna CarterJoanna Carter Membre, Modérateur

    Tu auras plus d'entités que ces deux ; les données ne sont pas normalisées.


     


    En plus tu as :


     


    Magasin


    Fréquence


    Emplacement




  • Merci à  tous pour vos réponses. Je vais reposer mon problème avec plus de détail.


    J'ai un client. Ce client me demande de lui faire une application avec une base de donnée dans l'app par défaut. Les données me sont fournies au format CSV (voir les deux pièces jointes). Comme vous pouvez le voir, j'ai donc un fichier "Dotations.csv" dans lequel figure tout une liste de dotation avec un code "Article" (ex : 122124). J'ai un autre fichier "Articles.csv". Dedans il y a la liste des article avec leur code.


     


    Dans mon application, j'ai donc une entité "Article" et une entité "Dotations". Dans "Dotations", j'ai une relation One To, vers "Articles". Et j'aimerais, qu'au premier lancement de l'app, je créer mon entité "Article", puis j'ajoute les articles à  mon entité "Dotation".


     


    Quel est le moyen le plus simple et le plus efficace pour faire ça ?




     


    Vu que c'est du CSV et non du SQLite, il faut éviter de le parser au premier lancement IMHO, ça sera lent.


     


    Ici l'option la plus simple est effectivement d'avoir une app OS X qui dispose du même schéma de base CoreData qui te fasse l'import, ensuite tu n'as plus qu'à  copier la base généré dans ton app iOS.


     


    Attention à  bien couper toutes les options de perf CoreData sur l'app OS X pour être certain de n'avoir qu'un seul fichier SQLite.

  • Perso, je te conseillerais de ne pas utiliser core data pour cette partie.


     


    Ensuite, ça dépend de la taille de ta base, etc. (cf. les remarques de Yoann pour la vitesse de chargement)


     


    Je créerais une classe (singleton) MyArticlesManager et pareil pour l'autre, que tu connectes à  tes fichiers csv au lancement de l'application et qui fournit les informations dont tu auras besoin (tous les articles, etc.).


     


    Pour faire le lien entre cette classe et ton modèle CoreData (car j'imagine que tu auras d'autres entités, auxquelles tu veux connecter tes articles, etc.), tu mets des attributs "string" (avec le nom de l'article) ou "transformable" de type array si tu as besoin d'avoir des relations one-to-many.


     


    En fait, j'ai été confronté au même problème que toi, j'ai d'abord mis en place une solution 100% core data mais j'ai trouvé que c'était trop galère à  maintenir : au cas où tes données de base changent. Je te renvoie au message http://forum.cocoacafe.fr/topic/13123-question-darchitecture-sur-core-data-et-des-entités-en-arbre/#entry125743


  • Joanna CarterJoanna Carter Membre, Modérateur

    Vue que le modèle dans les fichiers n'est pas normalisé, je suis d'accord avec Yoann. Il faut, en premier lieu, préparer les données, en supprimant les doublons et les clés significatives afin qu'on ne travaille qu'avec les données "nettes et propres"


  • AliGatorAliGator Membre, Modérateur

    Perso, je te conseillerais de ne pas utiliser core data pour cette partie.
    [...]
    Je créerais une classe (singleton) MyArticlesManager et pareil pour l'autre, que tu connectes à  tes fichiers csv au lancement de l'application et qui fournit les informations dont tu auras besoin (tous les articles, etc.).

    Je ne suis pas d'accord, je trouve cela TRES dommage de se passer de la puissance de CoreData pour cela.

    1) Avec ta méthode, le parsing du CSV embarqué dans le bundle va se faire sur chaque iPhone, augmentant potentiellement de façon significative le temps de premier lancement de l'application (ne pas oublier que le premier lancement est aussi la première impression de ton application sur l'utilisateur, si elle met 3 plombes à  démarrer " même si pour les lancements suivants elle ne sera pas aussi longue, mais ça l'utilisateur ne le sait pas encore " ça donne d'emblée une mauvaise impression)

    2) Tu embarques l'algo d'import / conversion du CSV en objets modèles inutilement dans ton app

    3) En te passant de CoreData, tu te passes aussi de sa puissance, en particulier des FetchRequest que tu peux faire (si tu veux faire des requêtes pour récupérer certains objets selon certaines conditions ou autre, comme récupérer un article d'ID donné, ou toutes les dotations à  une date donnée, etc), de la gestion des relationShips et des faults, des NSFetchedResultsController, de la possibilité d'avoir plusieurs contextes / MOC pour faire par exemple des modales de modification de données qui soient annulables, etc...
    Bref tous les avantages qui facilitent la réalisation d'une application typiquement orientée CRUD en faisant limite tout le boulot pour toi.

    Je ne vois pas ce que ça apporterait comme inconvénient qui justifierai de s'en passer.

    4) En faisant un utilitaire séparé qui prend ton CSV et construit ta base CoreData en conséquence, tu fais la conversion en amont, et tu sépares surtout l'intelligence de conversion CSV->MDD de toute le reste de ton application. Ainsi, si par exemple ton client un jour décide de t'envoyer les données dans un autre format que CSV, ou change l'ordre de ses colonnes, bref change le fichier de données qu'il te fournit en entrée, tu auras juste à  adapter ta moulinette de conversion, sans toucher à  une seule ligne de ton app. C'est la séparation des responsabilités, un des concepts de base de la POO.
  • @Ali : Je suis d'accord avec tout ce que tu dis.


     


    J'ai donné ce conseil pour le cas d'utilisation suivant :


     


    -> tes fichiers de seed changent


    -> tu recrées une seed core data


     


    Ce sera alors galère de devoir gérer l'intégration de l'ancienne seed et de la nouvelle seed, c'est-à -dire de remplacer l'ancienne seed dans les données de l'utilisateur par la nouvelle seed. Et ce sera d'autant plus complexe que la structure de ta seed est complexe et que la seed est très imbriquée dans les données de ton utilisateur.


  • Je vais essayer un utilitaire pour me créer direct un store CoreData et l'importer dans mon projet. Je vous dirais ce que j'en ai pensé, si cela fonctionne correctement et des liens si cela fonctionne. Merci beaucoup.


  • J'ai suivi vos conseils et j'ai téléchargé un petit utilitaire nommé CoreData editor. Il importe les fichiers .csv et peut créer des relations avec des tables c'est très pratique et ça fonctionne très bien. J'ai donc utilisé cet utilitaire et je m'en suis sortit.


    Merci beaucoup pour toutes vos réponses ça m'a bien aidé.


Connectez-vous ou Inscrivez-vous pour répondre.