Zugriff auf eine SQLite-Datenbank in Swift

103

Ich suche nach einer Möglichkeit, mit Swift-Code auf eine SQLite-Datenbank in meiner App zuzugreifen.

Ich weiß, dass ich in Objective C einen SQLite Wrapper und den Bridging-Header verwenden kann, aber ich würde dieses Projekt lieber vollständig in Swift ausführen können. Gibt es eine Möglichkeit, dies zu tun? Wenn ja, kann mich jemand auf eine Referenz verweisen, die zeigt, wie eine Abfrage gesendet, Zeilen abgerufen usw. wird?

Jase
quelle
1
Wo soll ich meine Datenbankdatei ablegen?
C. Feliana
1
@ C.Feliana - Das Anwendungsunterstützungsverzeichnis ist ein großartiger Ort, z let dbPath = try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("test.sqlite").path.
Rob

Antworten:

143

Während Sie wahrscheinlich einen der vielen SQLite-Wrapper verwenden sollten, würden Sie Folgendes tun, wenn Sie wissen möchten, wie Sie die SQLite-Bibliothek selbst aufrufen können:

  1. Konfigurieren Sie Ihr Swift-Projekt für die Verarbeitung von SQLite C-Aufrufen. Wenn Sie Xcode 9 oder höher verwenden, können Sie einfach Folgendes tun:

    import SQLite3
  2. Datenbank erstellen / öffnen.

    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
    }

    Hinweis: Ich weiß, dass es seltsam erscheint, die Datenbank zu schließen, wenn sie nicht geöffnet werden kann. In der sqlite3_open Dokumentation wird jedoch ausdrücklich darauf hingewiesen, dass dies erforderlich ist, um Speicherverluste zu vermeiden:

    Unabhängig davon, ob beim Öffnen ein Fehler auftritt oder nicht, sollten die mit dem Datenbankverbindungshandle verknüpften Ressourcen freigegeben werden, indem sie an übergeben werden, sqlite3_close()wenn sie nicht mehr benötigt werden.

  3. Verwenden Sie sqlite3_execdiese Option , um SQL auszuführen (z. B. Tabelle erstellen).

    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. Verwenden Sie sqlite3_prepare_v2diese Option , um SQL mit einem ?Platzhalter vorzubereiten, an den der Wert gebunden wird.

    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)")
    }

    Beachten Sie, dass die SQLITE_TRANSIENTKonstante verwendet wird, die wie folgt implementiert werden kann:

    internal let SQLITE_STATIC = unsafeBitCast(0, to: sqlite3_destructor_type.self)
    internal let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
  5. Setzen Sie SQL zurück, um einen anderen Wert einzufügen. In diesem Beispiel füge ich einen NULLWert ein:

    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. Finalisieren Sie die vorbereitete Anweisung, um den mit dieser vorbereiteten Anweisung verknüpften Speicher wiederherzustellen:

    if sqlite3_finalize(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error finalizing prepared statement: \(errmsg)")
    }
    
    statement = nil
  7. Bereiten Sie eine neue Anweisung zum Auswählen von Werten aus Tabelle und Schleife vor, indem Sie die Werte abrufen:

    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. Datenbank schließen:

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

Informationen zu Swift 2 und älteren Versionen von Xcode finden Sie in früheren Versionen dieser Antwort .

