diff --git a/Library/Database/Databse.swift b/Library/Database/Databse.swift index fea031b..c179201 100644 --- a/Library/Database/Databse.swift +++ b/Library/Database/Databse.swift @@ -1,40 +1,39 @@ import Foundation import GRDB -actor Database { - private static var writer: (any DatabaseWriter)? +enum Database { + static let sharedWriter = makeShared() - static func sharedWriter() throws -> any DatabaseWriter { - if let writer { - return writer - } - try FileManager.default.createDirectory(at: FilePath.sharedDirectory, withIntermediateDirectories: true) - let database = try DatabasePool(path: FilePath.sharedDirectory.appendingPathComponent("settings.db").relativePath) - var migrator = DatabaseMigrator().disablingDeferredForeignKeyChecks() - migrator.registerMigration("initialize") { db in - try db.create(table: "profiles") { t in - t.autoIncrementedPrimaryKey("id") - t.column("name", .text).notNull() - t.column("order", .integer).notNull() - t.column("type", .integer).notNull().defaults(to: ProfileType.local.rawValue) - t.column("path", .text).notNull() - t.column("remoteURL", .text) - t.column("autoUpdate", .boolean).notNull().defaults(to: false) - t.column("lastUpdated", .datetime) - } - try db.create(table: "preferences") { t in - t.primaryKey("name", .text, onConflict: .replace).notNull() - t.column("data", .blob) + private static func makeShared() -> any DatabaseWriter { + do { + try FileManager.default.createDirectory(at: FilePath.sharedDirectory, withIntermediateDirectories: true) + let database = try DatabasePool(path: FilePath.sharedDirectory.appendingPathComponent("settings.db").relativePath) + var migrator = DatabaseMigrator().disablingDeferredForeignKeyChecks() + migrator.registerMigration("initialize") { db in + try db.create(table: "profiles") { t in + t.autoIncrementedPrimaryKey("id") + t.column("name", .text).notNull() + t.column("order", .integer).notNull() + t.column("type", .integer).notNull().defaults(to: ProfileType.local.rawValue) + t.column("path", .text).notNull() + t.column("remoteURL", .text) + t.column("autoUpdate", .boolean).notNull().defaults(to: false) + t.column("lastUpdated", .datetime) + } + try db.create(table: "preferences") { t in + t.primaryKey("name", .text, onConflict: .replace).notNull() + t.column("data", .blob) + } } - } - migrator.registerMigration("add_auto_update_interval") { db in - try db.alter(table: "profiles") { t in - t.add(column: "autoUpdateInterval", .integer).notNull().defaults(to: 0) + migrator.registerMigration("add_auto_update_interval") { db in + try db.alter(table: "profiles") { t in + t.add(column: "autoUpdateInterval", .integer).notNull().defaults(to: 0) + } } + try migrator.migrate(database) + return database + } catch { + fatalError(error.localizedDescription) } - - try migrator.migrate(database) - writer = database - return database } } diff --git a/Library/Database/ProfileManager.swift b/Library/Database/ProfileManager.swift index c004d62..1032305 100644 --- a/Library/Database/ProfileManager.swift +++ b/Library/Database/ProfileManager.swift @@ -4,37 +4,37 @@ import GRDB public enum ProfileManager { public nonisolated static func create(_ profile: Profile) async throws { profile.order = try await nextOrder() - try await Database.sharedWriter().write { db in + try await Database.sharedWriter.write { db in try profile.insert(db, onConflict: .fail) } } public nonisolated static func get(_ profileID: Int64) async throws -> Profile? { - try await Database.sharedWriter().read { db in + try await Database.sharedWriter.read { db in try Profile.fetchOne(db, id: profileID) } } public nonisolated static func get(by profileName: String) async throws -> Profile? { - try await Database.sharedWriter().read { db in + try await Database.sharedWriter.read { db in try Profile.filter(Column("name") == profileName).fetchOne(db) } } public nonisolated static func delete(_ profile: Profile) async throws { - _ = try await Database.sharedWriter().write { db in + _ = try await Database.sharedWriter.write { db in try profile.delete(db) } } public nonisolated static func delete(by id: Int64) async throws { - _ = try await Database.sharedWriter().write { db in + _ = try await Database.sharedWriter.write { db in try Profile.deleteOne(db, id: id) } } public nonisolated static func delete(_ profileList: [Profile]) async throws -> Int { - try await Database.sharedWriter().write { db in + try await Database.sharedWriter.write { db in try Profile.deleteAll(db, keys: profileList.map { ["id": $0.id!] }) @@ -42,20 +42,20 @@ public enum ProfileManager { } public nonisolated static func delete(by id: [Int64]) async throws -> Int { - try await Database.sharedWriter().write { db in + try await Database.sharedWriter.write { db in try Profile.deleteAll(db, ids: id) } } public nonisolated static func update(_ profile: Profile) async throws { - _ = try await Database.sharedWriter().write { db in + _ = try await Database.sharedWriter.write { db in try profile.updateChanges(db) } } public nonisolated static func update(_ profileList: [Profile]) async throws { // TODO: batch update - try await Database.sharedWriter().write { db in + try await Database.sharedWriter.write { db in for profile in profileList { try profile.updateChanges(db) } @@ -63,25 +63,25 @@ public enum ProfileManager { } public nonisolated static func list() async throws -> [Profile] { - try await Database.sharedWriter().read { db in + try await Database.sharedWriter.read { db in try Profile.all().order(Column("order").asc).fetchAll(db) } } public nonisolated static func listRemote() async throws -> [Profile] { - try await Database.sharedWriter().read { db in + try await Database.sharedWriter.read { db in try Profile.filter(Column("type") == ProfileType.remote.rawValue).order(Column("order").asc).fetchAll(db) } } public nonisolated static func listAutoUpdateEnabled() async throws -> [Profile] { - try await Database.sharedWriter().read { db in + try await Database.sharedWriter.read { db in try Profile.filter(Column("autoUpdate") == true).order(Column("order").asc).fetchAll(db) } } public nonisolated static func nextID() async throws -> Int64 { - try await Database.sharedWriter().read { db in + try await Database.sharedWriter.read { db in if let lastProfile = try Profile.select(Column("id")).order(Column("id").desc).fetchOne(db) { return lastProfile.id! + 1 } else { @@ -91,7 +91,7 @@ public enum ProfileManager { } private nonisolated static func nextOrder() async throws -> UInt32 { - try await Database.sharedWriter().read { db in + try await Database.sharedWriter.read { db in try UInt32(Profile.fetchCount(db)) } } diff --git a/Library/Database/ShadredPreferences+Database.swift b/Library/Database/ShadredPreferences+Database.swift index 2cd9056..3dc9b13 100644 --- a/Library/Database/ShadredPreferences+Database.swift +++ b/Library/Database/ShadredPreferences+Database.swift @@ -37,7 +37,7 @@ extension SharedPreferences { } private nonisolated static func read(_ name: String) async throws -> T? { - guard let item = try await (Database.sharedWriter().read { db in + guard let item = try await (Database.sharedWriter.read { db in try Item.fetchOne(db, id: name) }) else { @@ -48,12 +48,12 @@ extension SharedPreferences { private nonisolated static func write(_ name: String, _ value: (some Codable)?) async throws { if value == nil { - _ = try await Database.sharedWriter().write { db in + _ = try await Database.sharedWriter.write { db in try Item.deleteOne(db, id: name) } } else { let data = try BinaryEncoder().encode(value) - try await Database.sharedWriter().write { db in + try await Database.sharedWriter.write { db in try Item(name: name, data: data).insert(db) } }