I have the following code to bookmark URLs for future access:
private func checkForSQLiteFiles(at containerURL: URL, appName: String) {
do {
viewModel.connect(strConnect: selectedSQLiteFile!.path)
viewModel.fetchAllTables()
// Save selected SQLite file to UserDefaults
UserDefaults.standard.set(selectedSQLiteFile, forKey: "selectedSQLiteFile")
saveBookmark(for: selectedSQLiteFile!, key: "SQLiteBookmark")
}
} catch {
errorMessages.append("Error")
}
}
func saveBookmark(for url: URL, key: String) {
do {
let bookmarkData = try url.bookmarkData(options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil)
UserDefaults.standard.set(bookmarkData, forKey: key)
} catch {
print("Failed to create bookmark for (url.path): (error)")
}
}
The code above works fine. BTW I’m getting access using NSOpenPanel
to files/directories that I selected.
In another place, I have code to restore URLs from bookmarks, try to connect, and request DB again using the same file I used before.
private func openExistingProject() {
if let sqliteURL = resolveBookmark(for: "SQLiteBookmark") {
if sqliteURL.startAccessingSecurityScopedResource() {
// Check if file is readable and exists
if FileManager.default.isReadableFile(atPath: sqliteURL.path) {
print("SQLite file is readable at path: (sqliteURL.path)")
// Proceed to connect and load entities
viewModel.connect(strConnect: sqliteURL.path)
viewModel.fetchAllTables()
} else {
print("Failed to access the SQLite file at (sqliteURL.path)")
}
} else {
print("Failed to access the security-scoped resource for (sqliteURL.path)")
}
defer { sqliteURL.stopAccessingSecurityScopedResource() }
} else {
print("No valid bookmark for SQLite file")
}
}
func resolveBookmark(for key: String) -> URL? {
if let bookmarkData = UserDefaults.standard.data(forKey: key) {
var isStale = false
do {
let url = try URL(resolvingBookmarkData: bookmarkData, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale)
if isStale {
print("Bookmark data is stale for (url.path)")
}
return url
} catch {
print("Failed to resolve bookmark for key (key): (error)")
}
}
return nil
}
But when I call fetchAllTables()
in ViewModel:
class SQLiteEntityManager: ObservableObject {
@Published var isConnected: Bool = false
private var db: Connection?
func connect(strConnect: String) {
// Define the path to the SQLite database file
let dbPath = strConnect
// Create a connection to the database
do {
db = try Connection(dbPath)
isConnected = true
} catch {
print("Unable to open database. Error: (error)")
}
}
func fetchAllTables() -> [String] {
guard let db = db else {
print("Database is not connected")
return []
}
do {
let tables = try db.prepare("SELECT name FROM sqlite_master WHERE type='table'")
return tables.map { "($0[0]!)" }
} catch {
print("Error fetching tables: (error)")
return []
}
}
}
it fails with "Error fetching tables: access denied (code:23)"
. But it still connects successfully without any issues to that DB using that bookmarked URL.
Error happens only when I try to do db.prepare(“…..”).
Any advices would be appreciated
3
Try to check race condition issue, I think you should call fetchAllTables() only after establishing the connection
as an quick test you can try to call func fetchAllTables()
just after db = try Connection(dbPath)