rauben
quelle
1
Beachten Sie Folgendes, wenn bei Pass 1 einige Probleme aufgetreten sind: Erstellen Sie einen Bridging-Header in Ihrem Xcode-Projekt (z. B. BridgingHeader.h). Diese Header-Datei enthält möglicherweise nur Zeilen, die Objective-C / C-Header für die Überbrückung zu Swift importieren (z. B. #include <sqlite3.h>). Suchen Sie unter "Build Settings" nach "Objective-C Bridging Header" (Sie können die Suchleiste verwenden) und geben Sie "BridgingHeader.h" ein (wenn Sie eine Fehlermeldung wie "Objective-C Header konnte nicht importiert werden" erhalten, versuchen Sie "project-" name / BridgingHeader.h "); Gehen Sie zu "Phasen erstellen", "Binär mit Bibliotheken verknüpfen" und fügen Sie libsqlite3.0.dylib oder libsqlite3.0.tbd in XCode 7
Jorg B Jorge
Wäre es besser, das if (... == SQLITE_OK) zu verschachteln, damit das Folgende nicht ausgeführt wird, wenn es fehlschlägt. Ich frage nur, weil ich sehr neu in diesem Bereich bin und nur neugierig war, ob Sie dies zu Unterrichtszwecken getan haben.
quemeful
@quemeful - Sicher, aber wenn Sie dies mit vielen SQLite-Aufrufen tun, erhalten Sie Code, der wirklich tief verschachtelt ist. Wenn Sie darüber besorgt sind, würde ich wahrscheinlich guardstattdessen Anweisungen verwenden.
Rob
@Jorg B Jorge Ich habe alles gemacht, musst du auch irgendwie Bridging Header importieren? Ich arbeite in der Testklasse
Testklasse
Hallo @Rob, ich verwende hier Ihren SQLite-Wrapper in einem Swift-Projekt. Es ist wirklich schön, danke. Ich kann jedoch keine Auswahlzählung (*) aus der Tabelle damit durchführen. Es stürzt immer wieder ab. Wenn ich eine Auswahlzählung (col_name) aus tabellenname mit some_col = xxx durchgeführt habe, funktioniert dies. Was schlagen Sie vor?
Gbenroscience
18

Das Beste, was Sie tun können, ist, die dynamische Bibliothek in einen Bridging-Header zu importieren:

  1. Fügen Sie libsqlite3.dylib zu Ihrer Build-Phase "Link Binary With Libraries" hinzu
  2. Erstellen Sie eine "Bridging-Header.h" und fügen Sie #import <sqlite3.h>sie oben hinzu
  3. Stellen Sie "Bridging-Header.h" für die Einstellung "Objective-C Bridging Header" in den Build-Einstellungen unter "Swift Compiler - Code Generation" ein.

Sie können dann wie sqlite3_openin Ihrem schnellen Code auf alle c-Methoden zugreifen .

Möglicherweise möchten Sie jedoch nur verwenden FMDB verwenden und diese über den Bridging-Header importieren, da dies ein objektorientierterer Wrapper von SQLite ist. Der Umgang mit C-Zeigern und -Strukturen ist in Swift umständlich.

Drawag
quelle
Ich musste dies unter den Einstellungen für die Projekterstellung und nicht unter den Einstellungen für die Zielerstellung hinzufügen, damit Xcode den Überbrückungsheader finden konnte.
rob5408
3
Auch alle und ihr Vater haben jetzt einen Swift-Wrapper erstellt. Siehe unten
quemeful
1
Leider ist keiner von ihnen furchtbar ausgereift. Wenn Sie also einen dieser neuen Wrapper verwenden, seien Sie vorsichtig. Zum Zeitpunkt des Schreibens habe ich zum Beispiel vier davon angeschaut, und drei haben Daten falsch behandelt, und die vierte hat sie überhaupt nicht behandelt.
Rob
@Rob Haben Sie sich github.com/stephencelis/SQLite.swift#readme angesehen ? Informationen zur Konfiguration für die Verwendung mit NSDate finden Sie hier: github.com/stephencelis/SQLite.swift/blob/master/Documentation/…
stephencelis
@stephencelis Hey, das ist besser als die meisten von ihnen, weil du zumindest die Zeitzone spezifizierst, aber ich habe immer noch Probleme damit NSDateFormatter. Meine Absicht war es jedoch weniger, diesen speziellen Aspekt dieser speziellen Implementierungen zu kritisieren, als darauf hinzuweisen, dass dies auf ein breiteres Problem hinweist und dass diese nicht die jahrelange Verfeinerung aufweisen, die Lösungen wie FMDB haben. Ich denke, die Leute sind zu schnell, um bewährte Objective-C-Lösungen zugunsten weniger ausgereifter Swift-Implementierungen zu verwerfen (TFHpple vs NDHpple sind ein weiteres gutes Beispiel).
Rob
11

Auch ich suchte nach einer Möglichkeit, mit SQLite so zu interagieren, wie ich es zuvor in Objective-C gewohnt war. Zugegeben, aus Gründen der C-Kompatibilität habe ich nur die reine C-API verwendet.

Da derzeit kein Wrapper für SQLite in Swift vorhanden ist und der oben erwähnte SQLiteDB-Code eine etwas höhere Ebene hat und eine bestimmte Verwendung voraussetzt, habe ich beschlossen, einen Wrapper zu erstellen und mich dabei ein wenig mit Swift vertraut zu machen. Sie finden es hier: 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
quelle
5

Ich habe eine elegante SQLite-Bibliothek namens SwiftData erstellt, die vollständig in Swift geschrieben wurde .

Einige seiner Funktionen sind:

  • Binden Sie Objekte bequem an die SQL-Zeichenfolge
  • Unterstützung für Transaktionen und Sicherungspunkte
  • Inline-Fehlerbehandlung
  • Standardmäßig vollständig threadsicher

Es bietet eine einfache Möglichkeit, 'Änderungen' auszuführen (z. B. EINFÜGEN, AKTUALISIEREN, LÖSCHEN usw.):

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
}

