Swift4からSQLite3のデータベースでWALモードのものに実はアクセスは普通にできますが、サンドボックスの時に問題が起きます。
サンドボックス時に一般的なフォルダにsqliteのファイルがあるとWALモードの場合は一度目のアクセスでデータベースは開けるのですが、SQL文のチェックの為に二度目のアクセスをするとエラーになります。
これを回避する為に私が取ったのはsqliteファイルをキャッシュフォルダにコピーをして、そのキャッシュフォルダにコピーしたsqliteに対してSQL文を実行し、アプリを終了する時にもともとあったsqliteファイルを削除して、先ほどのコピーしたsqliteファイルを元々の場所にコピーをして戻して、キャッシュフォルダのsqliteファイルを削除します。
データベースを読み込む時の処理例
let op = NSOpenPanel()
op.canChooseFiles = true
op.canChooseDirectories = false
op.allowsMultipleSelection = false
op.allowedFileTypes = ["sqlite"]
op.directoryURL = myURL
if op.runModal() != NSApplication.ModalResponse.OK {
return
}
//ファイルのコピーの処理
let manager = FileManager.default
//キャッシュフォルダのパスの取得
let localPath = manager.urls(for: .cachesDirectory, in: .userDomainMask).first!
//キャッシュにコピーする時のファイル名は固定にしています。
let cacheFolder = "sample.sqlite"
let localBackupDirectory = localPath.appendingPathComponent(cacheFolder)
do {
//コピー処理
try manager.copyItem(at: op.url!, to: localBackupDirectory)
} catch {
print("Copy files Error: \(error)")
return
}
//以下はSQL文の実行例ですが、sqlite3のAPIで直に操作しています。
var db: OpaquePointer? = nil
let sqlRet0 = sqlite3_open_v2(localBackupDirectory.absoluteString, &db, SQLITE_OPEN_URI | SQLITE_OPEN_SHAREDCACHE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_READWRITE, nil)
if sqlRet0 == SQLITE_OK {
print("Database is opened.")
} else {
print("Database couldn't be opened.")
return
}
let queryStatementString = "SELECT id, name FROM groups"
var queryStatement: OpaquePointer? = nil
let sqlRet1 = sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil)
print(sqlRet1)
if sqlRet1 == SQLITE_OK {
print("SQL statement is OK.")
while (sqlite3_step(queryStatement) == SQLITE_ROW) {
let id = sqlite3_column_int(queryStatement, 0)
let titleCol1 = sqlite3_column_text(queryStatement, 1)
var title: String? = nil
if titleCol1 == nil {
title = ""
} else {
title = String(cString: titleCol1!)
}
print("id: \(id), name: \(title!)")
}
}
//sqlite3の後処理
sqlite3_finalize(queryStatement)
sqlite3_close(db)
//元の位置に戻すための処理
do {
//元々のファイルを削除。上書きコピーができないからです。
try manager.removeItem(at: op.url!)
//キャッシュフォルダのファイルを元々の場所にコピー
try manager.copyItem(at: localBackupDirectory!, to: op.url!)
//キャッシュフォルダのファイルを削除。これが残っていると次にコピーできない。
try manager.removeItem(at: localBackupDirectory!)
} catch {
print("\(error)")
let panel = NSAlert()
panel.messageText = "ファイルのコピーに失敗しました。"
panel.runModal()
return
}
