Dostęp do bazy danych SQLite w Swift

103

Szukam sposobu na dostęp do bazy danych SQLite w mojej aplikacji za pomocą kodu Swift.

Wiem, że mogę użyć SQLite Wrapper w Objective C i użyć nagłówka pomostowego, ale wolałbym móc wykonać ten projekt całkowicie w języku Swift. Czy istnieje sposób, aby to zrobić, a jeśli tak, czy ktoś może wskazać mi odniesienie, które pokazuje, jak przesłać zapytanie, pobrać wiersze itp.?

Jase
źródło
1
gdzie mam umieścić mój plik bazy danych?
C. Feliana
1
@ C.Feliana - Katalog obsługi aplikacji to świetne miejsce np let dbPath = try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("test.sqlite").path.
Rob

Odpowiedzi:

144

Chociaż prawdopodobnie powinieneś użyć jednego z wielu opakowań SQLite, jeśli chcesz wiedzieć, jak samodzielnie wywołać bibliotekę SQLite, powinieneś:

  1. Skonfiguruj projekt Swift do obsługi wywołań SQLite C. Jeśli używasz Xcode 9 lub nowszego, możesz po prostu:

    import SQLite3
  2. Utwórz / otwórz bazę danych.

    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
    }

    Uwaga, wiem, że zamknięcie bazy danych po niepowodzeniu otwarcia wydaje się dziwne, ale sqlite3_open dokumentacja wyraźnie mówi, że musimy to zrobić, aby uniknąć wycieku pamięci:

    Niezależnie od tego, czy podczas otwierania wystąpi błąd, czy też nie, zasoby skojarzone z uchwytem połączenia z bazą danych powinny zostać zwolnione, przekazując je, sqlite3_close()gdy nie są już potrzebne.

  3. Służy sqlite3_execdo wykonywania SQL (np. Tworzenia tabeli).

    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. Służy sqlite3_prepare_v2do przygotowania kodu SQL z ?symbolem zastępczym, do którego przypiszemy wartość.

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

    Zauważ, że używa SQLITE_TRANSIENTstałej, którą można zaimplementować w następujący sposób:

    internal let SQLITE_STATIC = unsafeBitCast(0, to: sqlite3_destructor_type.self)
    internal let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
  5. Zresetuj kod SQL, aby wstawić inną wartość. W tym przykładzie wstawię NULLwartość:

    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. Sfinalizuj przygotowaną instrukcję, aby odzyskać pamięć związaną z tą przygotowaną instrukcją:

    if sqlite3_finalize(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error finalizing prepared statement: \(errmsg)")
    }
    
    statement = nil
  7. Przygotuj nową instrukcję do wybierania wartości z tabeli i zapętlaj pobieranie wartości:

    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. Zamknij bazę danych:

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

W przypadku Swift 2 i starszych wersji Xcode zobacz poprzednie wersje tej odpowiedzi .

