Application de gestion d'une imprimerie

Bonjour à toutes et à tous.

Faisant suite à ma présentation, je poste ici pour vous présenter mon application. Lien GitHub https://github.com/Akiten/Imprimerie

xCode 10, Swift 5

Il s'agit d'un application qui permet de suivre les travaux d'une imprimerie.
Elle sera somme toute assez simple.

Je vais la baser sur CoreData, qui me paraît plus simple à utiliser pour ce que je veux faire, pour la persistance des données. Les tables n'ont pas de jointures car ça ne sert pas complexifier le modèle, il n'y aura aucune suppression de données de base, et les seuls liens possibles pouvant exister sont les clients, et les parutions.

Celle-ci se base donc sur une fenêtre principale décomposée en quatre parties.
La première partie est une Toolbar pour la Window principale façon xCode (en cours d'implantation)
La deuxième partie est une outlineView à gauche pour les données à gérer
La troisième partie, au centre, présente les données sous forme de tableView variable et les tableaux de bords (seules les tableView sont implémentés pour le moment)
La quatrième partie va se trouver à droite comme xCode (l'inspecteur d'attributs), elle servira pour mettre à jour les données (je ne suis pas adepte de la mise à jour de données dans des listes). Pas encore implémentée.

Je suis plutôt content de ce que j'ai réussi à faire, mais je suis convaincu que c'est loin d'être parfait, et qu'il y a des choses à améliorer, voire à refaire.

Il y a pas mal de code trouvé sur internet et copié/collé, ce qui n'est pas très bon, mais j'ai commencé l'apprentissage il y a peu de temps, fin avril, et je dois quand même avancer.

En plus, il faut que j'apprenne à bosser avec gitHub :smiley:

Pour terminer, je vais vous livrer ici deux questions que je me pose
1) Storyboard ou xib ?
2) Je n'ai pas utilisé de customView pour les 3 parties principales, et c'est sûrement une erreur.

Je suis ouvert à toute remarque, critique, conseil, même si je dois reprendre à zéro.
Merci de m'avoir lu, et merci d'avance pour vos retours :wink:

Akiten.

Mots clés:

