SwiftでSQLiteデータベースにアクセスする


103

アプリ内のSQLiteデータベースにSwiftコードでアクセスする方法を探しています。

SQLiteラッパーをObjective Cで使用してブリッジヘッダーを使用できることはわかっていますが、このプロジェクトはSwiftで完全に実行できます。これを行う方法はありますか?ある場合、誰かがクエリを送信したり、行を取得したりする方法を示す参照を誰かに教えてもらえますか?



1
データベースファイルはどこに配置すればよいですか?
C.フェリアナ

1
@ C.Feliana-アプリケーションサポートディレクトリは素晴らしい場所let dbPath = try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("test.sqlite").pathです。
Rob

回答:


143

おそらく多くのSQLiteラッパーの1つを使用する必要がありますが、SQLiteライブラリを自分で呼び出す方法を知りたい場合は、次のようにします。

  1. SQLite C呼び出しを処理するようにSwiftプロジェクトを構成します。Xcode 9以降を使用している場合は、次のようにするだけです。

    import SQLite3
  2. データベースを作成/オープンします。

    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
    }

    注:データベースを開くことができなかった場合にデータベースを閉じるのは奇妙に思われるかもしれませんが、sqlite3_open ドキュメントでは、メモリリークを防ぐために閉じる必要があることを明示しています。

    開いたときにエラーが発生するかどうかに関係なく、データベース接続ハンドルに関連付けられているリソースsqlite3_close()は、不要になったときにそれを渡すことによって解放する必要があります。

  3. sqlite3_execSQLの実行に使用します(例:テーブルの作成)。

    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. 値をバインドするプレースホルダーsqlite3_prepare_v2付きのSQLを準備するために使用し?ます。

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

    これは、次のように実装できるSQLITE_TRANSIENT定数使用しています。

    internal let SQLITE_STATIC = unsafeBitCast(0, to: sqlite3_destructor_type.self)
    internal let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
  5. SQLをリセットして別の値を挿入します。この例では、NULL値を挿入します。

    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. 準備済みステートメントをファイナライズして、その準備済みステートメントに関連するメモリを回復します。

    if sqlite3_finalize(statement) != SQLITE_OK {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("error finalizing prepared statement: \(errmsg)")
    }
    
    statement = nil
  7. テーブルから値を選択するための新しいステートメントを準備し、値の取得をループします。

    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. データベースを閉じる:

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

Swift 2以前のバージョンのXcodeについては、この回答の以前のリビジョンを参照してください。


