RadrootsAppleIdentityMetadataStore.swift (2074B)
1 import Foundation 2 3 public final class RadrootsAppleIdentityMetadataStore: RadrootsIdentityMetadataStore, 4 @unchecked Sendable 5 { 6 private let lock = NSLock() 7 private let userDefaults: UserDefaults 8 private let keyPrefix: String 9 10 public init( 11 namespace: String, 12 userDefaults: UserDefaults = .standard, 13 keyPrefix: String = "org.radroots.kit.identity" 14 ) throws { 15 self.userDefaults = userDefaults 16 self.keyPrefix = try Self.normalizedPrefix(keyPrefix, namespace: namespace) 17 } 18 19 public func data(for slot: RadrootsIdentityMetadataSlot) throws -> Data? { 20 lock.lock() 21 defer { lock.unlock() } 22 return userDefaults.data(forKey: key(for: slot)) 23 } 24 25 public func put(_ data: Data, for slot: RadrootsIdentityMetadataSlot) throws { 26 guard !data.isEmpty, data.count <= 64 * 1024 else { 27 throw RadrootsIdentityCustodyError.invalidMetadata 28 } 29 lock.lock() 30 userDefaults.set(data, forKey: key(for: slot)) 31 lock.unlock() 32 } 33 34 public func delete(_ slot: RadrootsIdentityMetadataSlot) throws { 35 lock.lock() 36 userDefaults.removeObject(forKey: key(for: slot)) 37 lock.unlock() 38 } 39 40 func key(for slot: RadrootsIdentityMetadataSlot) -> String { 41 "\(keyPrefix).\(slot.rawValue)" 42 } 43 44 private static func normalizedPrefix(_ value: String, namespace: String) throws -> String { 45 let prefix = value.trimmingCharacters(in: .whitespacesAndNewlines) 46 let namespace = namespace.trimmingCharacters(in: .whitespacesAndNewlines) 47 guard !prefix.isEmpty, 48 prefix.utf8.count <= 128, 49 !namespace.isEmpty, 50 namespace.utf8.count <= 128, 51 !prefix.unicodeScalars.contains(where: CharacterSet.controlCharacters.contains), 52 !namespace.unicodeScalars.contains(where: CharacterSet.controlCharacters.contains) 53 else { 54 throw RadrootsIdentityCustodyError.invalidConfiguration 55 } 56 return "\(prefix).\(namespace)" 57 } 58 }