Obrabować
źródło
1
Dla tych, którzy mają problemy z przebiegiem 1, rozważ to: Utwórz nagłówek mostkujący w swoim projekcie Xcode (np. BridgingHeader.h); Ten plik nagłówkowy może zawierać tylko wiersze importujące nagłówki Objective-C / C w celu mostkowania do Swift (np. #Include <sqlite3.h>); W „Ustawieniach kompilacji” znajdź „Nagłówek mostkowania celu-C” (możesz użyć paska wyszukiwania) i wpisz „BridgingHeader.h” (jeśli pojawi się komunikat o błędzie, taki jak „Nie można zaimportować nagłówka celu-C”, spróbuj „projekt- nazwa / BridgingHeader.h ”); Przejdź do „Fazy kompilacji”, „Połącz pliki binarne z bibliotekami” i dodaj libsqlite3.0.dylib lub libsqlite3.0.tbd w XCode 7
Jorg B Jorge
Czy nie byłoby lepiej zagnieździć if (... == SQLITE_OK), aby w przypadku niepowodzenia następujące czynności nie zostały wykonane. Po prostu pytam, ponieważ jestem w tym bardzo nowy i byłem po prostu ciekawy, czy zrobiłeś to w celach dydaktycznych.
quemeful
@quemeful - Jasne, ale jeśli zrobisz to z wieloma wywołaniami SQLite, otrzymasz kod, który jest naprawdę głęboko zagnieżdżony. Jeśli martwisz się tym, prawdopodobnie guardużyłbym zamiast tego instrukcji.
Rob
@Jorg B Jorge Zrobiłem wszystko, czy musisz też jakoś zaimportować nagłówek mostkujący? Pracuję w klasie testowej
Async
Cześć @Rob, używam twojego opakowania sqlite w projekcie Swift tutaj. To naprawdę miłe, dzięki. Jednak nie mogę z nim zrobić wyboru zliczania (*) z tabeli. Ciągle się zawiesza. Jeśli wybrałem liczbę (col_name) z nazwy tabeli, gdzie some_col = xxx, to działa. Co sugerujesz?
gbenroscience
18

Najlepsze, co możesz zrobić, to zaimportować bibliotekę dynamiczną do nagłówka mostkującego:

  1. Dodaj libsqlite3.dylib do fazy budowania "Połącz plik binarny z bibliotekami"
  2. Utwórz „Bridging-Header.h” i dodaj #import <sqlite3.h>na górze
  3. ustaw „Bridging-Header.h” dla ustawienia „Objective-C Bridging Header” w ustawieniach kompilacji w sekcji „Swift Compiler - Code Generation”

Będziesz wtedy mógł uzyskać dostęp do wszystkich metod c, takich jak sqlite3_openz kodu Swift.

Jednak możesz po prostu chcieć użyć FMDB i zaimportować to przez nagłówek mostkujący, ponieważ jest to bardziej zorientowane obiektowo opakowanie sqlite. Obsługa wskaźników i struktur języka C będzie uciążliwa w języku Swift.

drewag
źródło
Musiałem dodać to w ustawieniach kompilacji projektu, a nie w ustawieniach kompilacji docelowej, aby Xcode mógł znaleźć nagłówek mostkujący.
rob5408
3
także wszyscy i ich ojciec stworzyli teraz opakowanie Swift ... patrz poniżej
quemeful
1
Niestety żaden z nich nie jest strasznie dojrzały, więc jeśli używasz któregokolwiek z tych nowych opakowań, bądź ostrożny. Na przykład w chwili pisania tego tekstu spojrzałem na cztery z nich, trzy z nich nieprawidłowo obsłużyły, a czwarty w ogóle ich nie obsługiwał.
Rob
@Rob Czy spojrzałeś na github.com/stephencelis/SQLite.swift#readme ? Informacje na temat konfiguracji do używania z NSDate
stephencelis
@stephencelis Hej, to jest lepsze niż większość z nich, ponieważ przynajmniej określasz strefę czasową, ale nadal mam z tym problemyNSDateFormatter . Ale moim zamiarem było nie tyle krytykowanie tego konkretnego aspektu tych konkretnych wdrożeń, ile zasugerowanie, że wskazuje to na szerszy problem, że nie mają one lat udoskonalenia, jakie mają rozwiązania takie jak FMDB. Myślę, że ludzie zbyt szybko odrzucają sprawdzone rozwiązania Objective-C na rzecz mniej dojrzałych implementacji Swift (kolejnym dobrym przykładem są TFHpple vs NDHpple).
Rob
11

Ja również szukałem sposobu na interakcję z SQLite w taki sam sposób, jak robiłem to wcześniej w Objective-C. Trzeba przyznać, że ze względu na kompatybilność z C po prostu użyłem prostego API C.

Ponieważ obecnie nie istnieje opakowanie dla SQLite w Swift, a wspomniany powyżej kod SQLiteDB idzie nieco wyżej i zakłada określone użycie, zdecydowałem się stworzyć opakowanie i przy okazji trochę zaznajomić się ze Swift. Możesz go znaleźć tutaj: https://github.com/chrismimpson/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
źródło
5

Stworzyłem elegancką bibliotekę SQLite napisaną w całości w języku Swift o nazwie SwiftData .

Niektóre z jego funkcji to:

  • Dogodnie wiąż obiekty z łańcuchem SQL
  • Wsparcie dla transakcji i zapisów
  • Wbudowana obsługa błędów
  • Całkowicie bezpieczne wątki domyślnie

Zapewnia łatwy sposób wykonywania „zmian” (np. WSTAW, AKTUALIZUJ, USUŃ itp.):

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
}

i „zapytania” (np. 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)")
        }
    }
}

Wraz z wieloma innymi funkcjami!

Możesz to sprawdzić tutaj

ryanfowler
źródło
Niestety Twoja biblioteka jest dostępna tylko na iOS! : - /
BadmintonCat
3

Kolejna otoka SQLite dla Swift 2 i Swift 3: http://github.com/groue/GRDB.swift

