从 SwiftUI 中添加书签的 URL 恢复对数据库的访问

问题描述 投票:0回答:1

我有以下代码来为 URL 添加书签以供将来访问:

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

上面的代码运行良好。顺便说一句,我可以使用

NSOpenPanel
访问我选择的文件/目录。

在另一个地方,我有代码从书签恢复 URL,尝试连接,并使用我之前使用的相同文件再次请求数据库。

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
}

但是当我在 ViewModel 中调用

fetchAllTables()
时:

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

失败并显示

"Error fetching tables: access denied (code:23)"
。但它仍然可以使用该书签 URL 成功连接到该数据库,没有任何问题。 仅当我尝试执行 db.prepare(".....") 时才会发生错误。

如有任何建议,我们将不胜感激

swift macos sqlite
1个回答
0
投票

尝试检查竞争条件问题,我认为您应该仅在建立连接后调用 fetchAllTables()

作为快速测试,您可以尝试在

func fetchAllTables()
 之后致电 
db = try Connection(dbPath)

© www.soinside.com 2019 - 2024. All rights reserved.