Réponses

  • AkitenAkiten Membre

    @Akiten a dit
    Je vais la baser sur CoreData, qui me paraît plus simple à utiliser pour ce que je veux faire, pour la persistance des données.

    Petite précision, les entités sont définies dans le xcdatamodel, mais les données sont en dur dans les classes pour le moment.

  • Joanna CarterJoanna Carter Membre, Modérateur
    1 août modifié #3

    1) à mon avis, utilises les storyboards
    2) pourquoi tu crois qu'il faut utiliser les customViews?

    Coté données, avec CoreData, on ne parle pas des tables, on parle plutôt des entités, parce que les jointures ne sont pas créées dans la même manière qu'avec les BDD.

    e.g. (BDD)

    Facture
      ID
      Numéro
      Client
      Adresse
    
    LigneDeFacture
      ID
      FactureID
      Produit
      Quantité
    

    e.g. (CoreData)

    Facture
      Numéro
      Client
      Adresse
      Lignes (relationship 1 -> n, delete cascade)
    
    LigneDeFacture
      Produit (relationship n -> 1, delete nullify)
      Quantité
    

    C'est CoreData qui s'occupe de l'entretien des primary keys et foreign keys ; il ne faut pas définir les identifiants dans la modèle.

    En plus, il faut absolument créer les jointures entre les entités. Sinon, tu violes le principe de SPOD (single point of definition) - ce que tu devrais savoir bien de tes expériences avec SQL.

  • AkitenAkiten Membre
    1 août modifié #4

    Johana bonjour.
    Merci pour ta réponse.

    Ok pour le storyboard, ça ne dérange pas, je m'habitue à son utilisation.

    Pour les customView et les xib, voilà mon interrogation actuelle.
    La partie centrale est pour le moment réservée à une tableView dynamique pour lister les données. Ok, mais je vais avoir la partie rapport qui va s'ajouter. Ce ne sera pas sous forme de liste, mais des documents PDF qui serviront au reporting.
    Pour la partie droite, je vais avoir toute la partie gestion des données. Donc dynamique aussi.
    Voilà pourquoi je me disais pourquoi pas deux customView basées sur des XIB différents appelés selon le contexte.
    partie centrale : 1 XIB pour la tableView, autant de XIB que de rapports
    partie droite : autant de XIB que de rapports. La partie droite étant à ce moment là cachée car inutile.

    Pour le CoreData, OK. Je n'avais pas bien cerné cette partie du xcdatamodel? Comme je suis quelqu'un de respectueux, je ne violerais pas ce principe. Cependant, je ne gère que des meta données qui ne nécessitent pas de fortes contraintes.

    Bien à toi.
    Akiten.

  • PyrohPyroh Membre

    Salut,

    J'ai regardé un peu ton application et je rejoins Johana dans ce qu'elle dit.
    Pour la partie Core Data déjà c'est bien de ne violer personne. Sinon plus sérieusement tu dois vraiment oublier l'aspect DB du framework ce n'est pas ce qui est important et oserai-je dire un détail d'implémentation (encore plus vrai avec l'adaptateur CloudKit de la cuvée 2019). Tu dois vraiment considérer tes entités comme des classes et voir plus un diagramme de classe que de l'entité-association chère aux experts DB.

    Apple parle d'Object Graph dans sa documentation. Tu dois alors partir dans ce sens : construit un diagramme avec des connexions entre les objets, des dépendances, des règles de surpressions, etc...

    Pour les vues c'est à toi de voir tu n'es pas obligé d'utiliser les xib tu peux créer des vues indépendantes dans ton storyboard et les instancier au besoin j'ai créé un petit protocol pour m'y aider :

    import Cocoa
    
    public protocol StoryboardInstantiable: NSObject {
        static var storyboardName: NSStoryboard.Name { get }
        static var sceneIdentifier: NSStoryboard.SceneIdentifier? { get }
        static var bundle: Bundle? { get }
    
        static func instantiate() -> Self
    }
    
    extension StoryboardInstantiable {
        public static var sceneIdentifier: NSStoryboard.SceneIdentifier? { return nil }
        public static var bundle: Bundle? { return nil }
    
        public static func instantiate() -> Self {
            if let id = self.sceneIdentifier {
                return NSStoryboard(name: self.storyboardName, bundle: self.bundle).instantiateController(withIdentifier: id) as! Self
            } else {
                return NSStoryboard(name: self.storyboardName, bundle: self.bundle).instantiateInitialController() as! Self
            }
        }
    }
    

    Pour l'utiliser c'est assez simple :

    • Ta scene doit avoir un *Storyboard ID" dans Interface Builder
    • Tu dois créer une sous-classe de NSViewController ou NSWindowController et changer le type du contrôleur de ta scène pour cette sous-classe.
    • La sous classe doit implémenter StoryboardInstantiable.
    • La propriété storyboardName est obligatoire est renvoie le nom du storyboard.
    • La propriété sceneIdentifier est optionnelle mais doit typiquement renvoyer le Storyboard ID de la scene que tu veux instancier. Si laissé à nil c'est la scene initiale qui sera instanciée
    • La propriété bundle doit renvoyer le bundle dans lequel chercher le storyboard. Elle est optionnelle et tu ne dois l'utiliser que si tu vas chercher le storyboard en dehors du bundle de l'application. La plupart du temps elle reste à nil.

    Pour te donner un exemple dans une application j'ai une sous classe de NSWindowController nommée ColorDetailWindowController qui utilise ce protocole. Son Storyboard ID est ColorDetailPanel et tout se trouve dans un storyboard nommé Main. Voici l'implémentation de StoryboardInstantiable dans ce cas :

    class ColorDetailWindowController: NSWindowController, StoryboardInstantiable {
        static var storyboardName: NSStoryboard.Name { "Main" }
        static var sceneIdentifier: NSStoryboard.SceneIdentifier? { "ColorDetailPanel" }
    
        // MARK: -
        // MARK: Actions
        @IBAction func closePanel(_ sender: Any) {
            self.window?.close()
        }
    }
    

    Dès que j'ai besoin d'un tel panel j'utilise

    let detailPanelCtrl = ColorDetailWindowController.instantiate()
    

    et je n'ai plus qu'à l'utiliser !

    J'utilise ça pour presque toutes mes vues c'est assez pratique.

  • PyrohPyroh Membre

    Salut,

    J'ai regardé un peu ton application et je rejoins Johana dans ce qu'elle dit.
    Pour la partie Core Data déjà c'est bien de ne violer personne. Sinon plus sérieusement tu dois vraiment oublier l'aspect DB du framework ce n'est pas ce qui est important et oserai-je dire un détail d'implémentation (encore plus vrai avec l'adaptateur CloudKit de la cuvée 2019). Tu dois vraiment considérer tes entités comme des classes et voir plus un diagramme de classe que de l'entité-association chère aux experts DB.

    Apple parle d'Object Graph dans sa documentation. Tu dois alors partir dans ce sens : construit un diagramme avec des connexions entre les objets, des dépendances, des règles de surpressions, etc...

    Pour les vues c'est à toi de voir tu n'es pas obligé d'utiliser les xib tu peux créer des vues indépendantes dans ton storyboard et les instancier au besoin j'ai créé un petit protocol pour m'y aider :

    import Cocoa
    
    public protocol StoryboardInstantiable: NSObject {
        static var storyboardName: NSStoryboard.Name { get }
        static var sceneIdentifier: NSStoryboard.SceneIdentifier? { get }
        static var bundle: Bundle? { get }
    
        static func instantiate() -> Self
    }
    
    extension StoryboardInstantiable {
        public static var sceneIdentifier: NSStoryboard.SceneIdentifier? { return nil }
        public static var bundle: Bundle? { return nil }
    
        public static func instantiate() -> Self {
            if let id = self.sceneIdentifier {
                return NSStoryboard(name: self.storyboardName, bundle: self.bundle).instantiateController(withIdentifier: id) as! Self
            } else {
                return NSStoryboard(name: self.storyboardName, bundle: self.bundle).instantiateInitialController() as! Self
            }
        }
    }
    

    Pour l'utiliser c'est assez simple :

    • Ta scene doit avoir un *Storyboard ID" dans Interface Builder
    • Tu dois créer une sous-classe de NSViewController ou NSWindowController et changer le type du contrôleur de ta scène pour cette sous-classe.
    • La sous classe doit implémenter StoryboardInstantiable.
    • La propriété storyboardName est obligatoire est renvoie le nom du storyboard.
    • La propriété sceneIdentifier est optionnelle mais doit typiquement renvoyer le Storyboard ID de la scene que tu veux instancier. Si laissé à nil c'est la scene initiale qui sera instanciée
    • La propriété bundle doit renvoyer le bundle dans lequel chercher le storyboard. Elle est optionnelle et tu ne dois l'utiliser que si tu vas chercher le storyboard en dehors du bundle de l'application. La plupart du temps elle reste à nil.

    Pour te donner un exemple dans une application j'ai une sous classe de NSWindowController nommée ColorDetailWindowController qui utilise ce protocole. Son Storyboard ID est ColorDetailPanel et tout se trouve dans un storyboard nommé Main. Voici l'implémentation de StoryboardInstantiable dans ce cas :

    class ColorDetailWindowController: NSWindowController, StoryboardInstantiable {
        static var storyboardName: NSStoryboard.Name { "Main" }
        static var sceneIdentifier: NSStoryboard.SceneIdentifier? { "ColorDetailPanel" }
    
        // MARK: -
        // MARK: Actions
        @IBAction func closePanel(_ sender: Any) {
            self.window?.close()
        }
    }
    

    Dès que j'ai besoin d'un tel panel j'utilise

    let detailPanelCtrl = ColorDetailWindowController.instantiate()
    

    et je n'ai plus qu'à l'utiliser !

    J'utilise ça pour presque toutes mes vues c'est assez pratique.

  • AkitenAkiten Membre

    Merci Pyroh.
    Ça me sera très utile pour la suite.
    Mais pour le moment, j'implémente une Custom View et les XIB.
    Ça me semble plus approprié pour l'instant.
    En même temps, j'apprends à les faire fonctionner.

    Pour le Core Data, ok pour vos remarques.

    Akiten

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