Files
sing-box-for-apple/Library/Database/SharedPreferences+Database.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
}
}