130 lines
4.0 KiB
Swift
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)
|
|
}
|
|
}
|
|
}
|