[Résolu] Date et représentation locale [code Mis À Jour]

2

Réponses

  • MayerickMayerick Membre
    mars 2016 modifié #32

    Merci à  tous pour vos réponse ! 


     


    Je vais essayer de répondre point par point : 


    @Draken et Céroce :


     


    • J'ai bien conscience de tout cela et vous avez tout à  fait raison. La connaissance des dates est purement culturelle, j'imagine sans mal être largué sur tout ce que tu dis en effet Draken. En fait c'est Ali qui à  semé le doute dans mon esprit :) . Dans l'idée les utilisateurs peuvent aussi soumettre des quizz et des questions pour qu'on les enregistre dans la database. À partir de là , la remarque d'Ali était tout à  fait pertinente ! Comment un étranger peut exprimer une date religieuse avec le calendrier grégorien alors que ce n'est pas celui d'usage pour la culture ? Beaucoup de pays utilisent le calendrier grégorien et les utilisateurs devraient pouvoir faire la conversion, mais j'aurais voulu que ce soit le plus naturel possible pour l'utilisateur.


     


    @Ali:


    Tu as raison pour les mois japonais et chinois, la logique est bien celle-ci. Mon étonnement vient du fait qu'en français uniquement, les mois sont traduit avec une "consonance chinoise", alors que dans les autres langues ils sont traduit dans la langue cible, en toute logique en français on aurait alors dû avoir :Mois1, Mois2... 


    Et tu n'aurais pas une idée pour récupérer les noms des cycles d'années en chinois à  la manière des mois ou des ères ? J'ai bien l'impression que ce n'est pas possible, sauf à  tout encoder à  la main.


     


    Bon alors en définitive et comme vous me le suggérez, je ne vais prendre en charge que le calendrier grégorien, avec un region format adapté. Enfin de toute façon comme vous dites ce n'est sans doute pas spécialement un problème. Je vais nettoyer le code.


     


    Merci !


  • MayerickMayerick Membre
    mars 2016 modifié #33

    Bonjour à  tous


     


    Je viens de finir le nettoyage du code. J'ai bien appliqué ton conseil Céroce, des méthodes les plus courtes et les plus spécialisées possible. Il n'y a plus de duplication de code et j'ai tout factorisé au maximum et c'est vrai qu'il n'y a pas photo. Donc  merci !


    ça m'a permis de découvrir de nouvelles choses comme la Lazy initialization, c'est vraiment pas mal. J'ai vraiment essayé de faire ça le plus propre possible:


     


    • Tous les attributs et les méthodes internes sont privés.


     


    • Il n'y a que 3 méthodes set accessibles depuis les autres classes. 


     


    • J'ai lu sur Ray Wanderlich qu'il est de bon gout d'utiliser des extensions de classe pour prendre en charge un délégué ou des méthodes spécifiques afin d'améliorer la lisibilité du code. Du coup j'ai effectivement utilisé quelques extensions pour séparer les méthodes et rendre le code plus lisible, là  où avant j'aurai simplement mis un //Pragma Mark, vous en pensez quoi ? C'est vraiment un plus d'utiliser une extension pour mettre les méthodes de délégué ? Si vous pouviez jeter un coup d''oeil au code et me dire si je n'ai pas trop abusé des bonnes choses ou au contraire pas assez...


     


    Par contre pour appeler les méthodes set, je passe par la sharedInstance. J'ai hésité à  utiliser des méthodes de classe, mais je me suis dis que si déjà  j'initialise et je partage une instance, autant l'utiliser, mais je ne suis pas certain que ce soit la meilleure manière de faire, et vous, vous auriez fait comment ? Votre avis est le bienvenue ! 


     


    Pour utiliser mon code dans un projet existant, comme expliqué dans le read_me il suffi de placer le fichier YAMDatePickerHelper dans le projet, et d'ajouter la sharedInstance en délégué et dataSource du picker et la magie opère toute seule.  :p   


    Si vous avez des remarques, suggestions d'utilisation ou de présentation pour le code ou read_me n'hésitez pas à  me le dire !


     


    C'est la première fois que je fais ça et je ne demande qu'à  mieux faire. Merci à  tous !


     


    git du projet : 


    https://github.com/aymericbaur/datePicker-component.git


     


     


    Edit: Céroce, j'ai essayé de faire une méthode initWithPicker(picker:UIPickerVIew) comme tu le préconisais mais je n'ai pas réussi. Le seul moyen que j'ai trouvé est celui utilisé, partager une instance et faire picker.delegate = Helper.sharedInstance;


  • CéroceCéroce Membre, Modérateur
    mars 2016 modifié #34

    Je débute en Swift (si, si), alors je n'ai pas forcément réponse à  tout. Mes remarques, désordonnées:

    - Tu n'utilises pas les targets de test, alors supprime-les.

    - AppDelegate.swift est plein de méthodes vides. Supprime-les.

    - ViewController: ça me parait bien. Mais comme tu me demandes de critiquer, je te dirais que la doc n'a rien à  faire là  ;-)

    YAMDatePickerHelper


    enum DateFormat et RegionFormat: ne peux-tu pas passer les enum en type String ? ça supprimerait le besoin de la méthode description().


    sharedInstance: il y a un problème. Tu ne peux pas en faire une instance partagée, parce que si tu as deux pickers qui utilisent l'instance partagée de YAMDatePickerHelper, alors les réglages s'appliquent forcément aux deux! En général, il n'y a pas de bonne raison d'utiliser la design pattern singleton. Tu as besoin d'une instance, alors que le view controller l'instancie.

     

    La méthode numberOfComponentsInPickerView(): le switch...case devrait se trouver dans une méthode numberOfComponents() de DateFormat. Tu dois aussi pouvoir le faire pour les autres méthodes du datasource.

    Tu vas me dire "Mais à  quoi ça sert ?!". En le faisant, tu vas te rendre compte que tu as plein de switch...case dans DateFormat. En POO les switch...case sont un "code smell".


    ça sent mauvais parce qu'à  chaque fois qu'on veut ajouter un nouveau format de date, il faut changer le code. Alors que tu pourrais faire un protocole DateFormat, et avoir trois implémentations concrètes: YearMonthDayDateFormat, YearMonthDateFormat et YearDateFormat. Ainsi, tu pourras par la suite ajouter un nouveau format sans les modifier.


    À ce propos, les Swifters, une enum peut-elle implémenter un protocole ? Je n'ai pas encore atteint ce chapitre de mon livre ;-)


    Pour conclure, je suis pointilleux pour te faire progresser, mais ton code est de bonne qualité.


  • CéroceCéroce Membre, Modérateur


     


    • J'ai lu sur Ray Wanderlich qu'il est de bon gout d'utiliser des extensions de classe pour prendre en charge un délégué ou des méthodes spécifiques afin d'améliorer la lisibilité du code. Du coup j'ai effectivement utilisé quelques extensions pour séparer les méthodes et rendre le code plus lisible, là  où avant j'aurai simplement mis un //Pragma Mark, vous en pensez quoi ? C'est vraiment un plus d'utiliser une extension pour mettre les méthodes de délégué ? 




     


    Disons, qu'il ne faut pas juste que ce soit un moyen de moins charger le fichier source.


    Le but est de réduire le couplage entre les classes. Souvent, en mettant une partie du code dans un autre fichier, ça réduit effectivement le couplage, parce que tu es obligé d'ajouter un peu d'abstraction pour que ce code n'aille pas sans arrêt consulter le premier.

  • Test de 10 secondes, et paf .. un poney de plus pour l'usine canigou !


     


    Simulateur réglé pour un iPhone 5


     


    Erreur : fatal error: unexpectedly found nil while unwrapping an Optional value


     


    Voir copie d'écran pour l'emplacement dans le code


     


    Condition de reproduction : j'ai un peu joué avec les dates, puis les boutons. Tout a explosé en pressant le bouton "yyyy".


     


     


     


  • MayerickMayerick Membre
    mars 2016 modifié #37

    Merci beaucoup tout est bien pris en note. Je m'y mets de suite ! C'est exactement ce genre de remarques que je cherches.  :)


     


    • Je supprime tout ça et je push.


    • Pour la doc, je l'ai mise ici car elle parle de ce qui est juste en-dessous, donc ça me semblait être l'endroit, mais soit je peux comprendre. :) Donc dans la classe helper plutôt ?


     


    • Initialement pour les enum c'est ce qui était prévu, mais depuis Swift 2, les enums String n'ont plus de correspondance en Int.  :(


    Et l'idée était de pouvoir passer la valeur de l'index des segmentedControl, donc j'ai besoin des Int. La méthode description est de loin la plus simple et la plus propre j'ai trouvé.


     


    • Pour la sharedInstance, je ne voyais pas le problème même avec deux picker, mais maintenant que tu le dis, on peut effectivement vouloir des pickers différents. Et je ne savais pas qu'une sharedInstance devait être évitée si possible.


    Mais j'ai deux choix, soit je les instancie dans le viewDidLoad avant de m'en servir comme délégué et dataSource et je passe les méthodes set (et get en fait, j'y viens après) en méthodes de classe. Ou alors j'instance le Helper en attribut de classe et à  chaque fois que je veux appeler une méthode je passe par l'instance spécifique qui m'intéresse "désignée de manière forte".


    En bon débutant je dirais la première solution, mais en réfléchissant je me rend compte qu'on n'a plus accès à  une customisation ultérieure si on fait ça, ce qui encore une fois peut être voulu, donc finalement la deuxième réponse semble la bonne, et les méthodes restent comme elles sont.


     


    • Très bien je regarde pour les switch, en effet une enum peut implémenter un protocole, je regarde comment faire et je modifie. Pour les implementations concrètes, tu veux bien dire trois méthodes du protocol à  implémenter concrètement dans l'enum (forcément). J'ai l'impression de sentir ce que tu veux dire, mais j'ai du mal à  voir précisément, ça va venir en le faisant, ne me dis rien là  dessus pour le moment je vais trouver.  :)


     


    • Enfin il me manque une méthode pour récupérer la date affichée dans le picker, c'était quand même la base du sample. ::)  Je vais rajouter un petit label pour afficher la date sélectionné par le picker à  chaque fois qu'on passe dans didSelectRow, donc je vais devoir faire un protocol et faire implémenter ce protocole dans le view controller, pour mettre à  jour le texte du label, ça devrait aller facilement, sauf si tu as quelques chose à  redire sur cette façon de faire.


     


    • Très bien je ne connaissais pas la notion de couplage entre les classes, j'ai fait quelques recherches et j'ai encore du mal à  voir concrètement comment mettre ça en pratique, mais je vais continuer les recherches.


     


    Merci ! 


     


    Edit: merci Draken, je chasse ça de suite !


  • Simplification des conditions de reproduction du bug : ça plante systématiquement avec le bouton "yyyy", sans avoir besoin de faire des choses avant.

  • CéroceCéroce Membre, Modérateur


    • Initialement pour les enum c'est ce qui était prévu, mais depuis Swift 2, les enums String n'ont plus de correspondance en Int.  :(


    Et l'idée était de pouvoir passer la valeur de l'index des segmentedControl, donc j'ai besoin des Int. La méthode description est de loin la plus simple et la plus propre j'ai trouvé.




    Avoir un index pour le Segmented Control est une responsabilité du View Controller, pas du Helper.

  • MayerickMayerick Membre
    mars 2016 modifié #40

    Je l'ai : Une inattention sur un test... la honte...  >:(



    if let selectedMonthRow = lastSelectedRows["monthIndex"]
    { // Set Month Component:
    picker.selectRow(selectedMonthRow, inComponent: newFormatIndex["month"]!, animated: false)
    }

    devient :
    edit :
    if let formatIndex = newFormatIndex["month"], selectedMonthRow = lastSelectedRows["monthIndex"]
    { // Set Month Component:
    picker.selectRow(selectedMonthRow, inComponent: formatIndex, animated: false)
    }

    Edit 2 :J'en ai encore corrigé une. Plus jamais le copie-collé !!!  <_<




  • Avoir un index pour le Segmented Control est une responsabilité du View Controller, pas du Helper.




     


     


    Je sais bien, mais il faut bien que l'un et l'autre puisse communiquer, j'ai besoin de la propriété rawValue des enum, et je ne peux l'avoir qu'en Int, ou alors je peux regarder le titre du segmentedControl pour le selectedIndex, et obtenir ainsi le bon nom. Mais ça ne me semble pas propre ça, j'ai tord ? 

  • AliGatorAliGator Membre, Modérateur

    Et tu n'aurais pas une idée pour récupérer les noms des cycles d'années en chinois à  la manière des mois ou des ères ? J'ai bien l'impression que ce n'est pas possible, sauf à  tout encoder à  la main.

     
    En utilisant soit NSDateFormatter, soit NSDateComponents, on devrait pouvoir extraire les eraSymbols ou un truc comme ça, ou utiliser un format pour obtenir le nom de l'ère. Vu que tout ça est basé sur libICU qui est plutôt riches en fonctionnalités de ce genre, ça m'étonnerait qu'il n'y ait pas ce genre de méthodes disponibles (pas le temps de regarder)
  • MayerickMayerick Membre
    mars 2016 modifié #43

    Merci ali, non comme j'ai dis un peu plus haut j'ai bien épluché la doc de NSDateFormatter et NSDateComponents et rien de cela. L'era correspond à  av-JC pour le calendrier grégorien et au règne de l'empereur pour le calendrier japonais. Avec NSDateFormater je peux facilement avoir le nom du cycle d'année en cours, mais impossible d'avoir la liste complète :



    let dateFormater : NSDateFormatter = NSDateFormatter();
    dateFormater.dateStyle = NSDateFormatterStyle.FullStyle
    let calendar : NSCalendar = NSCalendar(calendarIdentifier:NSCalendarIdentifierChinese)!;
    let local : NSLocale = NSLocale(localeIdentifier:"fr_ZH")

    let date = NSDate(); // "Mar 14, 2016, 6:36 PM"
    dateFormater.stringFromDate(date); // "lundi 6 èryuè bing-shen"

    let components = calendar.components([.Year, .Month, .Era, .Day], fromDate: date)
    let year = String(components.year) // "33"
    let month = String(components.month) //"2"
    let era = String(components.era) // "78"
    let day = String(components.day) // "6"

    dateFormater.dateFormat = "UUUU"
    let monthName = dateFormater.stringFromDate(date) // "bing-shen"

    Si c'est dans la doc, je vais me sentir bien mal, parce que j'ai vraiment bien cherché ou alors le nom ne correspond pas du tout à  ce à  quoi je m'attends, mais j'ai vraiment tout regardé en détail, les méthodes de l'auto-complétion et la doc, et vraiment rien. Limite je préfère me dire que c'est privé et que je suis passé à  autre chose, même si c'est intéressant.


     


    @Ceroce:


     


    J'ai beau retourné la question, je n'arrive quand même pas à  voir en quoi un protocol peut m'aider à  me passer des switch. Pour le moment j'en arrive à  ça 



    enum DateFormat:Int {
    case fullDate = 0 , mediumDate, yearDate;

    var description : String {
    switch self {
    ....
    }
    func getNumberOfComponents()->Int {
    switch self {
    case fullDate: return 4;
    case mediumDate: return 3;
    case yearDate: return 2;
    }
    }
    }

    Ce qui on est d'accord est exactement la même chose que dans la méthode numberOfComponentInPickerViewr( ) si je le fais en simple méthode. Mais comment le protocol peut m'aider ? Il va juste avoir la même méthode à  implémenter dans l'enum, mais ce sera un switch aussi. Ou alors une méthode par type de format, comme tu as l'air de le suggérer en te relisant mot pour mot pour la énième fois, mais alors je ne comprends pas comment accéder à  chaque méthode en fonction de l'objet. Peut-être que je ne regarde pas au bon endroit, mais là  en tout cas je ne vois pas comment supprimer les switch. :/


  • DrakenDraken Membre
    mars 2016 modifié #44

    Essai de la dernière version : euh .. compile pas !


     


    Il y a aussi un warning concernant l'absence du fichier "Default586@2x.png" launch image pour la compatibilité avec l'iPhone 4S.


  • CéroceCéroce Membre, Modérateur
    mars 2016 modifié #45

    J'ai beau retourner la question, je n'arrive quand même pas à  voir en quoi un protocol peut m'aider à  me passer des switch.

     
    Quelque chose comme ça:
     
    protocol DateFormat {
    func format()->String
    func numberOfComponents()->Int
    }

    enum YearMonthDayDateFormat:DateFormat {
    func format() -> String {
    return "yyyMMdd"
    }

    func numberOfComponents() -> Int {
    return 4
    }
    }

    enum YearMonthDateFormat:DateFormat {
    func format() -> String {
    return "yyyyMM"
    }

    func numberOfComponents() -> Int {
    return 3
    }
    }
    (Attention, je connais mal la syntaxe de Swift, il faut s'inspirer de l'idée, pas de la manière exacte. Notamment, on doit sans doute pouvoir remplacer les functions par des propriétés.)

    Ensuite, il n'y aura plus de switch. Tu auras ça, par exemple:


    extension YAMDatePickerHelper:UIPickerViewDataSource,UIPickerViewDelegate
    {
    func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
    return self.dateFormat.numberOfComponents()
    }

  • MayerickMayerick Membre
    mars 2016 modifié #46

    Bonjour à  tous. 


     


    En effet Draken, toute mes excuses. J'ai modifié le nom du fichier ou son emplacement pendant que Xcode tournait, et ça a été le début des problèmes. Impossible de faire un push, j'ai dû batailler, mais ça me fait prendre en main la chose.Bon je suis repartie sur une base propre, tout est à  jour. Encore pardon, c'est pas très cool.


     


    https://github.com/aymericbaur/datePicker-component.git


     


    Merci Céroce pour l'explication, c'est déjà  bien plus compréhensible. Par contre il y a quand même une chose qui me dérange dans ton implementation. C'est que justement self.dateFormat est de quelle type ? Il n'y a pas de lien entre tes enum YearMontDayDateFormat et YearMontDateFormat,  donc comment stocker le currentFormat ? Je bloque là . Avant curentDateFormat était de type DateFormat, mais maintenant DateFormat est un protocol, d'où mon interrogation.


  • CéroceCéroce Membre, Modérateur
    mars 2016 modifié #47

    C'est que justement self.dateFormat est de quelle type ?


    La propriété doit être déclarée ainsi:
     
    var dateFormat: DateFormat?
    Le protocole promet que DateFormat a des méthodes format() et numberOfComponents(). Le Helper se moque du type exact, il a juste besoin de savoir que l'objet se conforme au protocole.
    C'est justement pour cela que c'est mieux: le Helper ne repose pas sur l'implémentation de DateFormat, seulement sur son interface.


  • En effet Draken, toute mes excuses. 




    Excuses acceptées capitaine Needa.

  • MayerickMayerick Membre
    mars 2016 modifié #49

    @ Draken 


    Ta bonté te perdra.   :) Je n'ai malheureusement pas de référence à  citer.  ^_^


     


    @ Céroce : 


    Alors là  tu me coupes ma chique de débutant, je n'aurai vraiment pas pensé que l'on puisse utiliser les protocols de la sorte. Je n'y aurais jamais pensé, car pour moi cela aurait donné une "expression unused". Mais un rapide test avec le playground montre que ça marche très très bien, ça m'en bouche une là  :



    protocol SampleProtocol
    {
    var numberOfWheels:Int {get}
    }

    enum Bicyle: SampleProtocol
    {
    case moto, velo;
    var numberOfWheels : Int {
    return 2
    }
    }

    enum Car: SampleProtocol
    {
    case auto, bus;
    var numberOfWheels : Int {
    return 4
    }
    }


    var currentVehicule :SampleProtocol?; // nil
    currentVehicule = Bicyle.moto; // moto
    currentVehicule?.numberOfWheels; // 2

    currentVehicule = Car.auto // auto
    currentVehicule?.numberOfWheels; // 4


    Alors ok il n'y plus qu'à  adapter ça à  mon code et ça résout merveilleusement bien le switch pour le numberOfComponents. Mais tu dis qu'il devrait aussi être possible de remplacer les méthodes titleforRow et numberOfRowInComponent, ça semble encore plus coton, sauf peut-être à  faire une méthode numberOfRowForComponent(component : Int ) -> Int que je l'appelle de la même façon :



    func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
    return self.currentFormat.numberOfRowInComponent(component)
    }

    J'ai bon ? Mais j'ai l'impression que ça va me faire de la logique supplémentaire alors qu'avant il n'y avais que 4 pauvres if.  ::) Faut que j'essaie, je m'y mets et je reviens, il faut que j'intègre bien le design.


  • CéroceCéroce Membre, Modérateur
    mars 2016 modifié #50
    Disons qu'il n'y aura pas forcément d'économie en termes de nombre de lignes, mais admets que les méthodes de UIPickerViewDataSource vont être très simples, et tu n'as même pas besoin d'étudier les diverses implémentations concrètes de DateFormat pour comprendre le code.

    Un gros avantage est qu'on peut ajouter très facilement un nouveau DateFormat, alors que c'était un problème dans ton code précédent. On respecte le principe dit "Open-closed": le code est ouvert aux extensions (il suffit d'implémenter le protocole DateFormat) mais fermé aux évolutions (le code est complet, et il n'y a pas de modifications à  lui apporter).

    Si un jour tu te mets aux tests unitaires, tu verras aussi que c'est bien pratique de créer une simple enum qui implémente DateFormat, et utilisée uniquement pour le test. On n'a qu'à  fixer les valeurs et vérifier que le Helper les renvoie exactement. Alors qu'avec les switch...case, il faut tester tous les formats pour être sûr de passer dans toutes les branches du code.
  • • Oui j'admets facilement en effet. Et finalement ça ne fait que déporter la logique j'ai l'impression, en


     


    • Très bien, encore une fois je ne connaissais pas et c'est très intéressant. Merci pour toutes  ces références. 


     


    • Oui  je compte effectivement me mettre aux tests unitaires d'ici peu, donc cette remarque n'est pas tombée dans l'oreille d'un sourd.


     


    Je push bientôt, j'ai ajouté des explications dans la conversion des valeurs du picker en String, car ça pouvait sembler mystique. Merci en tout cas. 


  • Par contre une chose que j'ai du mal à  voir, on est d'accord pour dire que YearMonthDayDateformat va avoir un unique case yearMonthDayDateformat ? C'est "bête" mais pas trop d'autre solution pour pouvoir initialiser une instance de YearMonthDayDateformat, non ?


  • CéroceCéroce Membre, Modérateur
    Exact. À mon avis, il ne faut plus utiliser une enum mais plutôt une struct. Nous sommes partis là -dessus parce que c'est ce qu'il y avait au départ, mais ce n'est plus judicieux.
  • MayerickMayerick Membre
    mars 2016 modifié #54

    Très bien je regarde. Merci pour ta réactivité !


  • MayerickMayerick Membre
    mars 2016 modifié #55

    Encore une chose à  laquelle je pense depuis le début, je me disais chaque chose en son temps mais voici venu le temps.   >:D


     


    Tu me dis qu'avoir un index pour le segmentedControl est de la responsabilité du viewContnroller et je suis bien d'accord, mais comment faire pour retrouver la correspondance entre le selectedSegmentedIndex et les nouvelles struct qui ont perdu leurs propriété rawValue due à  l'enum ? Il faut ajouter une méthode de type factory ? Donc qui va prendre un index en paramètre et le transformer en fonction en YearMonthDayDateFormat ou MonthDayDateFormat... ? Donc finalement refaire un switch ? Ce qui ne semble pas être la meilleure solution..



    // PRAGMA MARK: Factory Method:
    private func getDateformatFromIndex(index: Int) -> DateFormat
    {
    switch index{
    case 0:
    return YearMonthDayDateFormat();
    case 1:
    return YearMonthDateFormat();
    case 2:
    return YearDateFormat();
    default:
    return YearMonthDayDateFormat();
    }
    }
  • Joanna CarterJoanna Carter Membre, Modérateur
    mars 2016 modifié #56


    Donc qui va prendre un index en paramètre et le transformer en fonction en YearMonthDayDateFormat ou MonthDayDateFormat... ? Donc finalement refaire un switch ?




     


    Tu peux utiliser un dictionnaire<Int, closure> ou, même, une liste (array) de closures.


  • D'accord merci Joanna, je crois voir ce que tu veux dire, c'est futé. Remarque sans doute bête, mais ce dictionnaire ou cette liste serait un attribut de ma classe helper, codé en dur à  l'initialisation ? Je ne vois pas trop d'autre moyen.


  • Bonjour à  tous. 


     


    Alors pour vous tenir un peu au courant d'où j'en suis : 


     


    • J'ai essayer de déplacer la logique des méthodes de dataSource dans les Struct mais je me suis heurté à  un problème. je n'arrive pas à  appeler les méthodes du helper depuis la Struct, ce qui fait que je ne peux pas récupérer la currentRegionFormat et renvoyer les bonne valeurs, que ce soit pour le nombre de rowsInComponent ou les titre. J'ai pensé les passer en argument d'une closure mais le problème reste le même pour aller chercher dans les tableaux de data, à  moins de  vraiment tout passer en argument, mais bof non ?  Je sais que si projet était plus gros, je devrais séparer les datas, mais l'idée était de faire un fichier le plus simple possible à  ajouter en une fois à  son projet.


     


    • J'ai crée une liste de closure comme recommandé par Joanna, et j'aurai une petite question à  laquelle je n'ai pas trouvé de réponse. Est ce que finalement tout ceci est équivalent ? :



    let create1 : ()->SampleProtocol = { return YearMonthDayDateFormat()}
    let create2 : SampleProtocol = { return YearMonthDayDateFormat()}()
    var array = [create1(), create2, {return YearMonthDayDateFormat()}(), YearMonthDayDateFormat()]

    J'ai le pressentiment que dans les cas 1, 2, 3 j'aurai un objet différent a chaque fois que je demande array[0], ou 1 ou 2, alors que pour le dernier c'est toujours le même qui est stocké et renvoyé. Si c'est ça pourquoi ne pas simplement stocker un instance au lieu d'en créer une chaque fois ? C'est ce que j'ai utilisé pour le moment, mais peut-être que je ne vois pas le problème qu'il pourrait y avoir en utilisant ça..


    Au final cela ne revient-il pas implicitement au même que de passer par un switch ? Car on attribut bien une valeur en dur ( l'index dans le tableau) pour chaque type de dateFormat. Après si on me dit que c'est mieux comme ça je veux bien l'appliquer, mais ça semble implicitement la même chose (mais peut-être que normal puisque ça fait la même chose)


     


    • Enfin le viewController peut appeler une méthode getDateFormatArray et récupère l'objet à  index sélectionné pour pouvoir utiliser la méthode setDateFormat du helper.


     


    • J'ai aussi mis à  jour le git.

  • CéroceCéroce Membre, Modérateur
    Je n'ai pas compris ce que voulais dire Joanna en parlant de closures, mais on peut sans doute écrire simplement:


    let dateFormats = [YearMonthDayDateFormat(), YearMonthDateFormat(), YearDateFormat()];
    (Ceci doit se trouver dans le ViewController, pas dans le Helper).
    Encore une fois, tu n'as pas besoin de switches, parce que les index dans dateFormats correspondent aux segments du SegmentedControl.
  • MayerickMayerick Membre
    mars 2016 modifié #60

    Bonjour à  tous, 


     


    Merci Céroce c'est corrigé ! J'ai mis à  jour le git avec une nouvelle branche, donc il faut prendre la protocolBranch et non la master pour voir les modifications ! 


     


    https://github.com/aymericbaur/datePicker-component.git


     


    J'ai vraiment tout modifier et je pense qu'on est vraiment pas mal là . En fait je suis parti de ton exemple concret Céroce, comment faire si je voulais ajouter un nouveau format qui n'a rien à  voir avec les jours les mois, comme par exemple les heures minutes et secondes.


     


    • Je me suis d'abord rendu compte que même si je pensais avoir été le plus abstrait possible avec mes méthodes, j'ai quand même tout codé en dur avec mes histoires de thousands, units, months et days. Donc il fallait en effet absolument tout réécrire pour adapter. 


     


    • Je me suis alors dis, que comme tu le préconises, il faut placer les besoins fini du composant au niveau du protocol, et non plus du helper, j'ai donc transféré la logique.


     


    • Je me suis alors retrouvé avec un gros fichier helper, et je me suis dis que ce n'était pas très propre, que les datas et les structs n'avaient plus rien faire là . Alors j'ai fais un fichier Model dans lequel j'ai transféré tout ça, pour n'avoir dans le helper plus que le strict nécéssaire. 


     


    • Du coup maintenant si quelqu'un veut implémenter un nouveau format, il lui suffit de déclarer une Struct qui se conforme à  mon protocol DateFormat, de remplir toute les méthodes et éventuellement de construire les datas dont il aura besoin, je crois que le but est atteint avec ça, non ? 


     


    • Il me reste encore deux trois petites choses sur lesquelles j'aimerais votre avis: 


     


    - Dans l'implémentation des méthodes de protocole de mes structs, j'ai pour l'instant collé le code que j'avais avant et qui fonctionne forcément puisqu'il avait été écrit spécifiquement pour les trois cas que sont YearMonthDayDateFormat, YearMonthDateFormat et YearDateFormat.


    Du coup ça me fait faire de la duplication de code, car les trois Struct répondent à  la même logique. Ma question est donc comment factoriser ce code ?


     


    Selon moi j'ai deux solutions: soit j'épure le code des tests sur les optionnels devenus inutiles puisque par définition, je sais maintenant quelles variables vont être présente pour telle struct, là  ou avant il fallait que je test si elles existaient pour la struct ou non. Finalement cela revient à  faire de la duplication de logique plus ou moins tronqué, mais cela donne des méthodes extrêmement spécialisées et les plus courte possible. 


    Ou alors, partout où mes composants utilisent la même logique, je pourrais faire une méthode qu'appelleraient mes composants et donc économiser beaucoup de ligne de code, puisque j'ai déjà  écrit une logique abstraite prenant en compte mes trois composant. Un exemple avec du code :


     


    Soit je peux faire ça, ça me fait économiser plus de place : 



    //: Playground - noun: a place where people can play

    import UIKit

    protocol SampleProtocol
    {
    func makeLogic();
    var importantVar:[String : Int]{get};
    }

    struct structOne: SampleProtocol {

    var importantVar:[String : Int]{return ["1" : 0]};

    func makeLogic()
    {
    theSharedLogicOfMyTwoStructs(self)
    }

    }

    struct structTwo: SampleProtocol {
    var importantVar:[String : Int]{return ["1" : 0, "2" : 1]};

    func makeLogic()
    {
    theSharedLogicOfMyTwoStructs(self)
    }
    }

    private func theSharedLogicOfMyTwoStructs(structs: SampleProtocol)
    {
    /* My own logic wich handles My Structs...*/
    // NumberOne is always there :
    let numberOne = structs.importantVar["1"];
    if let numberTwo = structs.importantVar["2"];
    {
    // blabla
    }
    // bla bla
    }

    struct aStructMadeBySomeOneElse: SampleProtocol
    {
    var importantVar:[String : Int]{return ["yourVar" : 100]};
    func makeLogic()
    {
    // Make your own logic...
    }
    }


    Ou alors ça, qui est un code plus optimisé mais avec de la duplication de logique : 



    struct structOne: SampleProtocol {

    var importantVar:[String : Int]{return ["1" : 0]};

    func makeLogic()
    {
    // We know there is always only numberOne :
    let numberOne = self.importantVar["1"];
    // blabla
    }
    }

    struct structTwo: SampleProtocol {
    var importantVar:[String : Int]{return ["1" : 0, "2" : 1]};

    func makeLogic()
    {
    // We know numberOne and numberTwo are always here :
    let numberOne = self.importantVar["1"];
    let numberTwo = self.importantVar["2"];
    // blabla
    }
    }

    Selon quelle est la meilleure manière de faire ? 


     


    - Enfin une dernière question, comment appelle-t-on les méthodes en dehors de toute classe comme ma 


    méthode private func theSharedLogicOfMyTwoStructs( ) ? Je n'ai pas réussi à  trouvé sur le net, je dois pas voir les bons mots clés. Est-ce mal d'utiliser ça ? Je me doute que c'est peut-être pas top, mais est-ce à  proscrire absolument ? 


     


    Voilà  j'espère que c'est pas trop mal et je suis toujours ouvert à  tous conseil ou remarque !!


    En tout cas merci pour les conseils et avis précédent, le code n'a vraiment plus rien à  voir, entre le premier projet que j'ai posté et celui-là , alors que l'interface et les fonctionnalités n'ont pas bougé d'un poil, donc c'est top !


  • AliGatorAliGator Membre, Modérateur
    mars 2016 modifié #61
    J'ai pas tout lu, mais j'ai juste vu ça :

    Du coup ça me fait faire de la duplication de code, car les trois Struct répondent à  la même logique. Ma question est donc comment factoriser ce code ?

    Protocols with Defaults Implementations.
     
    protocol SampleProtocol
    {
    func makeLogic()
    var importantVar: [String: Int] { get }
    }

    extension SampleProtocol {
    func makeLogic() {
    let numberOne = structs.importantVar["1"];
    if let numberTwo = structs.importantVar["2"];
    {
    // blabla
    }
    // bla bla
    }
    }

    struct structOne: SampleProtocol {
    var importantVar: [String: Int] { return ["1" : 0] }
    }

    struct structTwo: SampleProtocol {
    var importantVar: [String: Int] { return ["1" : 0, "2" : 1] }
    }
    PS : Et vire moi ces point-virgules partout !!!!!!!!! (Je te conseille fortement de mettre en place l'outil SwiftLint sur ton projet pour qu'il t'habitue aux bonnes pratiques)
Connectez-vous ou Inscrivez-vous pour répondre.