und 'Abfragen' (zB 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)")
        }
    }
}

Zusammen mit vielen weiteren Funktionen!

Sie können es hier überprüfen

Ryanfowler
quelle
Leider ist deine Bibliothek nur für iOS! : - /
BadmintonCat
3

Noch ein SQLite-Wrapper für Swift 2 und Swift 3: http://github.com/groue/GRDB.swift

Eigenschaften:

  • Eine API, die Benutzern von ccgus / fmdb bekannt vorkommt

  • Eine SQLite-API auf niedriger Ebene, die die Swift-Standardbibliothek nutzt

  • Eine hübsche Swift-Abfrageoberfläche für SQL-allergische Entwickler

  • Unterstützung für den SQLite WAL-Modus und gleichzeitiger Datenbankzugriff für zusätzliche Leistung

  • Eine Record-Klasse, die Ergebnismengen umschließt, Ihre benutzerdefinierten SQL-Abfragen zum Frühstück verarbeitet und grundlegende CRUD-Operationen bereitstellt

  • Schnelle Schriftfreiheit: Wählen Sie den richtigen schnellen Typ, der zu Ihren Daten passt. Verwenden Sie bei Bedarf Int64 oder bleiben Sie beim praktischen Int. Speichern und lesen Sie NSDate oder NSDateComponents. Deklarieren Sie Swift-Aufzählungen für diskrete Datentypen. Definieren Sie Ihre eigenen datenbankkonvertierbaren Typen.

  • Datenbankmigrationen

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

Gwendal Roué
quelle
GRDB ist eines der am besten dokumentierten, unterstützten und gepflegten Frameworks auf 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
}

}

Model.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 = ""
}

Zugriff auf Datenbank:

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

Datenbank Abfrage Feuer:

var DailyResult:Array<Model> = DB.AllSelectQuery("select * from food where foodtype == 'Sea Food' ORDER BY name ASC")
Jayesh Miruliya
quelle
Dieses Qyery funktioniert nicht. warum gibt es == statt nur eines =?
ArgaPK
1

Dies ist bei weitem die beste SQLite-Bibliothek, die ich in Swift verwendet habe: https://github.com/stephencelis/SQLite.swift

Schauen Sie sich die Codebeispiele an. So viel sauberer als die C-API:

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"

In der Dokumentation heißt es außerdem, dass "SQLite.swift auch als leichter, Swift-freundlicher Wrapper über die C-API funktioniert", und es folgen einige Beispiele dafür.

Andrew Koster
quelle
0

Ich habe eine in Swift geschriebene SQLite3-Wrapper-Bibliothek geschrieben .

Dies ist eigentlich ein Wrapper auf sehr hoher Ebene mit einer sehr einfachen API, aber trotzdem hat er C-Interop-Code auf niedriger Ebene, und ich poste hier einen (vereinfachten) Teil davon, um die C-Interop zu zeigen.

    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
    }

Wenn Sie einen vollständigen Quellcode für diesen Low-Level-Wrapper benötigen, lesen Sie diese Dateien.

