Accéder à une base de données SQLite dans Swift

103

Je recherche un moyen d'accéder à une base de données SQLite dans mon application avec du code Swift.

Je sais que je peux utiliser un SQLite Wrapper dans Objective C et utiliser l'en-tête de pontage, mais je préférerais pouvoir faire ce projet entièrement dans Swift. Existe-t-il un moyen de le faire, si oui, quelqu'un peut-il me diriger vers une référence qui montre comment soumettre une requête, récupérer des lignes, etc.?

Jase
la source
1
où dois-je mettre mon fichier de base de données?
C. Feliana
1
@ C.Feliana - Le répertoire de support d'application est un excellent endroit, par exemple let dbPath = try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("test.sqlite").path.
Rob

Réponses:

143

Alors que vous devriez probablement utiliser l'un des nombreux wrappers SQLite, si vous vouliez savoir comment appeler la bibliothèque SQLite vous-même, vous:

  1. Configurez votre projet Swift pour gérer les appels SQLite C. Si vous utilisez Xcode 9 ou version ultérieure, vous pouvez simplement faire:

    import SQLite3
  2. Créer / ouvrir une base de données.

    let fileURL = try! FileManager.default
        .url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
        .appendingPathComponent("test.sqlite")
    
    // open database
    
    var db: OpaquePointer?
    guard sqlite3_open(fileURL.path, &db) == SQLITE_OK else {
        print("error opening database")
        sqlite3_close(db)
        db = nil
        return
    }

    Remarque, je sais qu'il semble étrange de fermer la base de données en cas d'échec de l'ouverture, mais le sqlite3_open documentation précise que nous devons le faire pour éviter les fuites de mémoire:

    Qu'une erreur se produise ou non lors de son ouverture, les ressources associées au descripteur de connexion à la base de données doivent être libérées en le transmettant àsqlite3_close() lorsqu'il n'est plus nécessaire.

  3. Utilisez sqlite3_execpour exécuter SQL (par exemple créer une table).

    if sqlite3_exec(db, "create table if not exists test (id integer primary key autoincrement, name text)", nil, nil, nil) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error creating table: \(errmsg)")
    }
  4. Utilisez sqlite3_prepare_v2pour préparer SQL avec? espace réservé auquel nous lierons la valeur.

    var statement: OpaquePointer?
    
    if sqlite3_prepare_v2(db, "insert into test (name) values (?)", -1, &statement, nil) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error preparing insert: \(errmsg)")
    }
    
    if sqlite3_bind_text(statement, 1, "foo", -1, SQLITE_TRANSIENT) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("failure binding foo: \(errmsg)")
    }
    
    if sqlite3_step(statement) != SQLITE_DONE {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("failure inserting foo: \(errmsg)")
    }

    Notez que cela utilise la SQLITE_TRANSIENTconstante qui peut être implémentée comme suit:

    internal let SQLITE_STATIC = unsafeBitCast(0, to: sqlite3_destructor_type.self)
    internal let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
  5. Réinitialisez SQL pour insérer une autre valeur. Dans cet exemple, je vais insérer une NULLvaleur:

    if sqlite3_reset(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error resetting prepared statement: \(errmsg)")
    }
    
    if sqlite3_bind_null(statement, 1) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("failure binding null: \(errmsg)")
    }
    
    if sqlite3_step(statement) != SQLITE_DONE {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("failure inserting null: \(errmsg)")
    }
  6. Finalisez l'instruction préparée pour récupérer la mémoire associée à cette instruction préparée:

    if sqlite3_finalize(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error finalizing prepared statement: \(errmsg)")
    }
    
    statement = nil
  7. Préparez une nouvelle instruction pour sélectionner les valeurs de la table et bouclez en récupérant les valeurs:

    if sqlite3_prepare_v2(db, "select id, name from test", -1, &statement, nil) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error preparing select: \(errmsg)")
    }
    
    while sqlite3_step(statement) == SQLITE_ROW {
        let id = sqlite3_column_int64(statement, 0)
        print("id = \(id); ", terminator: "")
    
        if let cString = sqlite3_column_text(statement, 1) {
            let name = String(cString: cString)
            print("name = \(name)")
        } else {
            print("name not found")
        }
    }
    
    if sqlite3_finalize(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error finalizing prepared statement: \(errmsg)")
    }
    
    statement = nil
  8. Fermer la base de données:

    if sqlite3_close(db) != SQLITE_OK {
        print("error closing database")
    }
    
    db = nil

Pour Swift 2 et les versions antérieures de Xcode, consultez les révisions précédentes de cette réponse .

