116 lines
3.0 KiB
Swift
116 lines
3.0 KiB
Swift
import BinaryCodable
|
|
import Foundation
|
|
import GRDB
|
|
import os
|
|
|
|
private let logger = Logger(category: "SharedPreferences")
|
|
|
|
extension SharedPreferences {
|
|
public class Preference<T: Codable> {
|
|
let name: String
|
|
private let defaultValue: T
|
|
|
|
init(_ name: String, defaultValue: T) {
|
|
self.name = name
|
|
self.defaultValue = defaultValue
|
|
}
|
|
|
|
public nonisolated func get() async -> T {
|
|
do {
|
|
return try await SharedPreferences.read(name) ?? defaultValue
|
|
} catch {
|
|
logger.error("read preferences error: \(error)")
|
|
return defaultValue
|
|
}
|
|
}
|
|
|
|
public func getBlocking() -> T {
|
|
runBlocking { [self] in
|
|
await get()
|
|
}
|
|
}
|
|
|
|
public nonisolated func set(_ newValue: T?) async {
|
|
do {
|
|
try await SharedPreferences.write(name, newValue)
|
|
} catch {
|
|
logger.error("write preferences error: \(error)")
|
|
}
|
|
}
|
|
}
|
|
|
|
private nonisolated static func read<T: Codable>(_ name: String) async throws -> T? {
|
|
guard let item = try await (Database.sharedWriter.read { db in
|
|
try Item.fetchOne(db, id: name)
|
|
})
|
|
else {
|
|
return nil
|
|
}
|
|
if T.self == String.self {
|
|
return String(data: item.data, encoding: .utf8) as? T
|
|
} else {
|
|
return try BinaryDecoder().decode(from: item.data)
|
|
}
|
|
}
|
|
|
|
private nonisolated static func write(_ name: String, _ value: (some Codable)?) async throws {
|
|
if value == nil {
|
|
_ = try await Database.sharedWriter.write { db in
|
|
try Item.deleteOne(db, id: name)
|
|
}
|
|
} else {
|
|
let data: Data
|
|
if let stringValue = value as? String {
|
|
data = stringValue.data(using: .utf8)!
|
|
} else {
|
|
data = try BinaryEncoder().encode(value)
|
|
}
|
|
try await Database.sharedWriter.write { db in
|
|
try Item(name: name, data: data).save(db)
|
|
}
|
|
}
|
|
}
|
|
|
|
nonisolated static func batchDelete(_ names: [String]) async throws {
|
|
try await Database.sharedWriter.write { db in
|
|
for name in names {
|
|
try Item.deleteOne(db, id: name)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private class Item: Record, Identifiable {
|
|
var id: String {
|
|
name
|
|
}
|
|
|
|
var name: String
|
|
var data: Data
|
|
|
|
init(name: String, data: Data) {
|
|
self.name = name
|
|
self.data = data
|
|
super.init()
|
|
}
|
|
|
|
override class var databaseTableName: String {
|
|
"preferences"
|
|
}
|
|
|
|
enum Columns: String, ColumnExpression {
|
|
case name, data
|
|
}
|
|
|
|
required init(row: Row) throws {
|
|
name = row[Columns.name]
|
|
data = row[Columns.data]
|
|
try super.init(row: row)
|
|
}
|
|
|
|
override func encode(to container: inout PersistenceContainer) throws {
|
|
container[Columns.name] = name
|
|
container[Columns.data] = data
|
|
}
|
|
}
|