accès variables de connexion[réglé]
toolsDev
Membre
Bonjour,
Pour avoir une connexion au lancement de l'application, j'ai procédé ainsi :
class ConnexionSQLite
{
static var sharedInstance = ConnexionSQLite()
init()
{
do
{
let dbQueue = try DatabaseQueue(path: "games.sqlite3")
}
catch
{
print(error)
}
}
func allNameByTable(table: String)
{
let rows = try dbQueue.inDatabase
{
db in try Row.fetchAll(db, "SELECT * FROM \(table)")
}
for row in rows
{
let nom: String = row.value(named: "nom")
print(nom)
}
}
}
donc, j'ai une méthode init() qui exécute la connexion depuis AppDelegate :
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
{
// Override point for customization after application launch.
ConnexionSQLite.init()
return true
}
et j'aimerais que dans la Classe ConnexionSQLite ou une autre (idéalement, une classe Queries)
avoir plusieurs méthodes. Ainsi pouvoir découpler le plus possible mes méthodes.
Mon souci est qu'elles n'ont plus accès à dbQueue et j'ai l'erreur suivante :
Initialization of immutable value 'dbQueue' was never used; consider replacing with assignment to '_' or removing it
je ne vois pas pourquoi je n'y ai pas accès ?
encore merci pour tout
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Bon, je débute en swift mais ça me semble normal car dbQueue est déclarée localement dans le init et pas dans la classe.
Sorti du init le dbQueue n'est plus connu.
je me suis dis comme toi, mais même en le mettant en début de classe, erreur... >:(
Edit :
Je ne sais pas quel type dois avoir la constante dbQueue si je la place en début de classe.
si je fais ça
j'ai cette erreur à la compilation : "Type annotation missing in patter"
Edit 2 :
je pense que le type est DatabaseQueue.
J'arrive à me servir de dbQueue dans le "do" de connexion.
Mais pas dans mes autres méthodes, j'ai l'erreur : Call can throw, but the error is not handled
Je ne comprends pas le "self.let dbQueue" ?
Au lieu de let dbQueue:DatabaseQueue, as-tu essayé var dbQueue: DatabaseQueue?
et dans le init tu enlèves le let
lol c'est une erreur de copier/coller... désolé
j'ai éditer ::)
j'ai tester en static, let, var, en affectant nil, bref rien ne fonctionne
le type est bien DatabaseQueue ça no soucis.
sinon oui tout ce que tu me conseilles à été tenté...
Tu dois commencer en apprenant comment écrire un singleton :
Pour le reste, tu dois te souvenir que tous les "try" doit être entourés par un do { ... } except { ... }
Bonjour Joanna Carter et merci pour ton aide,
Plusieurs choses m'échappent :
Tu initialises dbQueue en lui donnant pour type dbQueue, là ok, concernant la suite c'est confus pour moi :
lazy étant aussi un type (propriétés stockées paresseuses), nous avons donc 2 types pour la même variable : dbQueue et lazy
Puis ensuite dans l'affectation tu lui redonnes un autre type : let ( ce qui fait 3 types)
J'avoue ne pas comprendre.
De plus, si je veux avoir accès à dbQueue en dehors de init() cela ne fonctionne pas.
Depuis le début je ne comprends pas comment fonctionne la portée de dbQueue.
Je te mets ce que je tente de faire/comprendre pour plus de clarté, en suivant tes conseils :
Et l'erreur qui fait référence au ".self" ne colle pas, je n'ai pas d'instance, pourquoi y faire référence...
Comme tu le vois, je tente en fait de mettre ma connexion dans init() pour qu'au lancement de mon application elle soit faite une fois pour toute (ensuite je mettrais les données surement dans un objet/tableau etc)
Du coup je voulais découpler mon code/classes en fractions, pour exemple ma méthode "getNameFromTable()"
La porté de dbQueue étant limitée, cela ne fonctionne pas en l'état.
Tu l'auras compris, mon idée était de faire des classes "d'utilitaires" (ou plutôt selon ton conseil une structure) Connexion et une autre Queries etc Mais sans pouvoir accéder à dbQueue je ne peux pas.
Merci pour tout,
"lazy" signifie que la var sera initialisé au premier appel ; du coup, il n'est pas nécessaire d'initialiser la var autrement.
En revanche, on aurait pu écrire :
... mais, à cause du fait qu'il faut prendre compte du besoin d'utiliser un "try" avec l'init de DatabaseQueue, ça ne serait pas possible ; donc la "lazy var", qui utilise un closure pour permettre plusieurs lignes de code dans l'initialisation.
Sur la question de (apparemment) un deuxième let dans le lazy var, ce n'est qu'une valeur temporaire qui est tenu par la var et renvoyé du closure, sans la nécessité d'exécuter le code d'initialisation chaque fois que l'on appelle la var.
En même temps, car il est possible que DatabaseQueue puisse "throw", c'est pour ça qu'il est nécessaire faire la var comme optional (DatabaseQueue?) et de renvoyer nil en cas d'échec.
La portée d'un let/var, c'est entre les parenthèses bouclées qui lui entourent ; du coup, pour que le let dbQueue soit visible dans tous la struct ConnexionSQLite, il faut le mettre au premier "niveau", juste à l'intérieur des parenthèses bouclées de la struct. Par conséquent, toutes les fonctions de la struct puissent le voir).
Petit astuce - quand to veux référencer un let/var dans le code d'une struct/classe, il vaut utiliser self.myVar pour éviter la confusion avec les lets/vars plus locales.
En plus, il faut modifier ton code pour éviter mettre les ? ou les ! partout :
Navrer, mais je ne comprends pas, j'ai bien placé dbQueue en début de classe/structure, mais comme tu me l'a expliqué (ai j'ai mal compris ?) je ne peux pas du fait que c'est une closure et que cela engendre des erreurs à la compilation.
mon idée est un init() contenant la connexion, puis mes méthodes qui se servent de la variable globale (tester et valide) dbQueue (comme dans mon exemple plus haut), en début de classe/structure, comme tu me l'explique plus haut. Mais cela ne fonctionne pas.
Donc, avoir dans une variable globale ceci dans mon private init() :
Mais accessible depuis n'importe quelles méthodes de cette structure/classe, vois même d'une autre classe.
Tu n'as jamais appris un langage orienté objet auparavant ?
Qu'est-ce que tu crois sur la méthode init() ? De ce que tu as écrit, j'entends que tu crois c'est le commencement de ton appli >:(
Tu comprends pourquoi tu as mis le :
... dans ta struct ? ça fait quoi de ton avis ?
Tu parles des globales mais tu n'as jamais déclaré dbQueue comme globale. Par contre, tu essaies d'initialiser un let qui n'a que la portée de la méthode init()
Tu comprends que veut dire le terme Singleton et comment ça marche ?
Tu comprends le principe des classes et des structs ?
La portée des types, des lets/vars, et toute autre manière de code est bien documentée par Apple https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AccessControl.html
Il me semble que t'as beaucoup à apprendre avant que tu commences à bidouiller avec les concepts avancés comme tu essaies là .
je pense comprendre oui ???
le singleton ? Oui je connais, c'est une instance qui perdure durant toute la vie de l'application.
Cependant je ne vois pas ce que je comprends pas ? Et le but étant de ne pas surcharger le device justement...
je connais la POO donc, les classes et les portées oui... surtout en java, que je trouve bien plus clair et simple !
Pour moi le init() est l'équivalent d'un constructeur java (ou autre peu importe) d'ou le fait que je souhaite uniquement lui mettre ma connexion, comme je te le dis depuis le début.
Mais comme déjà dis je suis vraiment perdu avec cette structure, désolé on commence tous à un moment donné non ?
je pense déjà bien m'en sortir. Surtout que je cherche à faire "propre" et pas n'importe quoi.
Sinon, crois moi que j'aurais déjà fini mon petit projet.
mais je voulais du nickel :-*
Juste comme ça, je te montre ou j'en suis et ça servira à d'autre... ça j'en suis sur :
et pour en revenir au singletion :
Je pourrais bien sur m'en servir pour appeler mes variables (depuis l'extérieur), mais dans mes méthodes de classes/structures (en locale quoi!) cela ne fonctionne pas.
Après si tout ça est erroné, je suis bien sur à l'écoute de mes pairs et ça de façon très humble, comme sur tout forum d'entre aide en fait...
Excuses-moi je prends la discussion en cours.
Qu'est-ce qui ne marche pas ? (Peux-tu être plus précis ?)
Ne décourage pas, si tu as un bon background en Java, tu vas te faire à Swift. Il faut juste que tu "acceptes" qu'en Swift certaines choses sont différentes.
Salut,
Et bien oui je viens du monde java, ou ce que je tente de faire en swift, je l'ai déjà fait en java de façon très simple et plus au moins propre. Je te remercie pour tes encouragements.
Je lance une connexion (singleton) pour avoir au lancement de mon appli un flux, ensuite je mets toutes les données récupérées par mes requêtes dans une variable globale.
Mon souci :
J'aimerais que ces requêtes soit placées dans une classe ou structure différente, d'ou le fait d'avoir accès au flux de connexion (donc un flux testé et non nil) .
Puis je continue la vie de mon application via des ViewController.
Edit:
Après, j'ai peut être un souci d'analyse de mes besoins et donc, de conceptualisation...
Mais je trouve mon raisonnement bon, à voir...
Et de mon point de vue je trouve swift plus proche de javascript, affectation des variables avec des méthodes en est l'exemple...
OK
En fait je voulais savoir plus concrètement quel était ton problème : quelle ligne de code bugue ?
PS :
Effectivement, je ne vois pas forcément l'intérêt de couper ta classe en deux (connexion et requêtes). Mais bon c'est pas très grave non plus.
j'aimerais avoir dans mon init() seulement ma connexion :
et déjà pour bien comprendre, avoir une méthode différente pour chaque requêtes :
Ce que m'a conseillé Joanna Carter est de déplacer la closure en début de classe :
Mais je ne peux toujours pas y accéder dans mes méthodes locale, et cela plante, forcément...
Mal gré le fait qu'elle est bien une portée globale.
je pourrais bien sur placé la requête directement dans le init() mais je ne trouvais pas ça correcte (peut être à tord, je ne sais pas)
Toujours pareil j'ai essayé de faire un return de mes données dans le init(), mais sans succès.
(ce que je voyais, s'était un return ( dans mon init() ) d'une variable contenant les datas de mes requêtes pour la récupérer partout)
voilà , j'espère avoir été plus clair,
J'ai bien compris le concept du singleton :
Mais je ne vois pas comment m'en servir suivant ce que j'ai expliqué, à savoir une méthode init() (appelée dans AppDelegate) pour la connexion et des méthodes pour mes requêtes situées dans la structure ConnexionSQLite, voir dans une autre classe (Queries).
Pour moi (alors oui je ne comprends pas tout) faire ConnexionSQLite.sharedInstance.dbQueue dans chaque méthode pour accéder au dbQueue me parait faux, pourquoi ? Et bien niveau ressources utilisées, je pensais ( peut être à tort...) que ce n'était pas bien.
donc, là , c'est ma dernière version, sans la méthode déjà décrite plus haut. Ou tout est dans le init(), ce que je n'aime pas.
Edit:
Donc ce code fonctionne bien, mais tu remarqueras que je n'ai plus les méthode séparer de requêtes et ça me convient pas, encore une fois peut être à tort...
Non c'est très bien de faire comme ça !
Tu pourras avoir accès aux tables avec
PS : Désolé je ne parle pas Swift.... ni SQL ;-)
je ne savais pas trop quel type utilisé aussi, ayant tenté ceci :
je vais testé ta proposition, merci !
Edit:
ça plante
je me renseigne sur les types mais [:] je connais pas.
Tu ne peux mettre `nil` que aux types du genre `Row?`
T'as appelé init() depuis l'AppDelegate ? Mais tu m'as dit que tu comprends le Singleton
Je t'ai déjà dit qu'il faut mettre init en private, afin que l'on ne puisse pas l'appeler de l'extérieur de la classe.
init() n'est pas une méthode comme les autres. C'est réservée seulement pour l'initialisation d'un instance de la classe.
Le principe d'un Singleton, c'est que l'on ne puisse jamais avoir plus d'un instance ; chaque fois que tu appelles init() tu crées un nouveau instance.
Tu ne devrais pas avoir aucune logique dans init(), à part de ce qui contribue à la construction d'un instance.
init() ne doit être accessible que depuis l'initialisation de :
... c'est là que l'appel à init() est fait.
Le seul moyen d'accéder le seul instance de ConnexionSQLite est d'appeler les méthodes (mais pas init() ) via Connexion.SQLite.sharedInstance
No, si tu n'appelles pas init() tu ne gaspilles pas de ressources car, comme j'ai dit, il n'y aura plus qu'un instance.
Supprimes tous le code d'init() et le mettre dans une autre méthode
Que tu pourras appeler `func fetchAllTables` par exemple ;-)
Mais oui
bon je te remercie
je vais tout refaire en suivant tes précieux conseils !
Sinon, oui j'avais cru comprendre le singleton et la méthode init(), mais je dois réviser...
A force je me suis vraiment embrouillé, la pause s'impose, je prends du recul, vous relis, assimile et reviens vous dire !
Merci, pour ta patience et ta pédagogie Joanna Carter et merci aussi à toi colas_ et à tous les autres, mon seul but étant d'évoluer et de me débrouiller seul.
grâce à vous tous c'est engagé
La méthode init() n'est jamais appelée. Sauf, cas rares, dans certaines méthodes init().
Je laisse les swifters compléter si besoin est ;-)
salut,
Cette fois tout fonctionne bien.
Vos explications vont me permettre de mieux comprendre, cette fois ça été beaucoup plus clair.
Pour pouvoir me débrouiller seul (et surtout comprendre !)
Comment faire pour avoir une variable globale dans ma classe ?
J'ai bien mon resultSet (ma requête) mais j'aimerais pouvoir y disposer depuis l'ensemble de mon projet.
Ma question, est de comment connaitre le type à employer quand on ne le connait pas ?
Bien sûr, avec le debug me direz vous, il m'indique que table_datas est de type "Row" (sauf erreur de ma part ?)
du coup je tente de construire ma variable globale ainsi :
On ne peut pas avoir une variable globale dans une classe. Les globales sont déclarées hors de toutes classes.
Mais tu as déjà l'equivalent d'une globale avec ConnexionSQLite.sharedInstance. On peut l'accéder de n'importe quel code, partout dans le projet.
Tu dis que tu as ton resultSet mais je te demanderais où ça se trouve ? Tu utilises quel code pour créer et stocker le resultSet ?
Tu peux nous montrer le code qui appelle ConnexionSQLite pour faire ce que tu veux ?
Côté questions sur le sujet de colas :
... signifie un dictionnaire avec les clés de type String et les valeurs de type Row.`
... signifie l'initialisation d'une var de ce type avec un dictionnaire vide.
On pourrait aussi écrire :
... où le type de resultSet s'est déduit de la valeur qui est assignée
Je comprends que ma déclaration de variable globale doit ce faire ailleurs, du coup appDelegate (je pense ?) mais quelle type utilisé (Row) et surtout quelle affectation ? puisque visiblement contrairement à ce que je pensais sur swift je dois lui assigné une affectation.
Avant ta réponse je le mettait la déclaration de resultSet dans ConnexionSQLite
et puis son affectation avec tables_data (ma requêtes)
et son appel depuis (par exemple pour moi) ViewController :
Bah non !
Tu confonds toujours beaucoup de choses.
À l'intérieur de la struct ce n'est pas nécessaire d'utiliser sharedInstance, tous le code qui se trouve dans les méthodes de l'instance peuvent utiliser "self", qui fait référence à l'instance actuel.
Du coup, on aurait :
J'ai essayé de recréer les types de GRDB (pour tester ton code), sans inclure GRDB lui-même, dans mon projet mais, malgré tous ça, le compilateur n'aime pas quelques lignes de ton code.
En plus...
Si tu crées ConnexionSQLite comme struct, tu devras marker la méthode getallData() comme mutating et écrire la var resultSet pour permettre la mutation.