Rob
la source
1
Pour ceux qui ont rencontré des problèmes lors du passage 1, considérez ceci: Créez un en-tête de pontage dans votre projet Xcode (par exemple BridgingHeader.h); Ce fichier d'en-tête ne peut avoir que des lignes d'import d'en-têtes Objective-C / C pour le pontage vers Swift (par exemple #include <sqlite3.h>); Sur "Build Settings", recherchez "Objective-C Bridging Header" (vous pouvez utiliser la barre de recherche) et tapez "BridgingHeader.h" (si vous obtenez un message d'erreur comme "Impossible d'importer Objective-C Header", essayez "project- nom / BridgingHeader.h "); Allez dans "Build Phases", "Link Binary With Libraries", et ajoutez libsqlite3.0.dylib ou libsqlite3.0.tbd dans XCode 7
Jorg B Jorge
Serait-il préférable d'imbriquer le if (... == SQLITE_OK) afin que ce qui suit ne s'exécute pas en cas d'échec. Je demande simplement parce que je suis très nouveau dans ce domaine et que j'étais simplement curieux de savoir si vous l'avez fait à des fins d'enseignement.
quemeful le
@quemeful - Bien sûr, mais si vous faites cela avec de nombreux appels SQLite, vous vous retrouvez avec un code vraiment profondément imbriqué. Si cela vous inquiète, j'utiliserais probablement des guarddéclarations à la place.
Rob le
@Jorg B Jorge J'ai tout fait, avez-vous également besoin d'importer un en-tête de pontage? Je travaille dans la classe de test
Async-
Salut @Rob, j'utilise votre wrapper sqlite dans un projet Swift ici. C'est vraiment sympa, merci. Cependant, je ne peux pas faire un décompte sélectionné (*) de la table avec. Il continue de s'écraser. Si j'ai fait un décompte sélectionné (col_name) à partir de nom_table où some_col = xxx, cela fonctionne. Que suggérez-vous?
gbenroscience
18

Le mieux que vous puissiez faire est d'importer la bibliothèque dynamique dans un en-tête de pontage:

  1. Ajoutez libsqlite3.dylib à votre phase de construction "Link Binary With Libraries"
  2. Créez un "Bridging-Header.h" et ajoutez #import <sqlite3.h> le en haut
  3. définissez "Bridging-Header.h" pour le paramètre "Objective-C Bridging Header" dans les paramètres de construction sous "Swift Compiler - Code Generation"

Vous pourrez alors accéder à toutes les méthodes c comme sqlite3_open partir de votre code swift.

Cependant, vous voudrez peut-être simplement utiliser FMDB et l'importer via l'en-tête de pontage car il s'agit d'un wrapper plus orienté objet de sqlite. La gestion des pointeurs et des structures C sera fastidieuse dans Swift.

dessiner
la source
J'ai dû ajouter ceci sous les paramètres de construction du projet et non dans les paramètres de construction cible pour que Xcode trouve l'en-tête de pontage.
rob5408
3
aussi tout le monde et leur père ont maintenant créé un emballage Swift .. voir ci
quemeful
1
Malheureusement, aucun d'entre eux n'est terriblement mature, donc si vous utilisez l'un de ces nouveaux emballages, soyez prudent. Par exemple, au moment de la rédaction de cet article, j'ai jeté un coup d'œil à quatre d'entre eux, et trois ont mal géré les dates et le quatrième ne les a pas du tout gérées.
Rob
@Rob Avez-vous regardé github.com/stephencelis/SQLite.swift#readme ? Informations sur la configuration à utiliser avec NSDate ici: github.com/stephencelis/SQLite.swift/blob/master/Documentation
...
@stephencelis Hé, c'est mieux que la plupart d'entre eux, car au moins vous spécifiez le fuseau horaire, mais j'ai toujours des problèmes avec ça NSDateFormatter. Mais mon intention était moins de critiquer cet aspect particulier de ces implémentations particulières que de suggérer que cela indique un problème plus large, que celles-ci n'ont pas les années de raffinement que les solutions comme la FMDB ont. Je pense que les gens sont trop prompts à abandonner les solutions Objective-C éprouvées au profit d'implémentations Swift moins matures (TFHpple vs NDHpple sont un autre bon exemple).
Rob
11

Moi aussi, je cherchais un moyen d'interagir avec SQLite de la même manière que j'avais l'habitude de faire auparavant en Objective-C. Certes, en raison de la compatibilité C, je viens d'utiliser l'API C simple.

Comme aucun wrapper n'existe actuellement pour SQLite dans Swift et que le code SQLiteDB mentionné ci-dessus va un peu plus haut et suppose une certaine utilisation, j'ai décidé de créer un wrapper et de me familiariser un peu avec Swift dans le processus. Vous pouvez le trouver ici: https://github.com/chrismsimpson/SwiftSQLite .

var db = SQLiteDatabase();
db.open("/path/to/database.sqlite");

