Swift et sous classe de NSManagedObject

Bonjour,


 


Je suis en train de tester l'intégration de Swift à  CoreData. D'abord en créant des sous classes de NSManagedObject.


Je suis déjà  déconcerté à  ce stade. Je m'explique.


 


Prenons une entité avec deux dates. Lorsque je mets à  jour la première, la seconde est automatiquement mise à  jour en fonction de la valeur de la première date.


 


Avec Objective-C j'utilisais cette méthode :



@dynamic timeStamp;
@dynamic date; // Date qui est calculee automatiquement selon la valeur de timeStamp

// Des que je fais appel au setter de "timeStamp" j'en profite pour calculer "date"
- (void)setTimeStamp:(NSDate *)timeStamp { // Setter
[self willChangeValueForKey:@date];
[self setPrimitiveTimeStamp:timeStamp];
self.date = // je bidouille une date avec timeStamp;
[self didChangeValueForKey:@date];
}

Avec Swift je n'ai pas trouvé le moyen de reproduire ce code. La seule chose qui fonctionne est ceci :



class Event : NSManagedObject {
@NSManaged var timeStamp: NSDate
@NSManaged var date: NSDate

init(entity: NSEntityDescription!, insertIntoManagedObjectContext context: NSManagedObjectContext!) {
super.init(entity: entity, insertIntoManagedObjectContext: context)
addObserver(self, forKeyPath: "timeStamp", options: NSKeyValueObservingOptions.New | NSKeyValueObservingOptions.Old, context: nil)
}


override func observeValueForKeyPath(keyPath: String!, ofObject object: AnyObject!, change: NSDictionary!, context: CMutableVoidPointer) {
if (keyPath == "timeStamp") {
updateDate()
}

}

func updateDate () {
willChangeValueForKey("date")
self.setPrimitiveValue(/*Je bidouille une date*/, forKey: "date")
didChangeValueForKey("date")
}
}

Utilisation de KVO donc. Bien que cela soit parfaitement valide, je trouve le code nettement plus obscure. J'ai tenté d'utiliser les setter à  la façon de Swift ("set") mais c'est refusé par XCode.


 


Visiblement, pas d'autre moyen que de procéder ainsi.


Mots clés:

Réponses

  • AliGatorAliGator Membre, Modérateur
    juin 2014 modifié #2
    Utilise les Observers de Swift, à  savoir "willSet" et "didSet".

    Genre (pas testé, code en live à  adapter/corriger)
    timestamp: NSDate {
    didSet {
    self.date = ...
    }
    }
    La doc Swift sur le sujet
  • KubernanKubernan Membre
    juin 2014 modifié #3


    Utilise les Observers de Swift, à  savoir "willSet" et "didSet".


    Genre (pas testé, code en live à  adapter/corriger)



    timestamp: NSDate {
    didSet {
    self.date = ...
    }
    }

    La doc Swift sur le sujet

     




     


    J'avais déjà  essayé... refusé également : "NSManaged not allowed on observing properties"


  • AliGatorAliGator Membre, Modérateur
    T'as regardé la session 225 ("What's new in CoreData") ?
    Moi pas encore, mais je me dis qu'ils parlent peut-être un peu de ce genre de choses, sait-on jamais ?
  • AliGatorAliGator Membre, Modérateur
    En même temps ceci dit, c'est un peu bizarre ton pattern, là , non ?
    Pourquoi date n'est pas une propriété "transient" (et du coup non @NSManaged) ?

    Puisqu'après tout, si ta NSDate est calculée d'après ton timestamp, tu n'as besoin que de stocker ton timestamp dans ton DataModel, et ta NSDate sera alors une "computed property", non ?


  • T'as regardé la session 225 ("What's new in CoreData") ?

    Moi pas encore, mais je me dis qu'ils parlent peut-être un peu de ce genre de choses, sait-on jamais ?




     


    J'ai regardé... y a pas grand chose, et Melissa propose le même code que moi.



  • En même temps ceci dit, c'est un peu bizarre ton pattern, là , non ?

    Pourquoi date n'est pas une propriété "transient" (et du coup non @NSManaged) ?


    Puisqu'après tout, si ta NSDate est calculée d'après ton timestamp, tu n'as besoin que de stocker ton timestamp dans ton DataModel, et ta NSDate sera alors une "computed property", non ?




     


    Mon exemple est simplifié. En réalité j'utilise deux dates dont une qui représente le mois (façon normalisé) de la première date. Je la stocke pour des raisons de performances lors de mes requêtes et de l'affichage dans les tables view.

  • AliGatorAliGator Membre, Modérateur
    Tu appelles déjà  Mme Turner par son petit nom ? Comme c'est mignon :D


  • Tu appelles déjà  Mme Turner par son petit nom ? Comme c'est mignon :D




     


    Ah ah ah ! Ouais, à  force de regarder toutes ses vidéos je me sens intime.

  • AliGatorAliGator Membre, Modérateur
    Pour en revenir à  ton problème au fait, je pense que surcharger les setters/getters d'un NSManagedObject est une mauvaise idée à  la base (car sous le capot un setter de NSManagedObject fait plein de trucs dont on n'est pas au courant, comme la validation, l'autoboxing des Transformable, tout ça...).

    Je suis un peu surpris que tu aies pu le faire en ObjC à  vrai dire. Faut mieux utiliser le KVO pour ces trucs là  que surcharger un setter/getter d'une @property managée, à  mon avis, car un objet managé ça doit pas être simple sous le capot (contexte lié tout ça).

    Par contre c'est dommage que Swift ne te permette pas le willSet/didSet sur des @NSManaged, et que tu sois obligé de passer par le KVO d'ObjC, je suis surpris/déçu. Faudrait que t'envoies un mail ou un tweet à  Melissa (ou à  Chris) pour leur demander :D


    Ceci dit il ne faut pas oublier que Swift est un langage encore jeune, et surtout encore en Beta. On sait déjà  que dans la seed actuelle d'Xcode6 :
    - Y'a des bugs avec Swift et les IBOutlets
    - Il n'y a pas encore les access modifiers (private/public/...), même si ça va venir
    Donc c'est possible qu'il y ait aussi des ratés sur @NSManaged.

    N'hésite pas à  remonter un bug via le BugReport (même si le mieux aurait été d'aller voir Mélissa ou Crhis directement lors de la WWDC, hein ? ;)) d'autant qu'ils sont preneurs de retours sur comment améliorer Swift d'ici la sortie " ils l'ont assez laissé entendre dans leurs vidéos je pense.
  • Avant de surcharger les setter j'ai fait pas mal de tests pour vérifier que je n'avais pas mis la grouille quelque part. Notamment et surtout les notifications de contexte.


    Jusqu'à  maintenant je n'ai jamais eu de soucis. Ce qui ne veut pas dire qu'il n'y en aurait pas, j'en conviens.


     


    Comme toi, je trouve dommage cette histoire d'observer à  la swift qui n'est pas autorisé pour les NSManagedObject.


     


    Oui, je vais écrire à  Melissa, comme j'ai écrit à  Tim pour Aperture... mais c'est une autre histoire :-)


     


    Merci de ton aide.


  • Dernière chose. Pour que le runtime prenne en compte les sous-classe de NSManagedObject en swift il faut utiliser une nouvelle syntaxe dans le champ Class de l'éditeur du modèle : NomAppli.NomSousClasse (en ObjC on ne met que le nom de la sous-classe).


     


  • AliGatorAliGator Membre, Modérateur
    Oui c'est dû au fait qu'en Swift on a des namespaces (les modules). C'est cool, ça va nous éviter de devoir préfixer toutes nos classes et nos méthodes avec nos initiales et des trucs comme ça juste pour éviter les conflits comme on faisait avant en ObjC... Plus besoin de ça maintenant, c'est implicite.
Connectez-vous ou Inscrivez-vous pour répondre.