accès variables de connexion[réglé]

toolsDevtoolsDev Membre
février 2017 modifié dans Objective-C, Swift, C, C++ #1

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 :)


«13

Réponses

  • Eric P.Eric P. Membre
    janvier 2017 modifié #2

    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.


  • toolsDevtoolsDev Membre
    janvier 2017 modifié #3

    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



    class ConnexionSQLite
    {
    static var sharedInstance = ConnexionSQLite()
    static var dbQueue
    }

    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



    class ConnexionSQLite
    {
    let dbQueue:DatabaseQueue
    static dbQueue:DatabaseQueue
    /****************/

    dbQueue
    self.dbQueue

    /****************/

    {


  • 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


  • toolsDevtoolsDev Membre
    janvier 2017 modifié #5

    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é...


  • Joanna CarterJoanna Carter Membre, Modérateur
    janvier 2017 modifié #6

    Tu dois commencer en apprenant comment écrire un singleton :



    // utilises un struct à  la place d'une classe pour les singletons
    struct ConnexionSQLite
    {
    static let sharedInstance = ConnexionSQLite()

    // lazy var qui crée l'instance de DatabaseQueue au premier appel
    // mais il faut être optional, parce que c'est possible que le try échoue
    lazy var dbQueue: DatabaseQueue? =
    {
    do
    {
    let dbQueue = try DatabaseQueue(path: "games.sqlite3")

    return dbQueue
    }
    catch
    {
    print(error)

    return nil
    }

    }()

    // init doit être private pour que l'on ne puisse pas l'appeler
    // ça force l'utilisation de sharedInstance
    private init() { }

    }

    Pour le reste, tu dois te souvenir que tous les "try" doit être entourés par un do { ... } except { ... }


  • toolsDevtoolsDev Membre
    janvier 2017 modifié #7

    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



    lazy var dbQueue: dbQueue? =

    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 :



    struct ConnexionSQLite
    {
    static let sharedInstance = ConnexionSQLite()
    lazy var dbQueue: DatabaseQueue? // Déclaration globale pour y avoir accès partout
    // erreur: Lazy properties must have an initializer, pourquoi demander une initialisation alors que swift le fait par défaut et de façon dynamique ? (et de toute façon même initialisé à  nil cela plante)


    private init()
    {
    dbQueue =
    {
    do
    {
    dbQueue = try DatabaseQueue(path: "games.sqlite3")
    return dbQueue
    }
    catch
    {
    print(error)
    return nil
    }
    }()
    }

    func getNameFromTable(table:String)
    {
    do
    {
    let rows = try dbQueue.inDatabase /* erreur: Cannot use mutating getter on immutable value: 'self' is immutable*/
    {
    db in try Row.fetchAll(db, "SELECT * FROM \(table)")
    }

    for row in rows
    {
    let nom: String = row.value(named: "nom")
    print(nom)
    }
    }
    catch
    {
    print(error)
    }
    }

    func anotherMethod()
    {

    }
    }

    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,


  • Joanna CarterJoanna Carter Membre, Modérateur
    janvier 2017 modifié #8

    "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 :



    var dbQueue = DatabaseQueue(path: "games.sqlite3")

    ... 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.



    lazy var dbQueue: DatabaseQueue? =
    {
    do
    {
    let dbQueue = try DatabaseQueue(path: "games.sqlite3")

    return dbQueue
    }
    catch
    {
    print(error)

    return nil
    }
    }()


    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.


     




    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.


     


    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)




     


    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 :



    func getNameFromTable(table:String)
    {
    // tester que self.dbQueue ne soit pas nil avant de l'utiliser
    guard let dbQueue = self.dbQueue else
    {
    return
    }

    do
    {
    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)
    }
    }
    catch
    {
    print(error)
    }
    }

  • toolsDevtoolsDev Membre
    janvier 2017 modifié #9


     


     


    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).


     


     


     


     




     


     


    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() :



    let dbQueue = try DatabaseQueue(path: "games.sqlite3")

    Mais accessible depuis n'importe quelles méthodes de cette structure/classe, vois même d'une autre classe. 


  • Joanna CarterJoanna Carter Membre, Modérateur
    janvier 2017 modifié #10

    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 :



    static let sharedInstance = ConnexionSQLite()

    ... 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à .


  • toolsDevtoolsDev Membre
    janvier 2017 modifié #11

    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 :



    struct ConnexionSQLite
    {
    static let sharedInstance = ConnexionSQLite()

    lazy var dbQueue: DatabaseQueue? =
    {
    do
    {
    let dbQueue = try DatabaseQueue(path: "games.sqlite3")
    return dbQueue
    }
    catch
    {
    print(error)
    return nil
    }
    }()

    init()
    {
    // tester que self.dbQueue ne soit pas nil avant de l'utiliser
    guard let dbQueue = self.dbQueue else
    {
    return
    }

    do
    {

    let tables = try dbQueue.inDatabase
    {
    db in
    try Row.fetchAll(db, "SELECT name FROM sqlite_master WHERE type = 'table' AND name <> 'sqlite_sequence'")
    }

    for row in tables
    {
    let table: String = row.value(named: "name")
    print(table)


    let tables_data = try dbQueue.inDatabase
    {
    db in
    try Row.fetchAll(db, "SELECT * FROM \(table)")
    }
    for row in tables_data
    {
    print(row)
    }
    }
    }
    catch
    {
    print(error)
    }
    }``


    et pour en revenir au singletion :



    static let sharedInstance = ConnexionSQLite()


    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.


  • toolsDevtoolsDev Membre
    janvier 2017 modifié #13

    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...


  • colas_colas_ Membre
    janvier 2017 modifié #14

    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.


  • toolsDevtoolsDev Membre
    janvier 2017 modifié #15

    j'aimerais avoir dans mon init() seulement ma connexion :



    init()
    {
    do
    {
    let dbQueue = try DatabaseQueue(path: "games.sqlite3")
    return dbQueue
    }
    catch
    {
    print(error)
    return nil
    }
    }


    et déjà  pour bien comprendre, avoir une méthode différente pour chaque requêtes :



    func getNameFromTable(table:String)
    {
    guard let dbQueue = self.dbQueue else
    {
    return
    }

    do
    {
    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)
    }
    }
    catch
    {
    print(error)
    }
    }

    Ce que m'a conseillé Joanna Carter est de déplacer la closure en début de classe :



    lazy var dbQueue: DatabaseQueue? =
    {
    do
    {
    let dbQueue = try DatabaseQueue(path: "games.sqlite3")
    return dbQueue
    }
    catch
    {
    print(error)
    return nil
    }
    }()

    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,


  • Joanna CarterJoanna Carter Membre, Modérateur
    Qu'est-ce que tu as comme code qui utilise cette classe, et où se trouve-t-il ?
  • toolsDevtoolsDev Membre
    janvier 2017 modifié #17

    import Foundation
    import GRDB

    struct ConnexionSQLite
    {
    static let sharedInstance = ConnexionSQLite()

    lazy var dbQueue: DatabaseQueue? =
    {
    do
    {
    let dbQueue = try DatabaseQueue(path: "games.sqlite3")
    return dbQueue
    }
    catch
    {
    print(error)
    return nil
    }
    }()

    init()
    {
    // teste que self.dbQueue ne soit pas nil avant de l'utiliser
    guard let dbQueue = self.dbQueue else
    {
    return
    }

    do
    {
    // récupère toutes les tables :
    let tables = try dbQueue.inDatabase
    {
    db in
    try Row.fetchAll(db, "SELECT name FROM sqlite_master WHERE type = 'table' AND name <> 'sqlite_sequence'")
    }

    for row in tables
    {
    let table: String = row.value(named: "name")
    // récupère toutes les données de chaque tables :
    let tables_data = try dbQueue.inDatabase
    {
    db in
    try Row.fetchAll(db, "SELECT * FROM \(table)")
    }
    print(tables_data)
    // tables_data contient maintenant toutes les données de toutes mes tables
    je cherche à  avoir tout ça dans une variable (de type Row j'imagine?) accessible depuis l'extérieur.


    }
    }
    catch
    {
    print(error)
    }
    }
    }

    J'ai bien compris le concept du singleton :



    ConnexionSQLite.sharedInstance.dbQueue

    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...


  • colas_colas_ Membre
    janvier 2017 modifié #18





    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.




     


    Non c'est très bien de faire comme ça !



  • import Foundation
    import GRDB

    struct ConnexionSQLite
    {
    static let sharedInstance = ConnexionSQLite()

    var theTablesOfDB: [String:Row] = [:] // pas sûr du type

    lazy var dbQueue: DatabaseQueue? =
    {
    do
    {
    let dbQueue = try DatabaseQueue(path: "games.sqlite3")
    return dbQueue
    }
    catch
    {
    print(error)
    return nil
    }
    }()

    init()
    {
    // teste que self.dbQueue ne soit pas nil avant de l'utiliser
    guard let dbQueue = self.dbQueue else
    {
    return
    }

    do
    {
    // récupère toutes les tables :
    let tables = try dbQueue.inDatabase
    {
    db in
    try Row.fetchAll(db, "SELECT name FROM sqlite_master WHERE type = 'table' AND name <> 'sqlite_sequence'")
    }

    for row in tables
    {
    let table: String = row.value(named: "name")
    // récupère toutes les données de chaque tables :
    let tables_data = try dbQueue.inDatabase
    {
    db in
    try Row.fetchAll(db, "SELECT * FROM \(table)")
    }
    // C'est ici que tu vas remplir ton theTablesOfDB



    }
    }
    catch
    {
    print(error)
    }
    }
    }

    Tu pourras avoir accès aux tables avec 



    ConnexionSQLite.sharedInstance.theTablesOfTheDB

    PS : Désolé je ne parle pas Swift.... ni SQL ;-)


  • toolsDevtoolsDev Membre
    janvier 2017 modifié #20

    je ne savais pas trop quel type utilisé aussi, ayant tenté ceci :




    let data:Row = mais en affection je mettais nil et ça plantais :(

    je vais testé ta proposition, merci !


     


    Edit:


    ça plante :(


    je me renseigne sur les types mais [:] je connais pas.


  • colas_colas_ Membre
    janvier 2017 modifié #21

    Tu ne peux mettre `nil` que aux types du genre `Row?`


  • Joanna CarterJoanna Carter Membre, Modérateur


    J'ai bien compris le concept du singleton :



    ConnexionSQLite.sharedInstance.dbQueue

    Mais je ne vois pas comment m'en servir suivant ce que j'ai expliqué, à  savoir une méthode init() (appelée dans AppDelegate)




     


    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 :



    static let sharedInstance = ConnexionSQLite() 

    ... 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


     




    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.




     


    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.


     




    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.




     


    Supprimes tous le code d'init() et le mettre dans une autre méthode



  • Supprimes tous le code d'init() et le mettre dans une autre méthode




     


    Que tu pourras appeler `func fetchAllTables` par exemple ;-)

  • Joanna CarterJoanna Carter Membre, Modérateur


    Que tu pourras appeler `func fetchAllTables` par exemple ;-)




     


     


    Mais oui 

  • bon je te remercie  :D


     


    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 ;-)


  • toolsDevtoolsDev Membre
    janvier 2017 modifié #27

    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 :



    // Voici donc mes propositions :

    var resultSet:String = ""
    var resultSet:Row = nil
    var resultSet:Row? = nil

    // je ne comprends d'ailleurs pas ce que [:] est colas_ ?
    var resultSet:[String:Row] = [:]
    var resultSet:Row = [:]

    // et pour y accéder :
    ConnexionSQLite.sharedInstance.resultSet

    // et voici comment je réalise l'affectation depuis ma requêtes :
    ConnexionSQLite.sharedInstance.resultSet  = tables_data 

    mais sans succès, le type est faux et l'affectation aussi je pense.

     

    voici l'erreur retournée :

    Cannot assign value of type '[Row]' to type 'Row?'
  • Joanna CarterJoanna Carter Membre, Modérateur
    janvier 2017 modifié #28

    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 :



    [String : Row]

    ... signifie un dictionnaire avec les clés de type String et les valeurs de type Row.`



    var resultSet: [String : Row] = [:]

    ... signifie l'initialisation d'une var de ce type avec un dictionnaire vide.


     


    On pourrait aussi écrire :



    var resultSet = [String : Row]()

    ... où le type de resultSet s'est déduit de la valeur qui est assignée


  • toolsDevtoolsDev Membre
    janvier 2017 modifié #29


    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 ?




     


    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



    struct ConnexionSQLite
    {
    static var sharedInstance = ConnexionSQLite()
    var resultSet:Row?

    /***********/

    et puis son affectation avec tables_data (ma requêtes)



    private init(){}

    func getallData()
    {
    // teste que self.dbQueue ne soit pas nil avant de l'utiliser
    guard let dbQueue = ConnexionSQLite.sharedInstance.dbQueue else
    {
    return
    }

    do
    {
    // récupère toutes les tables :
    let tables = try dbQueue.inDatabase
    {
    db in
    try Row.fetchAll(db, "SELECT name FROM sqlite_master WHERE type = 'table' AND name <> 'sqlite_sequence'")
    }

    for row in tables
    {
    let table: String = row.value(named: "name")

    // récupère toutes les données de chaque tables :
    let tables_data = try dbQueue.inDatabase
    {
    db in
    try Row.fetchAll(db, "SELECT * FROM \(table)")
    }
    // affectation de resultSet avec tables_data (qui serait de type Row) :
    ConnexionSQLite.sharedInstance.resultSet = tables_data
    }
    }
    catch
    {
    print(error)
    }
    }

    et son appel depuis (par exemple pour moi) ViewController :



    override func viewDidLoad()
    {
    super.viewDidLoad()

    let data = ConnexionSQLite.sharedInstance.resultSet
    print(data)
    }

  • Joanna CarterJoanna Carter Membre, Modérateur

    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 :



    struct ConnexionSQLite
    {
    static var sharedInstance = ConnexionSQLite()

    private var dbQueue: DatabaseQueue?

    var resultSet: Row?

    private init(){}

    func getallData()
    {
    guard let dbQueue = self.dbQueue else
    {
    return
    }

    do
    {
    // récupère toutes les tables :
    let tables = try dbQueue.inDatabase
    {
    db in

    try Row.fetchAll(db, "SELECT name FROM sqlite_master WHERE type = 'table' AND name <> 'sqlite_sequence'")
    }

    for row in tables
    {
    let table: String = row.value(named: "name")

    // récupère toutes les données de chaque tables :
    let tables_data = try dbQueue.inDatabase
    {
    db in
    try Row.fetchAll(db, "SELECT * FROM \(table)")
    }

    self.resultSet = tables_data
    }
    }
    catch
    {
    print(error)
    }
    }
    }

    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.


  • Joanna CarterJoanna Carter Membre, Modérateur

    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.



    private var _resultSet: Row?

    var resultSet: Row?
    {
    get
    {
    return _resultSet
    }
    mutating set
    {
    _resultSet = newValue
    }
    }


    mutating func getallData()
    {
    ...
    }
Connectez-vous ou Inscrivez-vous pour répondre.