var statement = SQLiteStatement(database: db);

if ( statement.prepare("SELECT * FROM tableName WHERE Id = ?") != .Ok )
{
    /* handle error */
}

statement.bindInt(1, value: 123);

if ( statement.step() == .Row )
{
    /* do something with statement */
    var id:Int = statement.getIntAt(0)
    var stringValue:String? = statement.getStringAt(1)
    var boolValue:Bool = statement.getBoolAt(2)
    var dateValue:NSDate? = statement.getDateAt(3)
}

statement.finalizeStatement(); /* not called finalize() due to destructor/language keyword */
Chris Simpson
la source
5

J'ai créé une élégante bibliothèque SQLite entièrement écrite en Swift appelée SwiftData .

Certaines de ses caractéristiques sont:

  • Liez facilement les objets à la chaîne de SQL
  • Prise en charge des transactions et des points de sauvegarde
  • Gestion des erreurs en ligne
  • Complètement thread-safe par défaut

Il fournit un moyen simple d'exécuter des 'changements' (par exemple INSERT, UPDATE, DELETE, etc.):

if let err = SD.executeChange("INSERT INTO Cities (Name, Population, IsWarm, FoundedIn) VALUES ('Toronto', 2615060, 0, '1793-08-27')") {
    //there was an error during the insert, handle it here
} else {
    //no error, the row was inserted successfully
}

et 'requêtes' (par exemple SELECT):

let (resultSet, err) = SD.executeQuery("SELECT * FROM Cities")
if err != nil {
    //there was an error during the query, handle it here
} else {
    for row in resultSet {
        if let name = row["Name"].asString() {
            println("The City name is: \(name)")
        }
        if let population = row["Population"].asInt() {
            println("The population is: \(population)")
        }
        if let isWarm = row["IsWarm"].asBool() {
            if isWarm {
                println("The city is warm")
            } else {
                println("The city is cold")
            }
        }
        if let foundedIn = row["FoundedIn"].asDate() {
            println("The city was founded in: \(foundedIn)")
        }
    }
}

Avec de nombreuses autres fonctionnalités!

Vous pouvez le vérifier ici

Ryanfowler
la source
Malheureusement, votre bibliothèque est uniquement iOS! : - /
BadmintonCat
3

Encore un autre wrapper SQLite pour Swift 2 et Swift 3: http://github.com/groue/GRDB.swift

Fonctionnalités:

  • Une API qui semblera familière aux utilisateurs de ccgus / fmdb

  • Une API SQLite de bas niveau qui exploite la bibliothèque standard Swift

  • Une jolie interface de requête Swift pour les développeurs allergiques à SQL

  • Prise en charge du mode SQLite WAL et accès simultané à la base de données pour des performances supplémentaires

  • Une classe Record qui encapsule les ensembles de résultats, mange vos requêtes SQL personnalisées pour le petit-déjeuner et fournit des opérations CRUD de base

  • Liberté de type Swift: choisissez le type Swift qui correspond à vos données. Utilisez Int64 si nécessaire, ou restez avec le pratique Int. Stockez et lisez NSDate ou NSDateComponents. Déclarez les énumérations Swift pour les types de données discrets. Définissez vos propres types de base de données convertibles.

  • Migrations de bases de données

  • Vitesse: https://github.com/groue/GRDB.swift/wiki/Performance

Gwendal Roué
la source
GRDB est l'un des frameworks les mieux documentés, pris en charge et maintenus sur Github!
Klaas
3

AppDelegate.swift

func createDatabase()
{
    var path:Array=NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
    let directory:String=path[0]
    let DBpath=(directory as NSString).appendingPathComponent("Food.sqlite")

    print(DBpath)

    if (FileManager.default.fileExists(atPath: DBpath))
    {
        print("Successfull database create")
    }
    else
    {
        let pathfrom:String=(Bundle.main.resourcePath! as NSString).appendingPathComponent("Food.sqlite")

        var success:Bool
        do {
            try FileManager.default.copyItem(atPath: pathfrom, toPath: DBpath)
            success = true
        } catch _ {
            success = false
        }

        if !success
        {
            print("database not create ")
        }
        else
        {
            print("Successfull database new create")
        }
    }
}

Database.swift

import UIKit