Cechy:

  • Interfejs API, który będzie wyglądał znajomo dla użytkowników ccgus / fmdb

  • Niskopoziomowy interfejs API SQLite, który wykorzystuje standardową bibliotekę Swift

  • Ładny interfejs zapytań Swift dla programistów uczulonych na język SQL

  • Obsługa trybu SQLite WAL i jednoczesny dostęp do bazy danych w celu zwiększenia wydajności

  • Klasa Record, która zawija zestawy wyników, zjada niestandardowe zapytania SQL na śniadanie i udostępnia podstawowe operacje CRUD

  • Swift swobodę typów: wybierz odpowiedni typ Swift, który pasuje do Twoich danych. W razie potrzeby użyj Int64 lub trzymaj się wygodnego Int. Przechowuj i czytaj NSDate lub NSDateComponents. Zadeklaruj wyliczenia Swift dla dyskretnych typów danych. Zdefiniuj własne typy konwersji baz danych.

  • Migracje baz danych

  • Prędkość: https://github.com/groue/GRDB.swift/wiki/Performance

Gwendal Roué
źródło
GRDB to jeden z najlepiej udokumentowanych, obsługiwanych i utrzymywanych frameworków na 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 = ""
}

Baza danych dostępu:

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

bazy danych Query fire:

var DailyResult:Array<Model> = DB.AllSelectQuery("select * from food where foodtype == 'Sea Food' ORDER BY name ASC")
Jayesh Miruliya
źródło
ta qyery nie działa. dlaczego jest == zamiast tylko jednego =?
ArgaPK,
1

To zdecydowanie najlepsza biblioteka SQLite, z której korzystałem w Swift: https://github.com/stephencelis/SQLite.swift

Spójrz na przykłady kodu. O wiele czystszy niż 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"

Dokumentacja mówi również, że „SQLite.swift działa również jako lekka, przyjazna dla języka Swift nakładka na C API” i podaje kilka przykładów tego.

Andrew Koster
źródło
0

Napisałem bibliotekę opakowującą SQLite3 napisaną w języku Swift .

W rzeczywistości jest to opakowanie bardzo wysokiego poziomu z bardzo prostym interfejsem API, ale w każdym razie ma kod międzyoperacyjny C niskiego poziomu i zamieszczam tutaj (uproszczoną) jego część, aby pokazać inter-operację 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
    }

Jeśli potrzebujesz pełnego kodu źródłowego tego opakowania niskiego poziomu, zobacz te pliki.

eonil
źródło
0

Skonfiguruj projekt Swift do obsługi wywołań SQLite C:

Utwórz plik nagłówka mostkowania do projektu. Zobacz sekcję Importowanie Objective-C do Swift w podręczniku Używanie języka Swift z kakao i Objective-C. Ten nagłówek mostkujący powinien importować sqlite3.h:

Dodaj libsqlite3.0.dylib do swojego projektu. Zobacz dokumentację Apple dotyczącą dodawania biblioteki / frameworka do swojego projektu.

i użył następującego kodu

    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
źródło
0

Czasami wystarczy podejście „SQLite w 5 minut lub mniej” pokazane na sqlite.org w wersji Swift . W „5 min lub mniej,” zastosowanie podejścia sqlite3_exec(), które to owinięcie wygoda sqlite3_prepare(), sqlite3_step(), sqlite3_column()i sqlite3_finalize().

Swift 2.2 może bezpośrednio obsługiwać sqlite3_exec() callbackwskaźnik funkcji jako globalną procedurę niebędącą instancją funclub zamknięcie literału nieprzechwytywanego {}.

Czytelny typealias

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

Podejście oddzwonienia

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
}

Podejście końcowe

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
}

Aby przygotować projekt Xcode do wywołania biblioteki C, takiej jak SQLite, należy (1) dodać nagłówki C odwołania do pliku Bridging-Header.h, takie jak #import "sqlite3.h"(2) dodać Bridging-Header.h do nagłówka Bridging-C Bridging w projekcie settings i (3) dodaj libsqlite3.tbddo ustawień docelowych Link Binary With Library .

Sqlite.org „s «SQLite w 5 minut lub mniej» przykład jest realizowany w ramach projektu Swift Xcode7 tutaj .

l - marc l
źródło
0

Możesz użyć tej biblioteki w Swift dla SQLite https://github.com/pmurphyjam/SQLiteDemo

SQLiteDemo

SQLite Demo przy użyciu Swift z klasą SQLDataAccess napisaną w języku Swift

Dodawanie do projektu

Potrzebujesz tylko trzech plików, aby dodać do swojego projektu * SQLDataAccess.swift * DataConstants.swift * Bridging-Header.h Bridging-Header musi być ustawiony w projekcie Xcode „Objective-C Bridging Header” w „Swift Compiler - General”

Przykłady użycia

Po prostu postępuj zgodnie z kodem w ViewController.swift, aby zobaczyć, jak napisać prosty SQL za pomocą SQLDataAccess.swift Najpierw musisz otworzyć bazę danych SQLite, z którą masz do czynienia

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

Jeśli openConnection się powiodło, możesz teraz zrobić proste wstawienie do 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)")
    }

Zobacz, jakie to proste!

