NSTableView et Core Data

2»

Réponses

  • Non désolé j'ai l'erreur suivante dans mon AppDelegate: /AppDelegate.m:134:34: Property 'textfFieldDevisDate' not found on object of type 'id'


  • Tu peux poster la dernière version du projet ? C'est dur sans voir ce que tu fais.


  • Voilà  un zip


  • ça fait bizarre de voir un nib sans bindings... mais encore plus drôle de voir un projet sans fichier projet! :)  Pourrais-tu joindre ton fichier .xcodeproj, que je voie à  quoi tout ça ressemble une fois assemblé? 


  • Voici le dossier au grand complet


  • berfisberfis Membre
    janvier 2014 modifié #37

    Tu as pratiquement tout entre les mains pour faire une application avec une seule fenêtre et pratiquement sans une ligne de code. Mais il te faut passer sous trois fourches caudines...


     


    La première est le pattern MVC.


     


    La datasource masque quelque peu ce pattern parce que tu peux flanquer ces méthodes n'importe où, dans ton cas dans l'appDelegate. Celui-ci comprend déjà  une vingtaine de @properties, et autant de méthodes. Ce n'est pas le rôle de l'appDelegate: celui-ci reçoit les notifications de l'application, comme "j'ai fini de me lancer", "la dernière fenêtre vient d'être fermée", etc. Au final, assez peu de messages.


     


    Ton application ne possède aucun contrôleur. Résultat, l'appDelegate mélange modèle, parce qu'il accède aux données et tripatouille les entités Core Data, vue, parce qu'il change directement les champs et fait apparaà®tre et disparaà®tre des fenêtres (peu utiles d'ailleurs) et contrôleur, qu'il remplace totalement " en fait, il remplace tous les contrôleurs.


     


    La deuxième est d'apprendre ce qu'est vraiment Core Data.


     


    Tu es en train de tenter de faire son boulot (et assez mal, à  vrai dire). Ton appDelegate crée des entités, puis fait des fetch pour les récupérer dans le contexte, attribue lui-même les valeurs dans le dos de l'utilisateur, tire des liens entre les entités...


     


    Core Data est un framework agréable à  utiliser " quand on commence à  comprendre ce qu'il représente: une possibilité de créer, gérer et détruire des objets, d'établir des liens parfois complexes entre eux, et de finalement enregistrer ce graphe d'objets dans un fichier, en veillant à  la cohésion du modèle.


     


    D'après ce que j'ai cru comprendre, ton application ne fait que cela: créer des entités et les relier entre elles. Or, tu n'as pas défini la moindre "relationship" dans tes entités.


     


    La troisième fourche, c'est la "magie" des bindings.


     


    En fait, il n'y a rien de magique là -dedans, mais ça t'évite d'écrire des masses de "glue code" pour transférer tes valeurs, par exemple controlTextDidChange: if ([leControle isEqualTo: monChamp]) maProprieteNumerique = [[monChamp stringValue]integerValue] et de vérifier si le résultat correspond à  un nombre. Dans IB, tu cliques sur ton champ, tu lui flanques un numberFormatter, et dans le champ Value de l'inspecteur des bindings, tu mets maProprieteNumerique. Terminé! Si l'utilisateur tape une valeur, ta propriété est modifiée. Si la propriété change, le champ se met à  jour automatiquement. Le mot-clé ici est KVO/KVC.


     


    Donc en résumé :


     


    1 - Définis clairement ton modèle: ce qui EST quelque chose et ce qui A quelque chose. Par exemple une table EST un meuble et elle A quatre pieds, un tiroir, etc. Puis redessine ton graphe Core Data en t'inspirant du monde réel.


     


    2 - Vide ton appDelegate et crée des NSObjectControllers, NSArrayControllers pour faire le lien entre tes fenêtres et Core Data.


     


    3 - Finis de vider ton appDelegate (et tes contrôleurs) en utilisant les bindings. Ce n'est pas plus complexe à  entretenir qu'un code embrouillé, et s'ils existent sous MacOS profites-en, les gens d'iOS n'ont pas cette chance (mais bon, leur interface n'est pas la même).


     


    Pour faire bonne mesure (mais la doc c'est pas moi, c'est lui):


     


    MVC : https://developer.apple.com/library/ios/documentation/general/conceptual/CocoaEncyclopedia/Model-View-Controller/Model-View-Controller.html


    Core Data : https://developer.apple.com/library/mac/documentation/cocoa/conceptual/coredata/cdProgrammingGuide.html


    Bindings : https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaBindings/CocoaBindings.html


    KVO/KVC : https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueCoding/Articles/KeyValueCoding.html#//apple_ref/doc/uid/10000107i


     


    Bonne lecture et courage !


  • J'ai refais un test avec un ArrayController et j'ai enfin réussi voici un zip ou tu me dira si ça va mieux que avant


     


  • Ok. Est-ce que tu peux me dire (sans utiliser un seul terme de programmation, en langage ordinaire) ce que tu veux faire exactement avec ces entreprises et ces devis ? 


  • Je veux une colonne ou y a les entreprises puis quand on sélectionne une entreprise la colonne devis se remplis puis quand on clique sur un devis on à  le contre rendu qui aparait


  • berfisberfis Membre
    janvier 2014 modifié #41


    Je veux une colonne ou y a les entreprises puis quand on sélectionne une entreprise la colonne devis se remplis puis quand on clique sur un devis on à  le contre rendu qui aparait




     





    Je veux une colonne ou y a les entreprises puis quand on sélectionne une entreprise la colonne devis se remplis puis quand on clique sur un devis on à  le contre rendu qui aparait




    Donc dans ta fenêtre, il y a une tableView à  gauche où tu peux ajouter des entreprises (master). Une autre tableView contenant les devis (un autre master). Et une view qui sert de detail pour chaque devis, juste?


    Question : Une entreprise peut avoir plusieurs devis, ou un seul à  la fois? Ou un devis a-t-il une entreprise chargé du devis?

  • Une entreprise peut avoir plusieurs devis mais un devis n'appartient qu'à  une seule entreprise.


     


     




     


    Donc dans ta fenêtre, il y a une tableView à  gauche où tu peux ajouter des entreprises (master). Une autre tableView contenant les devis (un autre master). Et une view qui sert de detail pour chaque devis, juste?




     


    Après j'aurais des fenêtres indépendantes pour les enregistrements mais sinon oui c'est tout pour le moment.


  • berfisberfis Membre
    janvier 2014 modifié #43

    Voilà , essaie ça. C'est un squelette minimal, et ça demanderait un peu de polissage (sortDescriptors pour trier les colonnes par exemple).


     


    Aucun sous-classement Core Data, c'est inutile pour l'instant.


    Par contre, je me suis trompé sur le "zéro ligne". Il y en a deux.


     


    Bonne lecture.


  • J'ai regardé ton code et ben en faites tu as tout mit dans le AppDelegate hors que tu m'as dit de séparer et je vais toujours me retrouver avec au moins 5 fenêtres dans le AppDelegate et du coup plein de ligne de code non?


  • berfisberfis Membre
    janvier 2014 modifié #45

    Je n'ai strictement rien mis dans le appDelegate! C'est le template de Core Data qui remplit ça...


     


    En fait, tout le code fourni sert à  créer une "base de données" qui se trouve dans le fameux dossier Application Support. Si tu crées une Document-based Core Data Application, tu verras qu'il n'y a rien dans le appDelegate, et pratiquement rien dans le NSPersistentDocument.m, qui hérite de NSDocument pour pouvoir travailler avec un NSManagedContext.


     


    J'ai juste créé le DevisController (dérivé de NSArrayController) pour ajouter ceci dans le .m:



    @implementation DevisController

    - (IBAction)add:(id)sender
    {
    NSManagedObject *mo = [self newObject];
    [mo setValue: self.entreprises.selectedObjects [0] forKey:@entreprise];
    }

    Car lorsque tu ajoutes un devis, il faut qu'il sache à  quelle entreprise le rattacher: ce sera celle qui est sélectionnée dans la liste des entreprises. Tu peux considérer un NSManagedObject comme un NSDictionary: tu associes des valeurs à  des clés qui ne sont rien d'autres que les attributes de l'entité Core Data. Si j'associe l'entreprise à  la relationship ( une autre sorte d'attribute) "entreprise" du devis, Core Data établit la relation inverse: il attribue le devis à  l'entreprise, puisque j'ai défini une inverse relationship dans mon modèle.


     


    Tu remarqueras que le bouton + des devis est désactivé si aucune entreprise n'est sélectionnée, car sa variable Enabled est reliée dans IB à  la propriété canRemove du contrôleur des entreprises. Si le contrôleur d'entreprise n'a aucune sélection, il ne peut la retirer, donc son canRemove est FALSE-- et donc le Enabled du bouton + aussi. canRemove est Key-Value Compliant, c'est une propriété observable. Enabled est un Key-Value Observer, les deux sont reliés: c'est le binding.


     


    Pour reprendre ta remarque :


     


    1 - je n'ai rien mis dans le appDelegate, je n'ai même pas ouvert le document.


     


    2 - j'ai séparé le Modèle (Core Data) les Contrôleurs (Entreprises et Devis) et les Vues (les NSTableViews).


     


    3 - je n'ai qu'une seule fenêtre. Si tu veux en ajouter quatre, libre à  toi, mais je te conseille plutôt d'en faire des sheets sur la fenêtre principale, histoire de garder un nib facile à  gérer et de respecter les HIG. L'utilisateur n'a pas à  se retrouver à  la chasse au canard avec des fenêtres qui s'ouvrent dans tous les coins chaque fois qu'il clique sur un bouton.


     


    4 - si tu veux écrire plein de lignes de code, ne t'en prive pas, mais mes deux lignes font plus et mieux que toutes celles que tu es rédigées jusque-là . Le problème, c'est que plus on écrit de code, plus on risque d'introduire d'erreurs...


     


    Pour l'instant, ton application fonctionne comme tu me l'as décrite: Tu crées des entreprises, tu crées des devis que tu peux attribuer aux entreprises existantes, tu peux les supprimer (si tu supprimes une entreprise, tu peux choisir de supprimer les devis associés ou les garder quelque part, tu définis ça dans les Delete Rules de la relationship. Tu peux même changer les noms (double-clique sur la ligne pour l'éditer) et attribuer un numéro de devis (attention à  cette finesse: le Text Field a un Number Formatter associé, si tu n'en mets pas, Core Data va te jeter).


     


    Franchement, pour deux lignes de code, qui dit mieux... Merci qui? Merci les bindings, merci Core Data. Il y a des ingénieurs qui ont bossé dur pendant des années pour ça, alors pourquoi vouloir réinventer la roue?


     


    Dernier mot: pour savoir où ajouter deux lignes, et pour savoir quand il faut "laisser faire les frameworks", cela m'a tout de même pris deux ans de documentation, d'exemples, de tutos, de bouquins et de code foireux durant mes heures libres (la nuit donc). Je fais partie des dilettantes qui apprennent plutôt lentement, si ça peut te rassurer. Mais tu n'échapperas pas au RTFM (Read The Fucking Manual)...


     


    Et si l'anglais te rebute, les livres d'Aaron Hillegass existent en français, ainsi que quelques autres.


     


    Allez, bon courage et bonne lecture !


  • AliGatorAliGator Membre, Modérateur
    janvier 2014 modifié #46


    Tu peux considérer un NSManagedObject comme un NSDictionary: tu associes des valeurs à  des clés qui ne sont rien d'autres que les attributes de l'entité Core Data.

    L'utilisation de setValue:forKey: sur un NSManagedObject n'a rien à  voir avec une potentielle similitude avec un NSDictionary, ce n'est que de l'utilisation de KVC au lieu d'une affectation directe, chose qui peut être utilisée sur n'importe quel NSObject.


    Les NSManagedObjects sont des NSObject comme les autres. Je n'ai jamais compris pourquoi les gens préfèrent utiliser le KVC sur les ManagedObject plutôt que l'affectation directe de la propriété (Sans doute parce que plusieurs exemples Apple utilisent le KVC dans leurs sample codes génétiques ? Mais c'est moins lisible et surtout plus sujet à  des erreurs de frappes non détectées à  la compilation qui ne feraient crasher qu'à  l'exécution, donc à  mon avis une mauvaise idée)


    Il est tellement plus simple, plus lisible et surtout plus sûr (car l'auto complétion va vous assurer de ne pas faire de faute de frappe et le compilateur ça vous signaler si vous en faites une de toute façon plutôt que de masquer le problème et crasher au Runtime comme le fait le KVC) d'utiliser le setter dédié ou la notation pointée pour ça... comme on ferait pour n'importe quel NSObject (un NSManagedObject n'a pas de raison d'être une exception)

    mo.entreprise = self.entreprises.selectedObjects[0];
  • berfisberfis Membre
    janvier 2014 modifié #47

    AliGator,


     


    Je suis d'accord, je voulais garder l'exemple hyper-simple. Maintenant, sous-classer les entités dans un deuxième temps est certainement la chose à  faire, pour quatre raisons:


    1. La syntaxe est plus claire


    2. Cela permet un bien meilleur contrôle à  la compilation (c'est vite fait de se tromper dans l'orthographe d'un nom de clé)


    3. C'est plus rapide (de peu, mais si on fait des boucles sur 50'000 objets, ça va se sentir)


    4. On peut ajouter des propriétés (readonly, p.ex) à  l'entité dérivée sans avoir besoin de modifier son modèle et de devoir faire du versioning,


     


    Quant aux pratiques KVO/KVC, je les ai même trouvées chez Hillegass, c'est dire si c'est répandu. Et à  propos du dictionary, la doc que je me contentais de citer, la voici in extenso:


    In some respects, an NSManagedObject acts like a dictionary


    https://developer.apple.com/library/mac/documentation/cocoa/conceptual/coredata


  • CéroceCéroce Membre, Modérateur

    Je n'ai jamais compris pourquoi les gens préfèrent utiliser le KVC sur les ManagedObject plutôt que l'affectation directe de la propriété.

    Sans doute parce que ça permet de modifier les champs de l'entité sans créer de sous-classe de NSManagedObject. Ceci dit, je suis 100% d'accord avec toi, je ne vois pas l'intérêt.
  • CéroceCéroce Membre, Modérateur

    1. La syntaxe est plus claire
    2. Cela permet un bien meilleur contrôle à  la compilation (c'est vite fait de se tromper dans l'orthographe d'un nom de clé)
    3. C'est plus rapide (de peu, mais si on fait des boucles sur 50'000 objets, ça va se sentir)
    4. On peut ajouter des propriétés (readonly, p.ex) à  l'entité dérivée sans avoir besoin de modifier son modèle et de devoir faire du versioning,

    5. On associe souvent du code à  une entité.
Connectez-vous ou Inscrivez-vous pour répondre.