class database: NSObject
{
func databasePath() -> NSString
{
    var path:Array=NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
    let directory:String=path[0] 
    let DBpath=(directory as NSString).appendingPathComponent("Food.sqlite")

    if (FileManager.default.fileExists(atPath: DBpath))
    {
        return DBpath as NSString
    }
    return DBpath as NSString
}

func ExecuteQuery(_ str:String) -> Bool
{
    var result:Bool=false
    let DBpath:String=self.databasePath() as String

    var db: OpaquePointer? = nil
    var stmt:OpaquePointer? = nil

    let strExec=str.cString(using: String.Encoding.utf8)

    if (sqlite3_open(DBpath, &db)==SQLITE_OK)
    {
        if (sqlite3_prepare_v2(db, strExec! , -1, &stmt, nil) == SQLITE_OK)
        {
            if (sqlite3_step(stmt) == SQLITE_DONE)
            {
                result=true
            } 
        }
        sqlite3_finalize(stmt)
    }
    sqlite3_close(db)

    return result
}

func SelectQuery(_ str:String) -> Array<Dictionary<String,String>>
{
    var result:Array<Dictionary<String,String>>=[]
    let DBpath:String=self.databasePath() as String

    var db: OpaquePointer? = nil
    var stmt:OpaquePointer? = nil

    let strExec=str.cString(using: String.Encoding.utf8)

    if ( sqlite3_open(DBpath,&db) == SQLITE_OK)
    {
        if (sqlite3_prepare_v2(db, strExec! , -1, &stmt, nil) == SQLITE_OK)
        {
            while (sqlite3_step(stmt) == SQLITE_ROW)
            {
                var i:Int32=0
                let icount:Int32=sqlite3_column_count(stmt)

                var dict=Dictionary<String, String>()

                while i < icount
                {
                    let strF=sqlite3_column_name(stmt, i)
                    let strV = sqlite3_column_text(stmt, i)

                    let rFiled:String=String(cString: strF!)
                    let rValue:String=String(cString: strV!)
                    //let rValue=String(cString: UnsafePointer<Int8>(strV!))

                    dict[rFiled] = rValue

                    i += 1
                }
                result.insert(dict, at: result.count)
            }
        sqlite3_finalize(stmt)
        }

    sqlite3_close(db)
    }
    return result
}

func AllSelectQuery(_ str:String) -> Array<Model>
{
    var result:Array<Model>=[]
    let DBpath:String=self.databasePath() as String

    var db: OpaquePointer? = nil
    var stmt:OpaquePointer? = nil

    let strExec=str.cString(using: String.Encoding.utf8)

    if ( sqlite3_open(DBpath,&db) == SQLITE_OK)
    {
        if (sqlite3_prepare_v2(db, strExec! , -1, &stmt, nil) == SQLITE_OK)
        {
            while (sqlite3_step(stmt) == SQLITE_ROW)
            {
                let mod=Model()

                mod.id=String(cString: sqlite3_column_text(stmt, 0))
                mod.image=String(cString: sqlite3_column_text(stmt, 1))
                mod.name=String(cString: sqlite3_column_text(stmt, 2))
                mod.foodtype=String(cString: sqlite3_column_text(stmt, 3))
                mod.vegtype=String(cString: sqlite3_column_text(stmt, 4))
                mod.details=String(cString: sqlite3_column_text(stmt, 5))

                result.insert(mod, at: result.count)
            }
            sqlite3_finalize(stmt)
        }
        sqlite3_close(db)
    }
    return result
}

}

Modèle.swift

import UIKit


class Model: NSObject
{
var uid:Int = 0
var id:String = ""
var image:String = ""
var name:String = ""
var foodtype:String = ""
var vegtype:String = ""
var details:String = ""
var mealtype:String = ""
var date:String = ""
}

Accéder à la base de données:

let DB=database()
var mod=Model()

feu de requête de base de données:

var DailyResult:Array<Model> = DB.AllSelectQuery("select * from food where foodtype == 'Sea Food' ORDER BY name ASC")
Jayesh Miruliya
la source
cette qyery ne fonctionne pas. pourquoi il y a == au lieu d'un seul =?
ArgaPK
1

C'est de loin la meilleure bibliothèque SQLite que j'ai utilisée dans Swift: https://github.com/stephencelis/SQLite.swift

Regardez les exemples de code. Tellement plus propre que l'API C:

import SQLite

let db = try Connection("path/to/db.sqlite3")

let users = Table("users")
let id = Expression<Int64>("id")
let name = Expression<String?>("name")
let email = Expression<String>("email")

try db.run(users.create { t in
    t.column(id, primaryKey: true)
    t.column(name)
    t.column(email, unique: true)
})
// CREATE TABLE "users" (
//     "id" INTEGER PRIMARY KEY NOT NULL,
//     "name" TEXT,
//     "email" TEXT NOT NULL UNIQUE
// )

let insert = users.insert(name <- "Alice", email <- "[email protected]")
let rowid = try db.run(insert)
// INSERT INTO "users" ("name", "email") VALUES ('Alice', '[email protected]')

for user in try db.prepare(users) {
    print("id: \(user[id]), name: \(user[name]), email: \(user[email])")
    // id: 1, name: Optional("Alice"), email: [email protected]
}
// SELECT * FROM "users"