Pierwszym terminem w db.executeStatement jest twój SQL jako String, wszystkie następujące terminy są listą argumentów o zmiennej liczbie argumentów typu Any i są parametrami w tablicy. Wszystkie te terminy są oddzielone przecinkami na liście argumentów SQL. Możesz wprowadzić ciągi, liczby całkowite, daty i obiekty blob bezpośrednio po instrukcji sequel, ponieważ wszystkie te terminy są uważane za parametry dla sequela. Zmienna tablica argumentów sprawia, że ​​wygodnie jest wprowadzić cały sequel za pomocą jednego wywołania executeStatement lub getRecordsForQuery. Jeśli nie masz żadnych parametrów, nie wprowadzaj niczego po swoim SQL.

Tablica wyników jest tablicą słowników, gdzie „klucz” to nazwa kolumny tabeli, a „wartość” to dane uzyskane z SQLite. Możesz łatwo iterować tę tablicę za pomocą pętli for lub wydrukować ją bezpośrednio lub przypisać te elementy Dictionary do niestandardowych klas obiektów danych, których używasz w kontrolerach widoku do wykorzystania w modelu.

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

SQLDataAccess będzie przechowywać, text, double, float, blob, Date, integer i long long integer. W przypadku obiektów blob można przechowywać pliki binarne, varbinary, blob.

W przypadku tekstu możesz przechowywać char, character, clob, narodowy znak zmienny, znak natywny, nchar, nvarchar, varchar, wariant, zmienny znak, tekst.

W przypadku dat można przechowywać datę i godzinę, godzinę, znacznik czasu, datę.

W przypadku liczb całkowitych można przechowywać bigint, bit, bool, boolean, int2, int8, integer, mediumint, smallint, tinyint, int.

W przypadku podwójnych można przechowywać dziesiętne, podwójnej precyzji, zmiennoprzecinkowe, numeryczne, rzeczywiste, podwójne. Double ma największą precyzję.

Możesz nawet przechowywać wartości Null typu Null.

W ViewController.swift przedstawiono bardziej złożony przykład pokazujący, jak wstawić słownik jako „obiekt BLOB”. Ponadto SQLDataAccess rozumie natywną datę Swift (), dzięki czemu można wstawiać te obiekty bez konwersji, konwertuje je na tekst i zapisuje, a po odzyskaniu konwertuje je z powrotem z tekstu na datę.

Oczywiście prawdziwą mocą SQLite są możliwości Transakcji. Tutaj możesz dosłownie ustawić w kolejce do 400 instrukcji SQL z parametrami i wstawić je wszystkie naraz, co jest naprawdę potężne, ponieważ jest tak szybkie. ViewController.swift pokazuje również przykład, jak to zrobić. Wszystko, co naprawdę robisz, to tworzenie tablicy słowników o nazwie `` sqlAndParams '', w tej tablicy Twoje słowniki przechowujące z dwoma kluczami `` SQL '' dla instrukcji lub zapytania sequel String oraz `` PARAMS '', które jest po prostu tablicą obiektów natywnych SQLite rozumie dla tego zapytania. Każdy „sqlParams”, który jest indywidualnym Słownikiem dalszych zapytań wraz z parametrami, jest następnie przechowywany w tablicy „sqlAndParams”. Po utworzeniu tej tablicy po prostu wywołujesz.

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

Ponadto wszystkie metody executeStatement i getRecordsForQuery można wykonać za pomocą prostego ciągu znaków dla zapytania SQL i tablicy dla parametrów wymaganych przez zapytanie.

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

Istnieje również wersja Objective-C i nazywa się ją tym samym SQLDataAccess, więc teraz możesz napisać swoją kontynuację w Objective-C lub Swift. Ponadto SQLDataAccess będzie również działał z SQLCipher, obecny kod nie jest jeszcze skonfigurowany do pracy z nim, ale jest to dość łatwe, a przykład, jak to zrobić, znajduje się w wersji SQLDataAccess Objective-C.

SQLDataAccess jest bardzo szybką i wydajną klasą i może być używana zamiast CoreData, która tak naprawdę używa SQLite, ponieważ jest to podstawowy magazyn danych bez wszystkich błędów integralności danych CoreData, które pochodzą z CoreData.

Mróz
źródło
-1

Możesz z łatwością skonfigurować SQLite przy użyciu klasy pojedynczej tony.

Odnosić się

https://github.com/hasyapanchasara/SQLite_SingleManagerClass

Sposób tworzenia bazy danych

func methodToCreateDatabase() -> NSURL?{} 

Metoda wstawiania, aktualizowania i usuwania danych

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

Metoda wyboru danych

func methodToSelectData(strQuery : String) -> NSMutableArray{}
Hasya
źródło