[Résolu] Créer une classe pour le model coreData

busterTheobusterTheo Membre
octobre 2015 modifié dans Dev. iOS, watchOS, tvOS #1

Bonjour, suite à  ce post sur lequel je reviendrai plus tard, je suis amené, comme j'ai encore du mal (sur Instruments) à  identifier les leaks et les problèmes de mémoire qui gonfle sans arrêt (retain...), je me consacre donc à  faire en premier lieu, les corrections laissées en suspend auxquelles je pense.


 


Tout d'abord, revoir mon model/CoreData. J'ai au moins 250 champs, je dois donc scinder ma base en plusieurs bases.


 


Et, ce faisant, j'en profite au passage pour externaliser les codes re récup/save/delete de coreData.


J'ai donc créé une classe qui me sert à  faire ces requêtes.


 


Je travaille pour l'instant sur la récupération des données.


 


Cela marche super bien, mais je tente d'en faire plus, et je n'y parviens pas.


 


Voilà  ce qui fonctionne :



// MARK: - Recup CoreData

func confCoreDate() {
let fetchEntity: FetchEntity = FetchEntity()
let fetchResults: NSArray = fetchEntity.getFetchResults("Patients", predicateField: listeDesVariables.nomString)

let singlePatient: Patients = fetchResults.lastObject as! Patients

// 1- Début de la partie à  intégrer dans la classe
if let e1L1Regard = singlePatient.e1L1Regard {
listeDesVariables.e1L1RegardString = singlePatient.e1L1Regard!
} else {
listeDesVariables.e1L1RegardString = ""
}
if let e1L1Sourire = singlePatient.e1L1Sourire {
listeDesVariables.e1L1SourireString = singlePatient.e1L1Sourire!
} else {
listeDesVariables.e1L1SourireString = ""
}
// 2- Fin de la partie à  intégrer dans la classe
}

Et la classe en question :



import UIKit
import CoreData

let appDel:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext!

class FetchEntity {

func getEntity(entityName: String) -> NSEntityDescription {
let entityDescription = NSEntityDescription.entityForName(entityName, inManagedObjectContext: context)
return entityDescription!
}

func getFetchResults(entityName: String, predicateField: String) -> NSArray {
let fetchRequest: NSFetchRequest = NSFetchRequest()
fetchRequest.entity = getEntity(entityName)
fetchRequest.returnsObjectsAsFaults = false

let predicate: NSPredicate = NSPredicate(format: "nom == '\(predicateField)'")
fetchRequest.predicate = predicate
let fetchResults: NSArray = context.executeFetchRequest(fetchRequest, error: nil)!

return fetchResults
}
}

Jusque là , cela fonctionne parfaitement.


J'imagine que certaines remarques vont poindre, et elles seront les bienvenues.


Je pense par exemple, que je devrais mettre ( func getEntity() ) en private


 


Bref, j'en viens à  ma question.


En gros je ne sais pas comment intégrer ce qui se trouve entre "// 1"- et "// 2-" dans le premier code.


 


J'ai tout essayé et je galère.


 


J'ai fait dans la classe, un truc du genre :



func getLvd(model: String, lvd: String, fetchResults: NSArray) {
let singlePatient: Patients = fetchResults.lastObject as! Patients

if let singleModel = singlePatient."\(model)" {
listeDesVariables.lvd = singlePatient.singleModel!
} else {
listeDesVariables.lvd = ""
}
}

Je ne sais pas :


A- Comment transmettre un e1L1Regard qui est un des attributs dans mon modèle  (@NSManaged var e1L1Regard: String?)


 


J'ai essayé de transmettre un NSManaged, un String,...


 


B- Comment cibler l'attribut comme dans (singlePatient.e1L1Regard!), mais faire cela dans la classe.


 


Voilà , j'espère que c'est assez clair.


Je suis sûr que c'est évident pour les barmen, mais moi, je suis encore en cuisine... 8--)


 


 


Merci d'avance.