let alice = users.filter(id == rowid)

try db.run(alice.update(email <- email.replace("mac.com", with: "me.com")))
// UPDATE "users" SET "email" = replace("email", 'mac.com', 'me.com')
// WHERE ("id" = 1)

try db.run(alice.delete())
// DELETE FROM "users" WHERE ("id" = 1)

try db.scalar(users.count) // 0
// SELECT count(*) FROM "users"

La documentation indique également que «SQLite.swift fonctionne également comme un wrapper léger et convivial sur l'API C», et suit avec quelques exemples de cela.

Andrew Koster
la source
0

J'ai écrit une bibliothèque de wrapper SQLite3 écrite en Swift .

Il s'agit en fait d'un wrapper de très haut niveau avec une API très simple, mais de toute façon, il a un code d'interopérabilité C de bas niveau, et j'en publie ici une partie (simplifiée) pour montrer l'interopérabilité C.

    struct C
    {
        static let  NULL        =   COpaquePointer.null()
    }

    func open(filename:String, flags:OpenFlag)
    {
        let name2   =   filename.cStringUsingEncoding(NSUTF8StringEncoding)!
        let r       =   sqlite3_open_v2(name2, &_rawptr, flags.value, UnsafePointer<Int8>.null())
        checkNoErrorWith(resultCode: r)
    }

    func close()
    {   
        let r   =   sqlite3_close(_rawptr)
        checkNoErrorWith(resultCode: r)
        _rawptr =   C.NULL
    }

    func prepare(SQL:String) -> (statements:[Core.Statement], tail:String)
    {
        func once(zSql:UnsafePointer<Int8>, len:Int32, inout zTail:UnsafePointer<Int8>) -> Core.Statement?
        {
            var pStmt   =   C.NULL
            let r       =   sqlite3_prepare_v2(_rawptr, zSql, len, &pStmt, &zTail)
            checkNoErrorWith(resultCode: r)

            if pStmt == C.NULL
            {
                return  nil
            }
            return  Core.Statement(database: self, pointerToRawCStatementObject: pStmt)
        }

        var stmts:[Core.Statement]  =   []
        let sql2    =   SQL as NSString
        var zSql    =   UnsafePointer<Int8>(sql2.UTF8String)
        var zTail   =   UnsafePointer<Int8>.null()
        var len1    =   sql2.lengthOfBytesUsingEncoding(NSUTF8StringEncoding);
        var maxlen2 =   Int32(len1)+1

        while let one = once(zSql, maxlen2, &zTail)
        {
            stmts.append(one)
            zSql    =   zTail
        }

        let rest1   =   String.fromCString(zTail)
        let rest2   =   rest1 == nil ? "" : rest1!

        return  (stmts, rest2)
    }

    func step() -> Bool
    {   
        let rc1 =   sqlite3_step(_rawptr)

        switch rc1
        {   
            case SQLITE_ROW:
                return  true

            case SQLITE_DONE:
                return  false

            default:
                database.checkNoErrorWith(resultCode: rc1)
        }
    }

    func columnText(at index:Int32) -> String
    {
        let bc  =   sqlite3_column_bytes(_rawptr, Int32(index))
        let cs  =   sqlite3_column_text(_rawptr, Int32(index))

        let s1  =   bc == 0 ? "" : String.fromCString(UnsafePointer<CChar>(cs))!
        return  s1
    }

    func finalize()
    {
        let r   =   sqlite3_finalize(_rawptr)
        database.checkNoErrorWith(resultCode: r)

        _rawptr =   C.NULL
    }

Si vous voulez un code source complet de ce wrapper de bas niveau, consultez ces fichiers.

éonil
la source
0

Configurez votre projet Swift pour gérer les appels SQLite C:

Créez un fichier d'en-tête de pontage vers le projet. Consultez la section Importation d'Objective-C dans Swift de l'utilisation de Swift avec Cocoa et Objective-C. Cet en-tête de pontage doit importer sqlite3.h:

Ajoutez la libsqlite3.0.dylib à votre projet. Consultez la documentation d'Apple concernant l'ajout d'une bibliothèque / d'un framework à son projet.

