使用SQLite将数据库更改保存到.db文件(Swift)

时间:2019-06-02 16:24:38

标签: ios swift sqlite

在我的项目中,我正在使用.dbsqlite3_open打开一个已经存在的数据库。该文件是使用命令行工具sqlite3创建的。我正在更新表中的某些行,效果很好,但是这些更新仅在数据库的临时实例中进行。我还如何更新.db文件以供项目的将来运行以使用更新的数据?

有人问here如何在Swift中保存SQLite数据库。但是答案对我来说似乎有点令人不满意,因为该文件应以与sqlite3创建的格式写入,以便可以通过sqlite3_open重新打开。

SQLite甚至可能提供一个功能吗?我找不到任何东西...

1 个答案:

答案 0 :(得分:1)

您不能修改捆绑软件的内容。因此,您需要将文件复制到可以修改的目录中。从历史上看,我们可能建议使用“文档”文件夹。但是如今,我们使用“应用程序支持目录”。参见iOS Storage Best Practices

无论如何,基本思想是:

  • 在捆绑包中包含原始数据库(如果尚未添加,则将其添加到应用的目标中);

    enter image description here

  • 尝试打开数据库(使用SQLITE_READWRITE选项,但不使用SQLITE_CREATE选项...如果数据库没有找到,我们不希望它创建空白数据库)应用程序支持目录;和

  • 如果失败(因为找不到文件),请将数据库从分发包复制到应用程序支持目录,然后重试

因此,也许像这样:

var db: OpaquePointer?

enum DatabaseError: Error {
    case bundleDatabaseNotFound
    case sqliteError(Int32, String?)
}

func openDatabase() throws {
    let fileURL = try FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
        .appendingPathComponent("database.db")

    // try opening database and just return if it succeeded

    if sqlite3_open_v2(fileURL.path, &db, SQLITE_OPEN_READWRITE, nil) == SQLITE_OK {
        return
    }

    // if it failed, clean up before trying again ...

    sqlite3_close(db)
    db = nil

    // then copy database from bundle ...

    guard let bundlePath = Bundle.main.url(forResource: "database", withExtension: "db") else {
        throw DatabaseError.bundleDatabaseNotFound
    }
    try FileManager.default.copyItem(at: bundlePath, to: fileURL)

    // and try again

    let rc = sqlite3_open_v2(fileURL.path, &db, SQLITE_OPEN_READWRITE, nil)

    if rc == SQLITE_OK { return }

    // and if it still failed, again, clean up, and then throw error

    let errorMessage = sqlite3_errmsg(db)
        .flatMap { String(cString: $0) }

    sqlite3_close(db)
    db = nil

    throw DatabaseError.sqliteError(rc, errorMessage)
}