Réponses

  • busterTheobusterTheo Membre
    octobre 2015 modifié #2

    Je me répond à  moi-même, et pour ceux qui cherchent à  faire la même chose.


    J'y suis parvenu. Je suis énormément gravement joyeusement heureux d'avoir réussi, car cela va me faire gagner au moins 1000 lignes de codes sur tout mon projet. Et j'espère que ça va alléger la mémoire consommée.


     


    Voilà  le code...


     


    La classe :



    import UIKit
    import CoreData

    let appDel:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let context: NSManagedObjectContext = appDel.managedObjectContext!

    class FetchEntity {

    func getEntity(entityName: String) -> NSEntityDescription {
    let entityDescription = NSEntityDescription.entityForName(entityName, inManagedObjectContext: context)
    return entityDescription!
    }

    func getFetchResults(entityName: String, predicateField: String) -> NSArray {
    let fetchRequest: NSFetchRequest = NSFetchRequest()
    fetchRequest.entity = getEntity(entityName)
    fetchRequest.returnsObjectsAsFaults = false

    let predicate: NSPredicate = NSPredicate(format: "nom == '\(predicateField)'")
    fetchRequest.predicate = predicate
    let fetchResults: NSArray = context.executeFetchRequest(fetchRequest, error: nil)!

    return fetchResults
    }

    func getField(entityName: String,field: String, fetchResults: NSArray, predicateField: String)-> String {
    let fetchRequest: NSFetchRequest = NSFetchRequest()
    fetchRequest.entity = getEntity(entityName)
    fetchRequest.returnsObjectsAsFaults = false

    let predicate: NSPredicate = NSPredicate(format: "nom == '\(predicateField)'")
    fetchRequest.predicate = predicate
    let fetchResults: NSArray = context.executeFetchRequest(fetchRequest, error: nil)!

    var retourField = ""
    if fetchResults.count > 0 {
    for result in fetchResults as! [NSManagedObject] {
    if let singleField = result.valueForKey(field) as? String {
    retourField = singleField
    }
    }
    }
    return retourField
    }
    }

    Et les appels à  la classe :



    // MARK: - Recup CoreData

    func confCoreDate() {
    let fetchEntity: FetchEntity = FetchEntity()
    let fetchResults: NSArray = fetchEntity.getFetchResults("Patients", predicateField: listeDesVariables.nomString)

    listeDesVariables.e1L1RegardString = fetchEntity.getField("Patients", field: "e1L1Regard", fetchResults: fetchResults, predicateField: listeDesVariables.nomString)
    listeDesVariables.e1L1SourireString = fetchEntity.getField("Patients", field: "e1L1Sourire", fetchResults: fetchResults, predicateField: listeDesVariables.nomString)
    }


    Merci pour votre aide en pensée... :p   :D   :p


  • Une petite question, au passage.


     


    Est-il dangereux, voire pas recommandé d'avoir un fichier dans lequel je stocke toutes les variables de tous les écrans, modifiées à  partir de chacun des écrans ? Cela me permet de lire n'importe quelle variable à  partir de n'importe quel écran...


     


    Voilà  le type de fichier. Y'en a qui vont hurler...



    import UIKit
    import CoreData

    class ListeDesVariables {
    var etapesNames = ["Etape 1", "Etape 2", "Etape 3", "Etape 4", "Etape 5", "Etape 6"]

    var etape1Levels = ["Equilibre regard/sourire","Lignes horizontales","Les 3 étages","Angle naso-labial","Plan esthétique de ricketts"]
    var etape2Levels = ["Le plan frontal - Ligne du sourire", "Le plan frontal - Plan esthétique", "Le plan frontal - Hauteur lèvre supérieure", "Le plan sagittal", "Le plan horizontal"]
    var etape3Levels = ["à‰paisseur des lèvres", "Courbure de la lèvre supérieure", "Symétrie du sourire"]
    var etape4Levels = ["L'occlusion"]
    var etape5Levels = ["Taille des centrales", "Proportions centrale/sourire", "Proportions antérieure/sourire", "Proportions centrale/latérale/canine", "Diasthèmes", "Teinte et état de surface", "Forme des dents", "Alignement axial et rotations"]
    var etape6Levels = ["Santé gingivale", "Gencive kératinisée", "Alignement et forme des collets", "Papilles et trous noirs", "Crêtes édentées", "Coloration des racines"]

    var localController: String!
    var localControllerVC: ContentEtapesViewController!

    var edition = false
    var creation = false

    var existingItem: NSManagedObject! = nil
    var newUserVar: Patients! = nil
    var newUserHashValue = 0

    var nomString = ""
    var prenomString = ""
    var dateCreationString = ""
    var sexeString = ""
    var ageString = ""
    var teintString = ""
    var yeuxString = ""
    var changerString = ""
    var personnaliteString = ""
    var physiqueString = ""
    var typeString = ""
    var frontalPatientFullURL = ""
    var BpnmbfPatientFullURL = ""
    var CfnmsPatientFullURL = ""
    var DpnmsPatientFullURL = ""
    var EaiETsPatientFullURL = ""

    var e1L1RegardString = ""
    var e1L1SourireString = ""

    var e1L2ForcerString = ""
    var e1L2PoigneeTLyString = ""
    var e1L2PoigneeTRyString = ""
    var e1L2PoigneeMLyString = ""
    var e1L2PoigneeMRyString = ""
    var e1L2PoigneeBLyString = ""
    var e1L2PoigneeBRyString = ""
    var e1L2ZoomingString = ""
    var e1L2ZoomXString = ""
    var e1L2ZoomYString = ""

    etc
    etc
    etc
    }

    Et je l'appelle sur mon container général ainsi :



    // MARK: - Variables globales

    var listeDesVariables = ListeDesVariables()

    class ContainerViewController: UIViewController, PatientsViewControllerDelegate, UIPageViewControllerDelegate, Etape0ViewControllerDelegate, Etape1ViewControllerDelegate, Etape2ViewControllerDelegate, Etape3ViewControllerDelegate, Etape4ViewControllerDelegate, Etape5ViewControllerDelegate, Etape6ViewControllerDelegate {

    blablabla

    }

    Ainsi, j'attrape une variable comme ça (par exemple, bien sûr) à  partir d'un écran :



    var toto = listeDesVariables.e1L1RegardString

    Merci pour vos remarques.

  • L'intention n'est pas choquante mais la manière oui.


    Puisque tu utilises CoreData tu pourrais utiliser le mapping entre entité CoreData et classe.


  • Tu ne devrais utiliser des variables var que pour les données pouvant changer après la création.


     


    Exemple :



    var etape4Levels = ["L'occlusion"]

    C'est mieux avec un let :



    let etape4Levels = ["L'occlusion"]
  • Bonjour,


     


    Pour ta dernière question : Pour moi l'intention et la manière ne sont pas bonnes. Le code devient illisible, tu ne respectes pas plusieurs principe du développement objet ( encapsulation, responsabilité unique des classe,....) et oui c'est dangereux de faire ça : Tout le monde a accès à  tout de n'importe ou. 


     


    Je vois aussi que tu accèdes au délégué de ton application a partir de la classe qui gère CoreData, je sais que ça vient des templates par défaut Apple... il vaut mieux mettre toute la logique CoreData ailleurs, par exemple dans la même classe qui gère les entités, c'est beaucoup mieux que d'avoir une dépendance au delégué de ton application. 


     


     


    Il me semble que tu as un paramètre en plus (fetchResults) dans ta méthode getField. Normalement il devrais y avoir une erreur/warning non ?

  • AliGatorAliGator Membre, Modérateur
    octobre 2015 modifié #7

    Une petite question, au passage.
     
    Est-il dangereux, voire pas recommandé d'avoir un fichier dans lequel je stocke toutes les variables de tous les écrans, modifiées à  partir de chacun des écrans ? Cela me permet de lire n'importe quelle variable à  partir de n'importe quel écran...

    Du moment que tu n'en fais pas un singleton, mais que tu le passes de proche en proche de ViewController en ViewController, ça pourrait passer.

    Mais ce qui est dommage avec cette approche c'est que :
    - normalement tu as déjà  toutes ces données dans CoreData, donc pourquoi les dupliquer dans une structure géante ?
    - cette classe est encore une fois bien trop longue et peu structurée
     

    Voilà  le type de fichier. Y'en a qui vont hurler...

    Déjà , si tu sais qu'il y en a qui vont hurler, c'est que tu sais qu'il y a quelquechose qui ne va pas là -dedans, c'est déjà  un bon début ;-)

    Sinon, voilà  ce que je te propose pour que, si jamais tu veux vraiment continuer à  partir sur cette voie plutôt que d'utiliser tes données déjà  dans CoreData, au moins rendre to code plus structuré et plus lisible :
    struct PatientInfo {
    var nomString = ""
    var prenomString = ""
    var dateCreationString = ""
    var sexeString = ""
    var ageString = ""
    var teintString = ""
    var yeuxString = ""
    var changerString = ""
    var personnaliteString = ""
    var physiqueString = ""
    var typeString = ""
    }

    struct URLs {
    var frontalPatient = ""
    var BpnmbfPatient = ""
    var CfnmsPatient = ""
    var DpnmsPatient = ""
    var EaiETsPatient = ""
    }

    struct Poignee {
    var topLeftString = ""
    var topRightString = ""
    var middleLeftString = ""
    var middleRightString = ""
    var bottomLeftString = ""
    var bottomRightString = ""
    }

    struct Zoom {
    var zoomingString = ""
    var zoomXString = ""
    var zoomYString = ""
    }

    protocol Level {
    var name: String { get }
    }

    protocol Etape {
    var name: String { get }
    var levels: [Level] { get }
    }

    struct Etape1 : Etape {
    let name = "Etape 1"

    struct Level1 : Level {
    let name = "Equilibre regard/sourire"
    var regardString = ""
    var sourireString = ""
    }

    struct Level2 : Level {
    let name = "Lignes horizontales"
    var forcerString = ""
    var poignees = Poignee()
    var zoom = Zoom()
    }

    // ... et autres levels de l'étape 1

    var levels : [Level] = [Level1(), Level2()]
    }

    struct Etape2 : Etape {
    let name = "Etape 2"

    struct Level1 : Level {
    let name = "Le plan frontal - Ligne du sourire"
    // ...
    }

    // ... et autres levels de l'étape 2

    var levels : [Level] = [Level1()]
    }

    ...


    struct ListeDesVariables {
    var localController: String!
    var localControllerVC: ContentEtapesViewController!

    var edition = false
    var creation = false

    var existingItem: NSManagedObject! = nil
    var newUserVar: Patients! = nil
    var newUserHashValue = 0

    var patientInfo = PatientInfo()

    var urls = URLs()

    var etapes : [Etape] = [Etape1(), Etape2(), ...]
    }
    De même, pour l'adoption de tes protocoles dans ton ContainerViewController, cette liste énorme de protocole me semble tout à  fait justifiée, mais aussi un peu longue à  mettre sur une ligne.

    Avec la possibilité que donne Swift de faire conformer une classe à  un protocole via une extension, je trouve ça bien plus propre et lisible de découper ta classe ContainerViewController en extensions, en créant une extension par protocole à  se conformer contenant les méthodes dudit protocole. Ainsi :
    class ContainerViewController: UIViewController {
    // Déclarations de variables
    // Code générique non lié à  un des protocoles
    }

    extension ContainerViewController: PatientsViewControllerDelegate {
    // Méthodes implémentant le protocole PatientsViewControllerDelegate
    }

    extension ContainerViewController: UIPageViewControllerDelegate {
    // Méthodes implémentant le protocole
    func pageViewController(_ pageViewController: UIPageViewController,
    willTransitionToViewControllers pendingViewControllers: [UIViewController]) {
    // blabla
    }
    // etc...
    }

    extension ContainerViewController: Etape0ViewControllerDelegate {
    // Méthodes implémentant le protocole Etape0ViewControllerDelegate
    }

    extension ContainerViewController: Etape1ViewControllerDelegate {
    // Méthodes implémentant le protocole Etape1ViewControllerDelegate
    }

    extension ContainerViewController: Etape2ViewControllerDelegate {
    // Méthodes implémentant le protocole Etape2ViewControllerDelegate
    }

    extension ContainerViewController: Etape3ViewControllerDelegate {
    // Méthodes implémentant le protocole Etape3ViewControllerDelegate
    }

    extension ContainerViewController: Etape4ViewControllerDelegate {
    // Méthodes implémentant le protocole Etape4ViewControllerDelegate
    }

    extension ContainerViewController: Etape5ViewControllerDelegate {
    // Méthodes implémentant le protocole Etape5ViewControllerDelegate
    }

    extension ContainerViewController: Etape6ViewControllerDelegate {
    // Méthodes implémentant le protocole Etape6ViewControllerDelegate
    }
    Non seulement c'est beaucoup plus structuré avec une organisation hiérarchique et de la composition de petites structures plutôt qu'une géante, mais en plus ça te permet si besoin de ne passer que le struct nécessaire à  tes différents écrans ou tes différentes fonction de traitement. Tu peux ainsi ne passer que l'élément de type Etape1.Level2 à  une fonction si elle n'a besoin que de ça et n'a pas à  s'embrasser de la totalité de ton objet géant ListeDesVariables qui fait quand même très fourre-tout et volumineux.

    ---

    Après, tout ça ne répond pas vraiment à  ta question "est-ce que c'est une bonne idée d'avoir une grosse ListeDeVariables partout" (à  part sur le fait que "si tu le fais, il faut la passer de proche en proche et surtout pas en faire un singleton"), mais te permet déjà  de mieux structurer ton code.

    Et en tout cas c'est ce que je t'expliquais déjà  dans des réponses précédentes, j'espère que c'est aussi sous ce genre de forme hiérarchique que tu as organisé tes entités/tables de ta base CoreData, à  découper tes éléments en petites entités structurelles, comme j'ai fait pour Zoom et Poignee par exemple, plutôt que de tout garder à  plat.

    ---

    Pour moi la bonne solution ce n'est pas d'avoir cette ListeDesVariables, puisque tu as déjà  toutes ces infos dans CoreData à  priori, mais juste d'utiliser les données de CoreData pour stocker ça, pas la peine d'avoir ça en double. Mais du coup, de bien organiser les entités CoreData dans la même idée que ce que je viens de te montrer, de façon hiérarchique en découpant des objets en petits objets. Par exemple, c'est une aberration que d'avoir ces 6 attributs pour ta poignée qui sont à  plat mélangé avec le reste, alors qu'on sent bien que ces 6 attributs sont intimement liés et qu'ils forment à  eux 6 la définition d'une poignée. Ou à  minima, si tu les groupes pas tous les 6, fais toi au moins une structure/entité CoreData qui groupe le X et le Y ensemble !
  • samirsamir Membre
    octobre 2015 modifié #8

    @Aligator : Tu utilises des structures pour tes objets modèle ? 


  • AliGatorAliGator Membre, Modérateur
    octobre 2015 modifié #9
    Ca dépend des besoins, mais ici oui j'ai plutôt choisi ça car les struct ont la sémantique Value-Type qui me semble plus adaptée dans ce cas, étant donné que ce ne sont que des conteneurs de données et pas vraiment des "objets" avec des actions (et en plus permet d'éviter les problème d'accès concurrents à  une même référence puisque ce ne sont plus des références justement).
    Mais bon après ça se discute selon les cas. Je ne connais pas assez le contexte de l'application de Théo pour savoir si c'est vraiment justifié. En pratique c'était pour justement faire tilter Théo sur le fait qu'en Swift on pense aussi protocoles et struct avant de partir tête baissée sur une class comme on le ferait en ObjC.

    C'est d'ailleurs un sujet abordé dans de mon article de blog de cette semaine que je compte poster aujourd'hui ou demain, même si je compte en fait pointer sur l'excellent talk de Andy Matuschak sur le sujet.
  • AliGatorAliGator Membre, Modérateur
    @samir Après effectivement s'il veut modifier ses données, les passer par référence " pour ne pas modifier la copie mais bien l'original " peut être intéressant aussi on est d'accord. Surtout effectivement si ces données sont "mutable" et qu'il compte donc les changer.

    En effet, autant pour les données constantes ça ne pose pas de problème :

    var allVars = ListeDesVariables()

    let etapeNames = allVars.etapes.map { etape in etape.name }
    print(etapeNames) // ["Etape 1", "Etape 2"]

    let etape1 = allVars.etapes[0]
    let etape1LevelNames = etape1.levels.map { level in level.name }
    print(etape1LevelNames) // ["Equilibre regard/sourire", "Lignes horizontales"]
    Autant si on garde des struct, quand on va vouloir commencer à  changer les valeurs alors qu'on les aura passé à  une autre variable, ça va changer la copie mais pas l'original :

    if var e1l1 = etape1.levels[0] as? Etape1.Level1 {
    e1l1.regardString = "Blah"
    }
    print((etape1.levels[0] as? Etape1.Level1)?.regardString) // On voit que regardString n'a pas changé dans la variable originelle etape1
    Alors que si on passe les Etapes et Levels en class et non en struct, dans ce cas tout est passé par référence et on peut faire :

    if let e1l1 = etape1.levels[0] as? Etape1.Level1 {
    e1l1.regardString = "Blah"
    }
    print((etape1.levels[0] as? Etape1.Level1)?.regardString) // On voit que regardString a bien été modifié
  • busterTheobusterTheo Membre
    octobre 2015 modifié #11

    Merci pour tous vos efforts à  me faire entrer dans le droit chemin 


    - jpimbert



     


    Puisque tu utilises CoreData tu pourrais utiliser le mapping entre entité CoreData et classe.



    J'ai cherché partout, et je ne comprend pas, c'est quoi ce fameux mapping ?


    - Draken



     


    C'est mieux avec un let :



    Tu as carrément raison, un oubli, c'est corrigé. Merci.


    - Samir



     


    Je vois aussi que tu accèdes au délégué de ton application a partir de la classe qui gère CoreData



    Tu parles de ça ?



    let appDel:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate

    Dans la classe (class FetchEntity) ?



     


    il vaut mieux mettre toute la logique CoreData ailleurs, par exemple dans la même classe qui gère les entités



    Ben justement, ma classe (class FetchEntity), je croyais qu'elle gérait les entités. Je l'ai créée pour cela.


    Je n'ai donc encore rien compris. Pourtant, j'en lis des trucs sur coreData !



     


    Il me semble que tu as un paramètre en plus (fetchResults) dans ta méthode getField.



    Non, non, j'ai bien vérifié. Merci pour la correction.


    - AliGator



     


    j'espère que c'est aussi sous ce genre de forme hiérarchique que tu as organisé tes entités/tables de ta base CoreData



    Justement, je n'ai pas encore pris le temps de m'en occuper, mais t'inquiète, c'est l'objectif number One.


    C'est pour cela, que je suis en train d'essayer d'optimiser tout cela.


    Je t'ai lu au complet, et je dois te relire et essayer de tout bien comprendre pour le meilleur choix.


    Merci pour ce cours énorme. J'adore.


  • AliGator,


    une petite question.



     


     


    normalement tu as déjà  toutes ces données dans CoreData, donc pourquoi les dupliquer dans une structure géante ?

    Justement, en ayant fait cette classe (FetchEntity), et en m'en servant ainsi,



    listeDesVariables.e1L3ForcerString = fetchEntity.getField("Patients", field: "e1L3Forcer", fetchResults: fetchResults, predicateField: listeDesVariables.nomString)

    Je me disais justement que :


    1- C'était con de stocker les données coreData dans des variables dans une classe externe (ListeDesVariables)


    et 2- Que c'était con de stocker tout court ces données, dans des variables.


     


    Donc, ma question est :


    Si je ne stocke pas les données coreData dans des variables (externes, locales, array, dictionnary, etc), cela signifie qu'à  chaque fois que j'applique des traitements sur ces données, je dois le faire directement sur ces données ?


     


    Par exemple (en variables locales - sans listeDesVariables):


    Plutôt que de faire :



    e1L3ForcerString = fetchEntity.getField("Patients", field: "e1L3Forcer", fetchResults: fetchResults, predicateField: nomString)

    var forcerString = ""
    if e1L3ForcerString != "" {
    forcerString = e1L3ForcerString
    }
    let aForcerUpperString = forcerString.uppercaseString
    resultatForcer.text = aForcerUpperString
    viewFille2Etapes.addSubview(resultatForcer)

    Il vaut mieux faire cela ?



    var forcerString = ""
    if fetchEntity.getField("Patients", field: "e1L3Forcer", fetchResults: fetchResults, predicateField: nomString) != "" {
    forcerString = fetchEntity.getField("Patients", field: "e1L3Forcer", fetchResults: fetchResults, predicateField: nomString)
    }
    let aForcerUpperString = forcerString.uppercaseString
    resultatForcer.text = aForcerUpperString
    viewFille2Etapes.addSubview(resultatForcer)

    Et par exemple, plus loin dans un



    func label1ForcerTap(recognizer: UITapGestureRecognizer) {
    ...
    }

    plutôt que



    e1L3ForcerString = labelsChamps[0]

    je fais cela ? (setField évidemment plutôt que getField)



    fetchEntity.setField("Patients", field: "e1L3Forcer", fetchResults: fetchResults, predicateField: nomString)= labelsChamps[0]

    Et tout les traitements que je dois faire partout, on doit faire comme cela ?


     


    J'ai souvent des traitements de motifs de coordonnées (x et y) en temps réel (mes fameuses poignées), je fais ça aussi en direct sur coreData ? Huuummmm !


    On voit les poignées ici à  environ 48 secondes.


     


    En gros, moi je pensais que l'on devait éviter de taper sans arrêt dans la base, mais taper une seule fois, puis stocker ce dont on a besoin localement dans un array ou des var...


     


    CoreData plus rapide que des vars ?


    CoreData moins gourmand en mémoire ?


    etc.


     


    Merci d'avance

  • AliGator



     


     


    Sinon, voilà  ce que je te propose pour que, si jamais tu veux vraiment continuer à  partir sur cette voie plutôt que d'utiliser tes données déjà  dans CoreData, au moins rendre to code plus structuré et plus lisible :

    J'adore ton code, y' plein de trucs qui m'inspirent (struct, protocole, organisation, ...).


    D'ailleurs, "protocol", j'hallucine... J'en ai encore beaucoup à  apprendre.


    Merci pour le temps que tu as du passer pour le faire, et donc t'imprégner d'un projet qui n'est pas le tien.


     


    En regard de ta remarque et de ma réponse (juste au-dessus)



     


     


    normalement tu as déjà  toutes ces données dans CoreData, donc pourquoi les dupliquer dans une structure géante ?

     


    effectivement, si c'est le cas, je laisse tomber cette ListeDesVariables, et voire, plus aucune variables locales inutiles...


     


     


    Pour les



    extension ContainerViewController

    C'est génial.


    Je vais essayer...

  • AliGatorAliGator Membre, Modérateur
    octobre 2015 modifié #14
    En fait perso j'arrive pas trop à  lire du code CoreData brut de fonderie comme ça. Les NSFetchRequest, NSEntityDescription et tout ça me saoule. Je préfère de loin utiliser MagicalRecord : t'as rien à  faire, c'est mega-giga-plus-lisible et plus compréhensible comme code à  suivre, et ça rend les actions courantes (fetcher des données CoreData, les modifier, etc) totalement transparent au point que tu ne te rends plus compte que tu fais du CoreData.

    Perso, à  mes débuts j'ai essayé plusieurs fois CoreData, mais ça me faisait peur et j'y comprenais pas grand chose, bien trop de classes et de concepts nouveaux à  apprendre et à  comprendre. J'ai plusieurs fois essayé de m'y mettre et ai abandonné de découragement. Et puis un jour j'ai découvert MagicalRecord. Et là  tout est allé comme dans du beurre.

    j'en ai même fait un talk lors d'une conférence CocoaHeads Rennes que je t'invite fortement à  regarder, pour moi ça a été une révélation.


    Par exemple je suis un peu perdu par ton code
    if fetchEntity.getField("Patients", field: "e1L3Forcer", fetchResults: fetchResults, predicateField: nomString) != "" {
    forcerString = fetchEntity.getField("Patients", field: "e1L3Forcer", fetchResults: fetchResults, predicateField: nomString)
    }
    Moi avec MagicalRecord pour récupérer le Patient dont le nom est nomString et modifier son attribut e1L3Forcer je ferais:
    Patient* patient = Patient.findFirstByAttribute("nom", value:nomString) // Récupérer le premier patient dont le nom est nomString
    let ancienneValeur = patient.e1L3Forcer // lecture de la valeur d'un attribut
    patient.e1L3Forcer = nouvelleValeur // écriture d'une nouvelle valeur dans l'attribut
    NSManagedObjectContext.defaultContext.save() // sauvegarde dans CoreData
    Bon ça fait des plombes que j'ai pas touché à  CoreData et donc MagicalRecord et je connais pas son API Swift, mais l'idée est là .

    De ce que j'ai compris tu as toi-même commencé un espèce de classe "wrapper" autour de CoreData pour te faciliter la vie, mais c'est dommage de réinventer la roue (et certainement pas aussi bien) alors que MagicalRecord te met déjà  à  disposition qui encapsule tout ça pour te fournir une API tellement plus lisible...
  • Ok AliGator.


     


    Je suis en train de regarder ta vidéo. Mais c'est en objC. Je peux m'en servir sur un projet en swift ?


     


    Et sans quoi, pour une de mes nombreuses questions.


    1- Il faut travailler en direct sur la BDD (motifs de coordonnées, etc.), et donc enregistrer en temps réel


    2- Ou bien prendre dans la BDD, puis stocker dans des variables, et travailler sur nos variables, puis réenregistrer dans la BDD ?


     


    Merci.


  • AliGatorAliGator Membre, Modérateur
    Oui et 1
  • Merci beaucoup.


  • Bon j'ai bien regardé la vidéo, et j'ai téléchargé le framework et j'ai tous les liens des tutos et d'install.


    Je ne me sens pas le courage pour l'instant de l'installer sur ce projet, car j'ai tellement à  faire, à  restructurer.


    Je testerais cela sur un autre projet. Mais ça m'intéresse grave. Encore merci d'avoir insister.


     


    Objet de ma question.


     


    Dans un premier temps, j'ai viré tous mes accès dans tous mes fichiers à  des variables dans le fichier externe bidon (ListeDesVariables)


     


    Dans un second temps, puisque je ne charge plus toutes les variables à  partir de ce fichier, au départ d'un click tableView, je ne récupère que celles dont j'ai besoin dans chacun de fichiers. C'était fastidieux, mais bon, ça marche.


     


    Et j'ai ma p'tite classe (le wrapper coreData que je devrais améliorer, j'en conviens).


     


    Mais je pense, que, déjà , avec ces améliorations, je devrais gagner en mémoire (sur Instruments). Je testerai cela plus tard, j'ai encore des motifs de restructuration à  faire sur des images, etc.


     


    Mais comme maintenant mon fichier externe bidon des variables, est très épuré, je me demandais si je ne pouvais pas m'en débarrasser complètement.


     


    Je m'explique, j'ai quelques variables (et quelques constantes) à  l'intérieur, dont j'ai systématiquement besoin partout dans le projet.


    Et je me demandais (encore une fois  :-* ), s'il n'était pas préférable de les mettre en variables globales (tes cheveux se hissent sur la tête, hein) dans mon container général. Ce serait cool.


     


    Voici la liste des éléments en question :



    import UIKit
    import CoreData

    class ListeDesVariables {
    let etapesNames = ["Etape 1", "Etape 2", "Etape 3", "Etape 4", "Etape 5", "Etape 6"]

    let etape1Levels = ["Equilibre regard/sourire","Lignes horizontales","Lignes verticales","Les 3 étages","Angle naso-labial","Plan esthétique de ricketts"]
    let etape2Levels = ["Le plan frontal - Ligne du sourire", "Le plan frontal - Plan esthétique", "Le plan frontal - Hauteur lèvre supérieure", "Le plan sagittal", "Le plan horizontal"]
    let etape3Levels = ["à‰paisseur des lèvres", "Courbure de la lèvre supérieure", "Symétrie du sourire"]
    let etape4Levels = ["L'occlusion"]
    let etape5Levels = ["Taille des centrales", "Proportions centrale/sourire", "Proportions antérieure/sourire", "Proportions centrale/latérale/canine", "Diasthèmes", "Teinte et état de surface", "Forme des dents", "Alignement axial et rotations"]
    let etape6Levels = ["Santé gingivale", "Gencive kératinisée", "Alignement et forme des collets", "Papilles et trous noirs", "Crêtes édentées", "Coloration des racines"]

    var localController: String!
    var localControllerVC: ContentEtapesViewController!

    var edition = false
    var creation = false

    var existingItem: NSManagedObject! = nil
    var newUserVar: Patients! = nil
    }

    Il s'agit des noms des écrans, des noms des controllers, des controllers, du flag (je click dans le tableview ou je crée un nouveau patient), de l'objet nouvellement créé.


     


     


    Voici d'autre part, les quelques globales que j'ai déjà  dans mon container général :



    import UIKit
    import CoreData

    // Etat des panneaux
    enum OuvertureSlideNav {
    case SlideNavFermee
    case SlideNavOuverte
    }

    // MARK: - Variables globales

    var pagesNavigationController: UINavigationController!

    // Pour Fondu image intro appli
    var introAppli: Bool!

    // Photo par defaut
    var noPhotoPNG = "no_photo.png"

    // Il faut avoir les 5 photos avant de naviguer
    var compteurPhotos = 0

    // Model
    var listeDesVariables = ListeDesVariables()

    // Taille de lecran
    let MainScreenSize: CGSize = UIScreen.mainScreen().bounds.size

    // Couleur des boites blanches - box filles et mère
    let FondsColor = UIColor(red: 246/255, green: 246/255, blue: 246/255, alpha: 1.0)

    let tailleVignettes: CGSize = CGSize(width: 170, height: 135)
    let taillePhotos: CGSize = CGSize(width: 560, height: 420)


    class ContainerViewController: UIViewController {
    ...

    }

    Evidemment (var listeDesVariables = ListeDesVariables()), ça va dégager si t'es ok pour les globales...


     


     


     


     


     


    C'était ma question...


     


     


    Merci d'avance

  • busterTheobusterTheo Membre
    octobre 2015 modifié #19

    Désolé, une autre petite question, ou plutôt remarque, concernant les extensions par protocole.


    J'en ai testé un, ça marche nickel. Cool.


     


    Mais j'ai des méthodes qui sont dans plusieurs protocoles (Les accès aux étapes - précédentes et suivantes - Et aussi, la configuration de ma navBar). Je dois donc les placer dans plusieurs extensions. Pas de problème ?


     


    Bon, en attendant ta réponse, je le fais quand même...


     


    En même temps, en le faisant, je me rend compte que ça fait quand même beaucoup de lignes de code en plus, juste pour éviter la longue ligne de départ (qui moi, ne me dérange pas, si ça ne pose pas de problème à  la mémoire ou au proc).


  • AliGatorAliGator Membre, Modérateur
    Les "var" globales sont à  éviter à  tout prix !!!

    Ca c'est pas nouveau c'était déjà  une règle d'or en Objective-C


    Les "let" globales c'est ok vu que ce sont des constantes et dont qu'il n'y a pas les risques de multithreading et d'accès concurrents pouvant faire crasher ton app comme avec les "var". Là  encore, rien de nouveau c'est la même règle qu'en ObjC


    Concentrant les méthodes qui sont dans plusieurs protocoles si c'est le cas c'est qu'il y a un problème de conception là  !! Comment ça se fait que tu as des méthodes avec exactement les mêmes noms dans des protocoles différents plutôt que d'avoir extrait ces méthodes communes dans un protocole dédié commun à  tous ?!


    Pour être franc des fois ta logique bizarre me dépasse...
  • Bonjour AliGator,


    j'étais en train de te répondre, en m'expliquant sur mes pbs de var globales et



     


     


    des méthodes avec exactement les mêmes noms dans des protocoles différents

    mais ça devenait long et compliqué à  expliquer. J'ai tout effacé. Je recommence plus simple (enfin j'espère).


     


     


     


     


     


    Méthodes/Protocoles :


    En tout cas, j'ai des méthodes dans mon ContainerViewController, qui doivent être appelées par chacun des controllers.


    Il s'agit de la conf de la nav custom et donc de ses boutons (nouveau patient, préférences, ouverture de la nav tableView de la gauche)


     


    En gros, chacun des controllers à  besoin d'exécuter la même méthode qui se trouve sur le ContainerViewController.


     


    J'ai aussi des méthodes (goEtape1(levelAccesDirect: Int)) jusqu'à  6.


    Et même remarque. Je dois pouvoir faire go5 à  partir de plusieurs controllers.


     


    Qu'en dis-tu ?


     


     


     


     


     


     


     


     


     


    Variables globales :


    Pour les var globales, je les met où, celles dont j'ai toujours besoin ?


     


    Encore merci à  toi.


  • AliGatorAliGator Membre, Modérateur
    octobre 2015 modifié #22

    j'ai des méthodes dans mon ContainerViewController, qui doivent être appelées par chacun des controllers.
    Il s'agit de la conf de la nav custom et donc de ses boutons (nouveau patient, préférences, ouverture de la nav tableView de la gauche)
     
    En gros, chacun des controllers à  besoin d'exécuter la même méthode qui se trouve sur le ContainerViewController.

    Je ne suis pas sûr de comprendre, ou plutôt j'ai l'impression que tu t'embrouilles (m'embrouille ?) en utilisant le mauvais vocabulaire.
    Tu parles de méthodes qui sont déclarées plusieurs fois, ou juste appelées à  plusieurs endroits ?

    1) Si ce sont des méthodes identiques qui sont déclarées dans plusieurs de tes protocoles, comme ton message de la page précédente le laissait entendre, c'est qu'il y a un problème :

    Mais j'ai des méthodes qui sont dans plusieurs protocoles (Les accès aux étapes - précédentes et suivantes - Et aussi, la configuration de ma navBar). Je dois donc les placer dans plusieurs extensions. Pas de problème ?

    Là  tu laisses penser que les méthodes sont déclarées dans plusieurs protocoles, et donc que tu dois les redéfinir avec leur implémentation dans plusieurs extensions à  chaque fois. Et si c'est ça c'est qu'il y a un souci.

    2) Mais si ce sont des méthodes déclarées uniquement dans ton ContainerViewController (comme le laisse entendre ton message précédent), et appelées ensuite de plusieurs endroits, alors je ne vois pas trop le souci ?! Ca ne change rien qu'elles soient déclarées dans le "class ContainerViewController : UIViewController" (si ces méthodes ne viennent pas d'un protocole) ou dans une des "extension ContainerViewController : UnProtocole" (si tu dois implémenter ces méthodes parce qu'elle est définie dans le protocole UnProtocole), dans tous les cas de l'extérieur tu les appelleras tout pareil "monContainerViewController.maMethode()", donc quel est le problème ?!
     

    J'ai aussi des méthodes (goEtape1(levelAccesDirect: [/size]Int[/size])[/size]) jusqu'à  6.
    Et même remarque. Je dois pouvoir faire go5 à  partir de plusieurs controllers.

    Qu'entends-tu par "je dois pouvoir faire go5" ? Tu entends bien "je dois pouvoir appeler la méthode go5 depuis n'importe quel controller ? Et alors, quel est le problème ? Le fait que cette méthode go5 soit déclarées dans la classe principale ou dans une extension (l'équivalent des catégories en Objective-C) ne change rien, du moment qu'elle est définie.
     

    Pour les var globales, je les met où, celles dont j'ai toujours besoin ?

    Si tu dois avoir des variables globales, c'est que ton code est mal fichu. La question n'est pas "je les met où", mais plutôt "comment je m'en débarrasse". Parmi les réponses, il y a "en fait c'est une constante et pas une variable, donc je dois utiliser let et pas var", ou alors "en fait je vais aller chercher les données dans CoreData et pas utiliser une variable globale pour ça", ou encore "je dois passer la variable de proche en proche" (ou pour la version expert : j'utilise de l'injection de dépendances)

  •  


     


    tu les appelleras tout pareil "monContainerViewController.maMethode()"

    Ben en fait, c'est exactement cela.


    Je ne sais pas pourquoi, je me suis barré à  l'époque dans les delegate/protocol. Certainement des erreurs de débutants qui apprennent un truc génial qu'ils ne comprennent pas ; ce qui est mon cas.


     


    En plus, je l'utilise souvent ce principe controller.methode : j'adore ce truc, c'est un des trucs que je préfère.


    Chui vraiment trop con.


     


    Bref, j'ai viré tous mes delegate/protocol, sauf les deux principaux.


    Je n'ai pu en mettre qu'un en extension.


     


    Voilà  la classe de mon container



    class ContainerViewController: UIViewController, UIPageViewControllerDelegate, Etape0ViewControllerDelegate {


    ...
    }

    Et ma seule extension



    extension ContainerViewController: PatientsViewControllerDelegate {
    func editerPatient(nSmoEdit: NSManagedObject) {
    ...
    }
    }

    L'autre méthode que j'aurais aimé mettre en extension est (configureNavbar).


    Mais je ne peux pas, car elle est appelée à  partir des 6 controllers principaux, selon le principe controller.methode.


    Et elle doit rester en delegate, car pour l'étape 0, cela semble nécessaire (à  cause de la slideNav qui devient noire - Bref, laisse tomber, trop compliqué).


     


    Pas grave, ça marche du feu de dieu, et ça commence à  être très correct.


    Je pense qu'on peut toujours, quand on est super pro, faire mieux, mais pour l'instant, je pense et surtout j'espère que c'est pas mal.


    J'ai à -peu-près fait tout ce que tu me recommandais.


     


    Je suis en train de virer toutes les variables globales, et aussi je reconfigure ma BDD (j'en suis à  la moitié, c'est hyper long - pfff)


     


    J'ai bon espoir. Je pense que demain, je vais pouvoir re-tester tout cela sur Instruments, avec moins de merdes qui trainent...


     


     


    Vraiment, merci à  toi oh Grand AliGator, d'avoir pris tout ce temps pour m'expliquer toutes ces choses dont une partie n'est pas encore assimilée ni appliquée, mais ça rentre tout doucement.


  • Au fait, ça



     


     


    La question n'est pas "je les met où", mais plutôt "comment je m'en débarrasse"

    j'adore. Y'a pas plus clair.


    Cela devient un de mes commandements à  suivre.


     


    Allez, je met résolu, ça le vaut bien, non !


     


    Encore merci aux barmen.


    :p     :p   :D   :p

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