et utilisé le code suivant

    func executeQuery(query: NSString ) -> Int
    {
        if  sqlite3_open(databasePath! as String, &database) != SQLITE_OK
        {
            println("Databse is not open")
            return 0
        }
        else
        {
            query.stringByReplacingOccurrencesOfString("null", withString: "")
            var cStatement:COpaquePointer = nil
            var executeSql = query as NSString
            var lastId : Int?
            var sqlStatement = executeSql.cStringUsingEncoding(NSUTF8StringEncoding)
            sqlite3_prepare_v2(database, sqlStatement, -1, &cStatement, nil)
            var execute = sqlite3_step(cStatement)
            println("\(execute)")
            if execute == SQLITE_DONE
            {
                lastId = Int(sqlite3_last_insert_rowid(database))
            }
            else
            {
                println("Error in Run Statement :- \(sqlite3_errmsg16(database))")
            }
            sqlite3_finalize(cStatement)
            return lastId!
        }
    }
    func ViewAllData(query: NSString, error: NSError) -> NSArray
    {
        var cStatement = COpaquePointer()
        var result : AnyObject = NSNull()
        var thisArray : NSMutableArray = NSMutableArray(capacity: 4)
        cStatement = prepare(query)
        if cStatement != nil
        {
            while sqlite3_step(cStatement) == SQLITE_ROW
            {
                result = NSNull()
                var thisDict : NSMutableDictionary = NSMutableDictionary(capacity: 4)
                for var i = 0 ; i < Int(sqlite3_column_count(cStatement)) ; i++
                {
                    if sqlite3_column_type(cStatement, Int32(i)) == 0
                    {
                        continue
                    }
                    if sqlite3_column_decltype(cStatement, Int32(i)) != nil && strcasecmp(sqlite3_column_decltype(cStatement, Int32(i)), "Boolean") == 0
                    {
                        var temp = sqlite3_column_int(cStatement, Int32(i))
                        if temp == 0
                        {
                            result = NSNumber(bool : false)
                        }
                        else
                        {
                            result = NSNumber(bool : true)
                        }
                    }
                    else if sqlite3_column_type(cStatement,Int32(i)) == SQLITE_INTEGER
                    {
                        var temp = sqlite3_column_int(cStatement,Int32(i))
                        result = NSNumber(int : temp)
                    }
                    else if sqlite3_column_type(cStatement,Int32(i)) == SQLITE_FLOAT
                    {
                        var temp = sqlite3_column_double(cStatement,Int32(i))
                        result = NSNumber(double: temp)
                    }
                    else
                    {
                        if sqlite3_column_text(cStatement, Int32(i)) != nil
                        {
                            var temp = sqlite3_column_text(cStatement,Int32(i))
                            result = String.fromCString(UnsafePointer<CChar>(temp))!
                            
                            var keyString = sqlite3_column_name(cStatement,Int32(i))
                            thisDict.setObject(result, forKey: String.fromCString(UnsafePointer<CChar>(keyString))!)
                        }
                        result = NSNull()

                    }
                    if result as! NSObject != NSNull()
                    {
                        var keyString = sqlite3_column_name(cStatement,Int32(i))
                        thisDict.setObject(result, forKey: String.fromCString(UnsafePointer<CChar>(keyString))!)
                    }
                }
                thisArray.addObject(NSMutableDictionary(dictionary: thisDict))
            }
            sqlite3_finalize(cStatement)
        }
        return thisArray
    }
    func prepare(sql : NSString) -> COpaquePointer
    {
        var cStatement:COpaquePointer = nil
        sqlite3_open(databasePath! as String, &database)
        var utfSql = sql.UTF8String
        if sqlite3_prepare(database, utfSql, -1, &cStatement, nil) == 0
        {
            sqlite3_close(database)
            return cStatement
        }
        else
        {
            sqlite3_close(database)
            return nil
        }
    }
}
chirag shah
la source
0

Parfois, une version Swift de l' approche «SQLite en 5 minutes ou moins» présentée sur sqlite.org est suffisante. Les « 5 minutes ou moins » utilisations d'approche sqlite3_exec()qui est un emballage pratique pour sqlite3_prepare(), sqlite3_step(), sqlite3_column()et sqlite3_finalize().

Swift 2.2 peut directement prendre en charge le sqlite3_exec() callbackpointeur de fonction soit comme une procédure globale, non-instance, funcsoit comme une fermeture littérale non capturante {}.

Lisible typealias

typealias sqlite3 = COpaquePointer
typealias CCharHandle = UnsafeMutablePointer<UnsafeMutablePointer<CChar>>
typealias CCharPointer = UnsafeMutablePointer<CChar>
typealias CVoidPointer = UnsafeMutablePointer<Void>

Approche de rappel

func callback(
    resultVoidPointer: CVoidPointer, // void *NotUsed 
    columnCount: CInt,               // int argc
    values: CCharHandle,             // char **argv     
    columns: CCharHandle             // char **azColName
    ) -> CInt {
    for  i in 0 ..< Int(columnCount) {
        guard let value = String.fromCString(values[i]) 
        else { continue }
        guard let column = String.fromCString(columns[i]) 
        else { continue }
        print("\(column) = \(value)")
    }
    return 0 // status ok
}