eonil
quelle
0

Konfigurieren Sie Ihr Swift-Projekt für die Verarbeitung von SQLite C-Aufrufen:

Erstellen Sie eine Bridging-Header-Datei für das Projekt. Weitere Informationen finden Sie im Abschnitt Importieren von Objective-C in Swift unter Verwenden von Swift mit Kakao und Objective-C. Dieser Bridging-Header sollte sqlite3.h importieren:

Fügen Sie Ihrem Projekt die libsqlite3.0.dylib hinzu. Informationen zum Hinzufügen einer Bibliothek / eines Frameworks zum eigenen Projekt finden Sie in der Dokumentation von Apple.

und folgenden Code verwendet

    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
quelle
0

Manchmal ist eine Swift-Version des auf sqlite.org gezeigten Ansatzes "SQLite in 5 Minuten oder weniger" ausreichend. Die „5 Minuten oder weniger“ Ansatz verwendet , die ein Convenience - Wrapper ist für , , , undsqlite3_exec()sqlite3_prepare()sqlite3_step()sqlite3_column()sqlite3_finalize() .

Swift 2.2 kann den sqlite3_exec() callbackFunktionszeiger direkt als globale Prozedur ohne Instanz funcoder als nicht erfassenden Literalabschluss unterstützen {}.

Lesbar typealias

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

Rückrufansatz

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
}

Abschlussansatz

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
}

Um ein Xcode-Projekt für den Aufruf einer C-Bibliothek wie SQLite vorzubereiten, müssen Sie (1) eine Bridging-Header.h-Dateireferenz C-Header #import "sqlite3.h"hinzufügen, wie (2) Bridging-Header.h zu Objective-C Bridging Header im Projekt hinzufügen Einstellungen und (3) libsqlite3.tbdzu Link Binary With Library hinzufügen verknüpfen" .

Das sqlite.org ‚s ‚SQLite in 5 Minuten oder weniger‘ Beispiel ist in einem Swift Xcode7 Projekt umgesetzt hier .

l - marc l
quelle
0

Sie können SQLite auch schnell und einfach mit einer einzelnen Tonne konfigurieren.

Verweisen

https://github.com/hasyapanchasara/SQLite_SingleManagerClass

Methode zum Erstellen einer Datenbank

func methodToCreateDatabase() -> NSURL?{} 

Methode zum Einfügen, Aktualisieren und Löschen von Daten

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

Methode zur Auswahl von Daten

func methodToSelectData(strQuery : String) -> NSMutableArray{}
Hasya
quelle
0

Sie können diese Bibliothek in Swift für SQLite https://github.com/pmurphyjam/SQLiteDemo verwenden

SQLiteDemo

SQLite-Demo mit Swift mit in Swift geschriebener SQLDataAccess-Klasse

Hinzufügen zu Ihrem Projekt

Sie müssen Ihrem Projekt nur drei Dateien hinzufügen. * SQLDataAccess.swift * DataConstants.swift * Bridging-Header.h Der Bridging-Header muss im Xcode-Projekt 'Objective-C Bridging Header' unter 'Swift Compiler - General' festgelegt werden.

Anwendungsbeispiele

Folgen Sie einfach dem Code in ViewController.swift, um zu sehen, wie Sie einfaches SQL mit SQLDataAccess.swift schreiben. Zuerst müssen Sie die SQLite-Datenbank öffnen, mit der Sie sich befassen

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

Wenn openConnection erfolgreich war, können Sie jetzt eine einfache Einfügung in Table AppInfo vornehmen

    //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)")
    }

Sehen Sie, wie einfach das war!

Der erste Begriff in db.executeStatement ist Ihre SQL als Zeichenfolge. Alle folgenden Begriffe sind eine Liste mit variablen Argumenten vom Typ Any und Ihre Parameter in einem Array. Alle diese Begriffe werden in Ihrer Liste der SQL-Argumente durch Kommas getrennt. Sie können direkt nach der Sequel-Anweisung Strings, Integers, Date's und Blobs eingeben, da alle diese Begriffe als Parameter für die Fortsetzung gelten. Das Array mit variablen Argumenten macht es einfach bequem, Ihre gesamte Fortsetzung in nur einem Aufruf von executeStatement oder getRecordsForQuery einzugeben. Wenn Sie keine Parameter haben, geben Sie nach Ihrem SQL nichts ein.

