apple_kit

Apple-native services for Radroots iOS and macOS apps
git clone https://radroots.dev/git/apple_kit.git
Log | Files | Refs | README | LICENSE

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 }