Files
wehub-resource-sync 1d1286fadb
Build / Build and test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:18:38 +08:00

130 lines
4.0 KiB
Swift

import Foundation
@MainActor
public struct PluginRuntimeContext {
public let pluginID: String
public let resourceBundle: Bundle
public let resourceSubdirectory: String?
public let storage: PluginStorage
public let supportDirectory: URL?
public let cacheDirectory: URL?
public let temporaryDirectory: URL?
public init(
pluginID: String,
resourceBundle: Bundle = .main,
resourceSubdirectory: String? = nil,
storage: PluginStorage? = nil,
supportDirectory: URL? = nil,
cacheDirectory: URL? = nil,
temporaryDirectory: URL? = nil
) {
self.pluginID = pluginID
self.resourceBundle = resourceBundle
self.resourceSubdirectory = resourceSubdirectory
self.storage = storage ?? UserDefaultsPluginStorage(pluginID: pluginID)
self.supportDirectory = supportDirectory
self.cacheDirectory = cacheDirectory
self.temporaryDirectory = temporaryDirectory
}
public func resourceURL(forResource name: String, withExtension ext: String?) -> URL? {
if let resourceSubdirectory,
let url = resourceBundle.url(
forResource: name,
withExtension: ext,
subdirectory: resourceSubdirectory
) {
return url
}
return resourceBundle.url(forResource: name, withExtension: ext)
}
}
@MainActor
public protocol PluginStorage {
func object(forKey key: String) -> Any?
func data(forKey key: String) -> Data?
func string(forKey key: String) -> String?
func stringArray(forKey key: String) -> [String]?
func integer(forKey key: String) -> Int
func bool(forKey key: String) -> Bool
func set(_ value: Any?, forKey key: String)
func removeObject(forKey key: String)
func migrateValueIfNeeded(fromLegacyKey legacyKey: String, to key: String)
}
@MainActor
public final class UserDefaultsPluginStorage: PluginStorage {
private let pluginID: String
private let userDefaults: UserDefaults
public init(pluginID: String, userDefaults: UserDefaults = .standard) {
self.pluginID = pluginID
self.userDefaults = userDefaults
}
public func object(forKey key: String) -> Any? {
userDefaults.object(forKey: storageKey(for: key))
}
public func data(forKey key: String) -> Data? {
userDefaults.data(forKey: storageKey(for: key))
}
public func string(forKey key: String) -> String? {
userDefaults.string(forKey: storageKey(for: key))
}
public func stringArray(forKey key: String) -> [String]? {
userDefaults.stringArray(forKey: storageKey(for: key))
}
public func integer(forKey key: String) -> Int {
userDefaults.integer(forKey: storageKey(for: key))
}
public func bool(forKey key: String) -> Bool {
userDefaults.bool(forKey: storageKey(for: key))
}
public func set(_ value: Any?, forKey key: String) {
guard let value else {
removeObject(forKey: key)
return
}
userDefaults.set(value, forKey: storageKey(for: key))
}
public func removeObject(forKey key: String) {
userDefaults.removeObject(forKey: storageKey(for: key))
}
public func migrateValueIfNeeded(fromLegacyKey legacyKey: String, to key: String) {
let scopedKey = storageKey(for: key)
guard userDefaults.object(forKey: scopedKey) == nil,
let legacyValue = userDefaults.object(forKey: legacyKey)
else {
return
}
userDefaults.set(legacyValue, forKey: scopedKey)
userDefaults.removeObject(forKey: legacyKey)
}
private func storageKey(for key: String) -> String {
"plugin.\(pluginID).\(key)"
}
public static func removeAllValues(pluginID: String, userDefaults: UserDefaults = .standard) {
let prefix = "plugin.\(pluginID)."
for key in userDefaults.dictionaryRepresentation().keys where key.hasPrefix(prefix) {
userDefaults.removeObject(forKey: key)
}
}
}