func sqlQueryCallbackBasic(argc: Int, argv: [String]) -> Int {
    var db: sqlite3 = nil 
    var zErrMsg:CCharPointer = nil
    var rc: Int32 = 0 // result code

    if argc != 3 {
        print(String(format: "ERROR: Usage: %s DATABASE SQL-STATEMENT", argv[0]))
        return 1
    }

    rc = sqlite3_open(argv[1], &db)
    if  rc != 0 {
        print("ERROR: sqlite3_open " + String.fromCString(sqlite3_errmsg(db))! ?? "" )
        sqlite3_close(db)
        return 1
    }

    rc = sqlite3_exec(db, argv[2], callback, nil, &zErrMsg)
    if rc != SQLITE_OK {
        print("ERROR: sqlite3_exec " + String.fromCString(zErrMsg)! ?? "")
        sqlite3_free(zErrMsg)
    }

    sqlite3_close(db)
    return 0
}

Approche de fermeture

func sqlQueryClosureBasic(argc argc: Int, argv: [String]) -> Int {
    var db: sqlite3 = nil 
    var zErrMsg:CCharPointer = nil
    var rc: Int32 = 0

    if argc != 3 {
        print(String(format: "ERROR: Usage: %s DATABASE SQL-STATEMENT", argv[0]))
        return 1
    }

    rc = sqlite3_open(argv[1], &db)
    if  rc != 0 {
        print("ERROR: sqlite3_open " + String.fromCString(sqlite3_errmsg(db))! ?? "" )
        sqlite3_close(db)
        return 1
    }

    rc = sqlite3_exec(
        db,      // database 
        argv[2], // statement
        {        // callback: non-capturing closure
            resultVoidPointer, columnCount, values, columns in

            for i in 0 ..< Int(columnCount) {
                guard let value = String.fromCString(values[i]) 
                else { continue }
                guard let column = String.fromCString(columns[i]) 
                else { continue }
                print("\(column) = \(value)")
            }
            return 0
        }, 
        nil, 
        &zErrMsg
    )

    if rc != SQLITE_OK {
        let errorMsg = String.fromCString(zErrMsg)! ?? ""
        print("ERROR: sqlite3_exec \(errorMsg)")
        sqlite3_free(zErrMsg)
    }
    sqlite3_close(db)
    return 0
}

Pour préparer un projet Xcode à appeler une bibliothèque C telle que SQLite, il faut (1) ajouter des en-têtes C de référence de fichier Bridging-Header.h comme #import "sqlite3.h", (2) ajouter Bridging-Header.h à Objective-C Bridging Header dans le projet paramètres, et (3) ajouter libsqlite3.tbdà Link Binary With Library paramètres cibles .

Le sqlite.org de « SQLite en 5 minutes ou moins » par exemple est mis en œuvre dans un projet Swift Xcode7 ici .

l --marc l
la source
0

Vous pouvez utiliser cette bibliothèque dans Swift pour SQLite https://github.com/pmurphyjam/SQLiteDemo

SQLiteDemo

Démonstration SQLite utilisant Swift avec la classe SQLDataAccess écrite en Swift

Ajout à votre projet

Vous n'avez besoin que de trois fichiers à ajouter à votre projet * SQLDataAccess.swift * DataConstants.swift * Bridging-Header.h Bridging-Header doit être défini dans votre projet Xcode 'Objective-C Bridging Header' sous 'Swift Compiler - General'

Exemples d'utilisation

Suivez simplement le code dans ViewController.swift pour voir comment écrire du SQL simple avec SQLDataAccess.swift Vous devez d'abord ouvrir la base de données SQLite avec laquelle vous traitez

    let db = SQLDataAccess.shared
    db.setDBName(name:"SQLite.db")
    let opened = db.openConnection(copyFile:true)