1
パス1で問題が発生した場合は、次の点を考慮してください。Xcodeプロジェクト(BridgingHeader.hなど)にブリッジヘッダーを作成します。このヘッダーファイルには、SwiftにブリッジするためのObjective-C / Cヘッダーをインポートする行のみが含まれる場合があります(例:#include <sqlite3.h>); 「ビルド設定」で「Objective-Cブリッジヘッダー」を検索し(検索バーを使用できます)、「BridgingHeader.h」と入力します(「Objective-Cヘッダーをインポートできませんでした」などのエラーメッセージが表示された場合は、「project- name / BridgingHeader.h "); 「ビルドフェーズ」、「ライブラリとバイナリをリンク」に移動し、XCode 7にlibsqlite3.0.dylibまたはlibsqlite3.0.tbdを追加します
Jorg B Jorge

if(... == SQLITE_OK)をネストして、失敗した場合に以下が実行されないようにする方が良いでしょう。私はこれに非常に慣れていないので、純粋に尋ねています。あなたが教育目的でこれを行ったかどうかだけに興味がありました。
quemeful

@quemeful-もちろん、多くのSQLite呼び出しでそれを行うと、本当に深くネストされたコードになります。これが心配な場合は、おそらくguard代わりにステートメントを使用します。
Rob

@Jorg B Jorge私はすべてを行いましたが、どういうわけかブリッジングヘッダーをインポートする必要もありますか?私はテストクラスで働いています
Async-

こんにちは@ロブ、私はここのSwiftプロジェクトでsqliteラッパーを使用しています。本当にいい、ありがとう。ただし、それを使用してテーブルから選択カウント(*)を実行することはできません。クラッシュし続けます。some_col = xxxであるtablenameからselect count(col_name)を実行した場合、それは機能します。何を指示してるんですか?
gbenroscience

18

できる最善の方法は、ブリッジングヘッダー内にダイナミックライブラリをインポートすることです。

  1. libsqlite3.dylibを「バイナリをライブラリにリンク」ビルドフェーズに追加します。
  2. 「Bridging-Header.h」を作成して追加します #import <sqlite3.h>し、トップにします
  3. 「Swift Compiler-Code Generation」のビルド設定の「Objective-C Bridging Header」設定に「Bridging-Header.h」を設定します

その後、次のようなcメソッドすべてにアクセスできます。 sqlite3_open、迅速なコードからのます。

ただし、FMDBを使用し、それをブリッジングヘッダーを介してインポートすることもできます。これは、SQLiteのよりオブジェクト指向のラッパーであるためです。Cのポインタと構造体を扱うことは、Swiftでは扱いにくくなります。


Xcodeがブリッジヘッダーを見つけるために、ターゲットビルド設定ではなくプロジェクトビルド設定の下にこれを追加する必要がありました。
rob5408 2014

3
また、誰もが、その父は今スウィフトラッパーを作成しましたが...下記参照
quemeful

1
悲しいことに、どれもひどく成熟していないため、これらの新しいラッパーを使用する場合は注意してください。たとえば、執筆の時点では、そのうちの4つをちらっと見て、3つは日付を誤って処理し、4つ目はまったく処理しませんでした。
Rob

@Rob github.com/stephencelis/SQLite.swift#readmeを見たことがありますか?ここNSDateで使用する設定の詳細:github.com/stephencelis/SQLite.swift/blob/master/Documentation/...は
stephencelis

@stephencelisねえ、少なくともタイムゾーンを指定するので、それはそれらのほとんどより優れていますが、それでも問題がありNSDateFormatterます。しかし、私の意図は、これらの特定の実装のこの特定の側面を批評することよりも、FMDBのようなソリューションが長年にわたって洗練していないことから、より広範な問題を示すことを示唆することではありませんでした。成熟していないSwift実装を支持して、実証済みのObjective-Cソリューションを捨てるのは速すぎると思います(TFHppleとNDHppleも別の良い例です)。
Rob

11

私も、以前Objective-Cで行っていたのと同じ方法で、SQLiteを操作する方法を探していました。確かに、Cの互換性のため、私はそのままのC APIを使用しました。

現在、SwiftのSQLite用のラッパーは存在せず、上記のSQLiteDBコードは少し高いレベルになり、特定の使用法を想定しているため、ラッパーを作成し、その過程でSwiftに少し慣れることにしました。あなたはここでそれを見つけることができます: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 */

5

SwiftDataと呼ばれる、完全にSwiftで記述されたエレガントなSQLiteライブラリを作成しました。

その機能のいくつかは次のとおりです。

  • オブジェクトをSQLの文字列に便利にバインドする
  • トランザクションとセーブポイントのサポート
  • インラインエラー処理
  • デフォルトで完全にスレッドセーフ

「変更」(INSERT、UPDATE、DELETEなど)を実行する簡単な方法を提供します。

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
}

と「クエリ」(例: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)")
        }
    }
}

より多くの機能とともに!

こちらでチェックできます


残念ながらあなたのlibはiOS専用です!:-/
BadmintonCat

3

Swift 2およびSwift 3のさらに別のSQLiteラッパー:http : //github.com/groue/GRDB.swift

特徴:

  • ccgus / fmdbのユーザーに馴染みのあるAPI

  • Swift標準ライブラリを活用する低レベルのSQLite API

  • SQLアレルギーの開発者向けのかなりのSwiftクエリインターフェイス

  • SQLite WALモードのサポート、および追加のパフォーマンスのための同時データベースアクセス

  • 結果セットをラップし、朝食にカスタムSQLクエリを使用し、基本的なCRUD操作を提供するRecordクラス

  • Swiftタイプの自由:データに合った適切なSwiftタイプを選択してください。必要に応じてInt64を使用するか、便利なIntを使用してください。NSDateまたはNSDateComponentsを保存して読み取ります。離散データ型のSwift列挙型を宣言します。独自のデータベース変換可能なタイプを定義します。

  • データベースの移行

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


