在Swift Storyboard中如何创建并打开SQLite数据库及数据表?
问题分析
你遇到的核心问题集中在三个点:
- 路径格式错误:使用
url.absoluteString会得到带file://前缀的字符串,而sqlite3_open需要本地文件的纯路径字符串,这直接导致数据库文件没有被正确创建。 - 冗余的文件创建逻辑:
createFile方法仅生成URL但未实际创建文件,且SQLite的sqlite3_open会自动在指定路径创建数据库文件,手动创建完全没必要。 - 初始化流程混乱:创建表、启用外键的时机未与数据库打开流程绑定,导致这些操作要么未执行,要么重复执行报错。
解决方案
- 修正数据库路径获取方式,用
url.path替代absoluteString,传递正确的本地路径给SQLite - 删除无用的
createFile方法,依赖sqlite3_open自动创建数据库文件 - 将启用外键、创建表的逻辑绑定到数据库打开流程中,确保仅在数据库成功打开后执行
- 表创建语句中加入
IF NOT EXISTS,避免重复创建表的报错
修改后的代码示例
public class SQLiteDatabase { public static var currentAccount = Account(id: -1, email: "", name: "", lastname: "", gender: "", birthday: Date(), creationDate: Date()) public static let pathExtension = "xPose.sqlite" // 修正:用计算属性获取正确的数据库路径 public static var databasePath: String { do { // 确保Documents目录存在,create设为true let url = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) .appendingPathComponent(pathExtension) return url.path // 使用纯路径字符串,去掉file://前缀 } catch { fatalError("无法获取数据库路径:\(error)") } } private let dbPointer: OpaquePointer? private init(dbPointer: OpaquePointer?) { self.dbPointer = dbPointer } deinit { sqlite3_close(dbPointer) } fileprivate var errorMessage: String { if let errorPointer = sqlite3_errmsg(dbPointer) { return String(cString: errorPointer) } else { return "SQLite未提供错误信息" } } func enableForeignKeys() { if sqlite3_exec(dbPointer, "PRAGMA foreign_keys = ON", nil, nil, nil) != SQLITE_OK { let err = String(cString: sqlite3_errmsg(dbPointer)) print("启用外键失败:\(err)") } } func createTables() throws { try createTable(table: Account.self) try createTable(table: Measurement.self) try createTable(table: Posture.self) try createTable(table: Vital.self) try createTable(table: Mood.self) } // 修正:简化open方法,绑定初始化流程 static func open() throws -> SQLiteDatabase { let path = databasePath var db: OpaquePointer? guard sqlite3_open(path, &db) == SQLITE_OK else { defer { db.map(sqlite3_close) } let errorMsg = db.flatMap { String(cString: sqlite3_errmsg($0)) } ?? "未知错误" throw SQLiteError.OpenDatabase(message: errorMsg) } let database = SQLiteDatabase(dbPointer: db) // 启用外键 database.enableForeignKeys() // 创建表(需确保表语句带IF NOT EXISTS) try database.createTables() return database } func prepareStatement(sql: String) throws -> OpaquePointer? { var statement: OpaquePointer? guard sqlite3_prepare_v2(dbPointer, sql, -1, &statement, nil) == SQLITE_OK else { throw SQLiteError.Prepare(message: errorMessage) } return statement } func createTable(table: SQLTable.Type) throws { let createTableStatement = try prepareStatement(sql: table.createStatement) defer { sqlite3_finalize(createTableStatement) } guard sqlite3_step(createTableStatement) == SQLITE_DONE else { throw SQLiteError.Step(message: errorMessage) } print("\(table) 表创建成功。") } } // 注意:你的SQLTable协议的createStatement要加上IF NOT EXISTS,示例: protocol SQLTable { static var createStatement: String { get } } extension Account: SQLTable { static var createStatement: String { """ CREATE TABLE IF NOT EXISTS Account ( id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT UNIQUE NOT NULL, name TEXT, lastname TEXT, gender TEXT, birthday DATETIME, creationDate DATETIME NOT NULL ) """ } }
使用说明
在需要初始化数据库的地方(比如App启动时、ViewModel初始化时)调用:
do { let database = try SQLiteDatabase.open() // 这里可以开始使用数据库进行增删改查操作 } catch { print("数据库打开失败:\(error)") }
内容的提问来源于stack exchange,提问作者Xa4
相关产品推荐
相关产品推荐

