Swift + Magical record + select distinct
LeChatNoir
Membre, Modérateur
Salut à tous,
Ca fait 1 heure que je m'énerve pour faire un select distinct via swift+MR....
Voilà le code que j'ai réussi à pondre mais le compilo m'envoie paitre avec une erreur que je ne comprends pas.
J'ai des entités Topo qui ont un attribut "region". Je veux donc une liste de toutes les régions distinctes.
let paidFilter = NSPredicate(format:"free=%d",segment)
let req = Topo.mr_requestAll(with:paidFilter)
req.propertiesToFetch = ["region"]
req.returnsDistinctResults = true
region = Topo.mr_executeFetchRequest(req)
Et Xcode me dit :
Cannot invoke 'mr_executeFetchRequest' with an argument list of type '(NSFetchRequest<NSFetchRequestResult>)'
J'ai fait quoi de mal ?
Merci
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Je crois que c'est due au fait qu'il ne sait pas de quel type il s'agit. J'avais eu ce problème lors d'une migration sur Swift 3 et en gros il fallait spécifier le type.
Et concrètement, tu fais comment ? J'ai essayé un as! [Topo] mais pas mieux...
C'est ça. Après le fetch un as [NSManagedObject].
Mais dans mon cas c'était sans MagicalRecord donc je sais pas si la librairie joue un rôle dans ton erreur.
Bon j'avance mais je coince toujours...
Ce code là me fait une requête CoreData qui me ramène 9 résultats.
Dans le debugger, un print region me montre
Seulement le type retourné ne semble pas lui convenir car dès que je veux accéder à un membre, j'ai un beau
Alors comme je demande un retour sous forme de NSDicitonary, j'essaye de modifier mon tableau en [NSDictionary] mais la ligne du executeFetch est alors en erreur :
Pas étonnant puisque executeFetch est censée ramené des NSManagedObject...
Bref, je suis perdu
Petite précision qui a son importance, si je ne mets pas req.resultType = NSFetchRequestResultType.dictionaryResultType, ca part en cacahuète....
Ca cause à personne visiblement...
Ici, c'est clairement un pb de typage ... Malgré mes recherches, je n'ai rien trouvé sur "comment déterminer quels types contient une variable". J'ai chercher dans le débuger. Un print m'affiche bien le tableau avec un type mais quand je fais un po, bim. Erreur...
Je connais mal Swift, mais pourquoi tu ne tenterais pas un NSLog de ton objet. ça te donne pas la classe ?
Ne serait-pas (par hasard) un problème de "faulted objects" ?
Le print marche bien et il m'indique que c'est un array de Topo (ou de NSManagedObject).
C'est au moment d'acccéder à ses éléments que ca se complique et qu'il plante sauvagement. Un print de region[0] fait planter.
Est-ce que region est valide ?
Est-ce que region.count renvoie plus que 0 ?
region[0] renvoie un optionnel, du coup, il faut vérifier que le résultat n'est pas nil avant de le manipuler
oui, région est valide :
Tu as fait un forced unwrap avec le var region [Topo]! >:(
Le mieux c'est d'utiliser quelque chose comme :
J'ai fait
Et à l'exécution >:(
As-tu essayé de mettre des points d'arrêt dans la méthode de MR pour voir un peu ce qui se passe ?
Dans le cas des requêtes distinct, le résultat d'une requête est un array de dictionnaires, pas un array d'objets.
Je n'ai pas l'essayé et tu devrais peut-être vérifier le type des éléments de l'array
Malheureusement, quand je mets ça, il me dit :
'NSDictionary' is not a subtype of 'NSManagedObject'
J'aimerai vérifier le type des éléments de l'array mais dès que je cherche à y accéder, ça plante
Ah En fait, Topo est en Objective-C mais le viwController dans lequel je suis est en swift...
Bon ben c'est pas grave, je fais ça du coup :
Ca marche pareil. C'est moins élégant Merci de votre aide !
ok. Je vais utiliser guard. Promis
Quelques petites astuces :
1. Ne declares pas region avant de l'utiliser ; c'est pas nécessaire.
2. Tu mets une liste de Topo dans region ; il vaut mieux de le nommer topos, non ???
3. Si tu anticipes avoir besoin de trouver les valeurs uniques d'autres arrays, voici une extension sur Array qui le simplifierait
Du coup, tu pourrais faire
4. Mets les espaces entres les mots et les opérateurs et entre les lignes ; c'est plus facile à trouver les problèmes plus tard ::)
5. Si le membre "free" dans Topo ne pourrait être que "oui" ou "non", pourquoi pas utiliser Bool à la place de Int ?
Little question about guard Je parle d'un autre cas de figure, une String qui peut être nulle et que je veux affecter à un Label.
Contexte : je suis dans le cellForRow d'une UITableView. Dedans, je fais un switch indexPath.section.
Quand j'utilise guard, dans le else, il me demande de mettre un return ou un break. Si je mets un break, c'est embêtant car le reste ne s'exécute pas alors que je le souhaite quand meme.
C'est peut être pas le bon cas d'utilisation de guard ?
[edit] je crois que dans ce cas, c'est plus ça que je dois utiliser
non ?
Tu mets le guard ou exactement ? Tu peux montrer le code ?
Pourquoi pas un
?
Mais si, c'est possible, selon le contexte
Voilà ce que j'ai fait. Dans ce cas de figure, j'ai utilisé ?? pour mettre une valeur par défaut.
En gros, formattedPrice peut être nil.
Et pareil pour les UserDefault et le langage.
J'utilise ?? pour mettre "" dans ces cas là . Pas guard.
OK. On commence où ?
1. Tu as beaucoup de logique dans le mauvais endroit.
2. Tous les types, comme packDetailHeaderCell devrait commencer avec une majuscule (PackDetailHeaderCell)
J'ai fait un struct Topo pour illustrer l'example
En le faisant, j'ai vu quelques soucis :
1. Le formatting des chiffres et dates devrait être fait seulement dans le ViewController, en utilisant NumberFormatter et DateFormatter. Ne mets que les chiffres ou les dates dans les entités.
2. Tu as limité la choix de langues à l'anglais ou le français ; il vaut mieux d'avoir une deuxième entité, comme LocalizedTopo, qui tiennent les attributs comme : language (qui tient "en", "fr", etc), description, etc. La gestion des langues est plus compliqué que l'on imagine. J'ai rédige un struct pour déterminer la langue préférée et, en cas échéant parmi ceux que l'on a fourni, ça renvoie la langue de développement (ici "fr")
Du coup, on peut ajouter plus facilement n'importe quelle langue dans l'avenir.
Bon. maintenant aux cellules.
Il vaut mieux de séparer le code de présentation dans la cellule :
Du coup, on peut simplifier le code dans la méthode tableView(_:, cellForRowAt:)
Pour le formattedPrice, je préfère le stocker dans mon entité car les prix proviennent de l'appStore avec une struct "locale" et je préfère ne pas interroger l'appStore à chaque fois...
Pour le reste, merci bcp, je vais étudier ton code grande pécheresse du swift
Lorsque tu trouves les prix, ils sont en quel format ?
C'est dans un SKProduct.
Donc le prix :
Et après (doc Apple) :
Et voilà !
Tu mets Topo.price en NSDecimalNumber et tu mets quelque chose comme le code d'Apple dans la classe PackDetailHeaderCell, juste avant de l'assigner au bouton.
Mais, à la place d'utiliser product.priceLocale, tu utiliserais Locale.autoupdatingCurrent.