Si openConnection réussit, vous pouvez maintenant faire une simple insertion dans Table AppInfo

    //Insert into Table AppInfo
    let status = db.executeStatement("insert into AppInfo (name,value,descrip,date) values(?,?,?,?)",SQLiteDemo","1.0.2","unencrypted",Date())
    if(status)
    {
        //Read Table AppInfo into an Array of Dictionaries
        let results = db.getRecordsForQuery("select * from AppInfo ")
        NSLog("Results = \(results)")
    }

Voyez comme c'était simple!

Le premier terme de db.executeStatement est votre SQL sous forme de chaîne, tous les termes qui suivent sont une liste d'arguments variadiques de type Any et sont vos paramètres dans un tableau. Tous ces termes sont séparés par des virgules dans votre liste d'arguments SQL. Vous pouvez entrer des chaînes, des entiers, des dates et des objets blob juste après l'instruction de suite, car tous ces termes sont considérés comme des paramètres de la suite. Le tableau d'arguments variadiques permet simplement de saisir toute votre suite en un seul appel executeStatement ou getRecordsForQuery. Si vous n'avez aucun paramètre, n'entrez rien après votre SQL.

Le tableau de résultats est un tableau de dictionnaires où la «clé» est le nom de la colonne de votre table et la «valeur» est vos données obtenues à partir de SQLite. Vous pouvez facilement parcourir ce tableau avec une boucle for ou l'imprimer directement ou affecter ces éléments Dictionary à des classes d'objets de données personnalisées que vous utilisez dans vos contrôleurs d'affichage pour la consommation de modèles.

    for dic in results as! [[String:AnyObject]] {
       print(“result = \(dic))
    }

SQLDataAccess stockera, texte, double, flottant, blob, date, entier et longs entiers longs. Pour les objets blob, vous pouvez stocker des objets binaires, varbinary, blob.

Pour le texte, vous pouvez stocker char, caractère, clob, caractère variant national, caractère natif, nchar, nvarchar, varchar, variante, caractère variable, texte.

Pour les dates, vous pouvez stocker la date, l'heure, l'horodatage et la date.

Pour les entiers, vous pouvez stocker bigint, bit, bool, boolean, int2, int8, integer, mediumint, smallint, tinyint, int.

Pour les doubles, vous pouvez stocker des nombres décimaux, double précision, flottants, numériques, réels, doubles. Double a le plus de précision.

Vous pouvez même stocker des Nulls de type Null.

Dans ViewController.swift, un exemple plus complexe montre comment insérer un dictionnaire en tant que «Blob». En outre, SQLDataAccess comprend la date Swift native () afin que vous puissiez insérer ces objets sans conversion, et il les convertira en texte et les stockera, et une fois récupérés, les reconvertira du texte en date.

Bien sûr, la vraie puissance de SQLite est sa capacité de transaction. Ici, vous pouvez littéralement mettre en file d'attente 400 instructions SQL avec des paramètres et les insérer toutes en même temps, ce qui est vraiment puissant car c'est si rapide. ViewController.swift vous montre également un exemple de la façon de procéder. Tout ce que vous faites vraiment est de créer un tableau de dictionnaires appelé 'sqlAndParams', dans ce tableau, vos dictionnaires de stockage avec deux clés 'SQL' pour l'instruction ou la requête String sequel, et 'PARAMS' qui est juste un tableau d'objets natifs SQLite comprend pour cette requête. Chaque 'sqlParams' qui est un dictionnaire individuel de la requête de suite et des paramètres est ensuite stocké dans le tableau 'sqlAndParams'. Une fois que vous avez créé ce tableau, il vous suffit d'appeler.

    let status = db.executeTransaction(sqlAndParams)
    if(status)
    {
        //Read Table AppInfo into an Array of Dictionaries for the above Transactions
        let results = db.getRecordsForQuery("select * from AppInfo ")
        NSLog("Results = \(results)")
    }

En outre, toutes les méthodes executeStatement et getRecordsForQuery peuvent être effectuées avec une simple String pour une requête SQL et un tableau pour les paramètres nécessaires à la requête.

    let sql : String = "insert into AppInfo (name,value,descrip) values(?,?,?)"
    let params : Array = ["SQLiteDemo","1.0.0","unencrypted"]
    let status = db.executeStatement(sql, withParameters: params)
    if(status)
    {
        //Read Table AppInfo into an Array of Dictionaries for the above Transactions
        let results = db.getRecordsForQuery("select * from AppInfo ")
        NSLog("Results = \(results)")
    }

Une version Objective-C existe également et s'appelle le même SQLDataAccess, vous pouvez donc maintenant choisir d'écrire votre suite en Objective-C ou Swift. De plus, SQLDataAccess fonctionnera également avec SQLCipher, le code actuel n'est pas encore configuré pour fonctionner avec lui, mais c'est assez facile à faire, et un exemple de la façon de le faire se trouve en fait dans la version Objective-C de SQLDataAccess.

SQLDataAccess est une classe très rapide et efficace, et peut être utilisée à la place de CoreData qui utilise vraiment juste SQLite car c'est le magasin de données sous-jacent sans tous les pannes d'intégrité des données CoreData qui accompagnent CoreData.

Gel
la source
-1

Vous pouvez également facilement configurer SQLite avec swift en utilisant une classe de tonne unique.

Référer

https://github.com/hasyapanchasara/SQLite_SingleManagerClass

Méthode pour créer une base de données

func methodToCreateDatabase() -> NSURL?{} 

Méthode pour insérer, mettre à jour et supprimer des données

func methodToInsertUpdateDeleteData(strQuery : String) -> Bool{}

Méthode de sélection des données

func methodToSelectData(strQuery : String) -> NSMutableArray{}
Hasya
la source