Das Ergebnisarray ist ein Array von Wörterbüchern, bei dem der 'Schlüssel' der Name Ihrer Tabellenspalte und der 'Wert' Ihre von SQLite erhaltenen Daten sind. Sie können dieses Array einfach mit einer for-Schleife durchlaufen oder direkt ausdrucken oder diese Dictionary-Elemente benutzerdefinierten Datenobjektklassen zuweisen, die Sie in Ihren View Controllern für den Modellverbrauch verwenden.

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

SQLDataAccess speichert Text, Double, Float, Blob, Datum, Ganzzahl und lange lange Ganzzahlen. Für Blobs können Sie Binär, Varbinär, Blob speichern.

Für Text können Sie Zeichen, Zeichen, Clob, national variierendes Zeichen, natives Zeichen, nchar, nvarchar, varchar, Variante, variierendes Zeichen, Text speichern.

Für Daten können Sie Datum, Uhrzeit, Zeitstempel und Datum speichern.

Für Ganzzahlen können Sie bigint, bit, bool, boolean, int2, int8, Ganzzahl, mediumint, smallint, tinyint, int speichern.

Für Doubles können Sie Dezimalzahlen, doppelte Genauigkeit, Gleitkommazahlen, numerische Werte, reelle Zahlen und doppelte Werte speichern. Double hat die höchste Präzision.

Sie können sogar Nullen vom Typ Null speichern.

In ViewController.swift wird ein komplexeres Beispiel gezeigt, wie ein Wörterbuch als 'Blob' eingefügt wird. Darüber hinaus versteht SQLDataAccess das native Swift Date (), sodass Sie diese Objekte ohne Konvertierung einfügen können. Es konvertiert sie in Text und speichert sie. Wenn sie abgerufen werden, konvertieren Sie sie zurück von Text in Datum.

Die wahre Stärke von SQLite liegt natürlich in der Transaktionsfähigkeit. Hier können Sie buchstäblich 400 SQL-Anweisungen mit Parametern in die Warteschlange stellen und alle auf einmal einfügen, was sehr leistungsfähig ist, da es so schnell ist. ViewController.swift zeigt Ihnen auch ein Beispiel dafür. Alles, was Sie wirklich tun, ist, ein Array von Wörterbüchern mit dem Namen 'sqlAndParams' zu erstellen. In diesem Array speichern Sie Wörterbücher mit zwei Schlüsseln 'SQL' für die String-Folge-Anweisung oder -Abfrage und 'PARAMS', das nur ein Array nativer Objekte SQLite ist versteht für diese Abfrage. Jedes 'sqlParams', das ein individuelles Wörterbuch der Folgeabfrage plus Parameter ist, wird dann im 'sqlAndParams'-Array gespeichert. Sobald Sie dieses Array erstellt haben, rufen Sie einfach an.

    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)")
    }

Darüber hinaus können alle Methoden executeStatement und getRecordsForQuery mit einem einfachen String für SQL-Abfragen und einem Array für die von der Abfrage benötigten Parameter ausgeführt werden.

    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)")
    }

Es gibt auch eine Objective-C-Version, die als SQLDataAccess bezeichnet wird. Jetzt können Sie wählen, ob Sie Ihre Fortsetzung in Objective-C oder Swift schreiben möchten. Darüber hinaus funktioniert SQLDataAccess auch mit SQLCipher. Der vorliegende Code ist noch nicht eingerichtet, um damit zu arbeiten. Dies ist jedoch recht einfach. Ein Beispiel dafür finden Sie in der Objective-C-Version von SQLDataAccess.

SQLDataAccess ist eine sehr schnelle und effiziente Klasse und kann anstelle von CoreData verwendet werden, das wirklich nur SQLite als zugrunde liegenden Datenspeicher verwendet, ohne dass alle mit CoreData verbundenen CoreData-Kerndatenintegritätsfehler abstürzen.

Frost
quelle