GRDBは、Githubでドキュメント化、サポート、および維持されているフレームワークの1つです。
Klaas 2017

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

Accessデータベース:

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

データベースクエリ発火:

var DailyResult:Array<Model> = DB.AllSelectQuery("select * from food where foodtype == 'Sea Food' ORDER BY name ASC")

このqyeryは機能していません。なぜ==ではなく==があるのですか?
ArgaPK

1

これは、私がSwiftで使用した中で最も優れたSQLiteライブラリです。https//github.com/stephencelis/SQLite.swift

コード例を見てください。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 <- "alice@mac.com")
let rowid = try db.run(insert)
// INSERT INTO "users" ("name", "email") VALUES ('Alice', 'alice@mac.com')

for user in try db.prepare(users) {
    print("id: \(user[id]), name: \(user[name]), email: \(user[email])")
    // id: 1, name: Optional("Alice"), email: alice@mac.com
}
// 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"

ドキュメントはまた、「SQLite.swiftはC APIを介した軽量のSwiftフレンドリーなラッパーとしても機能する」と述べており、その例をいくつか示します。


0

私はSwiftで書かれたSQLite3ラッパーライブラリを書きました

これは実際には非常にシンプルなAPIを備えた非常に高レベルのラッパーですが、とにかく、低レベルのC相互運用コードが含まれています。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
    }

この低レベルラッパーの完全なソースコードが必要な場合は、これらのファイルを参照してください。


0

SQLite C呼び出しを処理するようにSwiftプロジェクトを構成します。

プロジェクトにブリッジヘッダーファイルを作成します。CocoaおよびObjective-CでのSwiftの使用のObjective-CのSwiftへのインポートのセクションを参照してください。このブリッジヘッダーはsqlite3.hをインポートする必要があります。

libsqlite3.0.dylibをプロジェクトに追加します。プロジェクトへのライブラリ/フレームワークの追加に関するAppleのドキュメントを参照してください。

そして使用されたコード

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

0

sqlite.orgに示されている「5分以内のSQLite」アプローチのSwiftバージョンで十分な場合があります。「5分以内」アプローチの使用のための便利なラッパーで、、、および。sqlite3_exec()sqlite3_prepare()sqlite3_step()sqlite3_column()sqlite3_finalize()

Swift 2.2は、sqlite3_exec() callback関数ポインタをグローバルな非インスタンスプロシージャfuncまたは非キャプチャリテラルクロージャとして直接サポートできます{}

読みやすい typealias

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

コールバックアプローチ

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
}

閉鎖アプローチ

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
}

SQLiteなどのCライブラリを呼び出すXcodeプロジェクトを準備するには、(1)Bridging-Header.hファイル参照のようなCヘッダーを#import "sqlite3.h"追加する、(2)プロジェクトのObjective-CブリッジヘッダーにBridging-Header.hを追加する必要があります。設定、および(3)ライブラリとバイナリlibsqlite3.tbdリンクに追加ターゲット設定にます。

sqlite.org『SQLiteの5分以内に』例では、スウィフトXcode7プロジェクトで実装されてここに


0

Swift for SQLiteでこのライブラリを使用できます https://github.com/pmurphyjam/SQLiteDemo

SQLiteDemo

Swiftで記述されたSQLDataAccessクラスでSwiftを使用するSQLiteデモ

プロジェクトに追加する

プロジェクトに追加する必要があるファイルは3つだけです* SQLDataAccess.swift * DataConstants.swift * Bridging-Header.h Bridging-Headerは、Xcodeのプロジェクト「Objective-C Bridging Header」の「Swift Compiler-General」の下に設定する必要があります

使用例

ViewController.swiftのコードに従って、SQLDataAccess.swiftで単純なSQLを作成する方法を確認してください。まず、処理するSQLiteデータベースを開く必要があります。

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

openConnectionが成功した場合は、テーブル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)")
    }

それがいかに簡単かを見てください!

db.executeStatementの最初の用語は文字列としてのSQLであり、それに続くすべての用語はAny型の可変引数リストであり、配列内のパラメーターです。SQL引数のリストでは、これらの用語はすべてコンマで区切られています。文字列、整数、日付、ブロブは、後のステートメントの直後に入力できます。これらの用語はすべて、後のパラメータと見なされるためです。可変個引数配列は、すべての続編を1つのexecuteStatementまたはgetRecordsForQuery呼び出しで入力するのに便利です。パラメータがない場合は、SQLの後に何も入力しないでください。

結果の配列はディクショナリの配列で、「キー」はテーブルの列名、「値」はSQLiteから取得したデータです。forループを使用してこの配列を簡単に反復処理したり、直接出力したり、これらのディクショナリ要素をモデル使用のためにビューコントローラーで使用するカスタムデータオブジェクトクラスに割り当てたりできます。

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

SQLDataAccessは、テキスト、double、float、blob、Date、integer、およびlong long integerを格納します。Blobの場合、binary、varbinary、blobを格納できます。

テキストの場合、文字、文字、CLOB、国別可変文字、ネイティブ文字、nchar、nvarchar、varchar、バリアント、可変文字、テキストを格納できます。

日付の場合は、日時、時刻、タイムスタンプ、日付を保存できます。

整数の場合、bigint、bit、bool、boolean、int2、int8、integer、mediumint、smallint、tinyint、intを格納できます。

Doubleの場合、10進数、倍精度、float、数値、実数、doubleを格納できます。最も精度が高いのはDoubleです。

NullタイプのNullを格納することもできます。

ViewController.swiftでは、より複雑な例が実行され、辞書を「Blob」として挿入する方法が示されます。さらに、SQLDataAccessはネイティブのSwift Date()を理解するので、これらのオブジェクトを変換せずに挿入できます。また、オブジェクトをテキストに変換して保存し、取得すると、テキストから日付に変換します。

もちろん、SQLiteの本当の力は、トランザクション機能です。ここでは、文字列で400のSQLステートメントをパラメーター付きでキューに入れ、それらを一度にすべて挿入できます。これは非常に高速であるため、非常に強力です。ViewController.swiftは、これを行う方法の例も示しています。実際に行っているのは、「sqlAndParams」と呼ばれるディクショナリの配列を作成することだけです。この配列では、文字列の続編ステートメントまたはクエリの2つのキー「SQL」と、ネイティブオブジェクトのSQLiteの配列である「PARAMS」を含むディクショナリを格納します。そのクエリを理解します。後継クエリとパラメータの個別のディクショナリである各「sqlParams」は、「sqlAndParams」配列に格納されます。この配列を作成したら、呼び出すだけです。

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

さらに、すべてのexecuteStatementメソッドとgetRecordsForQueryメソッドは、SQLクエリの単純な文字列とクエリで必要なパラメーターの配列を使用して実行できます。

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

Objective-Cバージョンも存在し、同じSQLDataAccessと呼ばれるため、Objective-CまたはSwiftで続編を作成することを選択できます。さらに、SQLDataAccessもSQLCipherで動作します。現在のコードはまだ動作するように設定されていませんが、実行するのは非常に簡単です。これを実行する方法の例は、実際にはSQLDataAccessのObjective-Cバージョンにあります。

SQLDataAccessは非常に高速で効率的なクラスであり、CoreDataに付随するすべてのCoreDataコアデータ整合性エラークラッシュなしに、基になるデータストアとしてSQLiteを実際に使用するCoreDataの代わりに使用できます。


-1

シングルトンクラスを使用して、SQLiteをすばやく構成することもできます。

参照

https://github.com/hasyapanchasara/SQLite_SingleManagerClass

データベースを作成する方法

func methodToCreateDatabase() -> NSURL?{} 

データを挿入、更新、削除する方法

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

データを選択する方法

func methodToSelectData(strQuery : String) -> NSMutableArray{}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.