Update Models, Protocols, Services + docs
Summary: - Sources: Models, Protocols, Services - Docs: README - Added symbols: struct Serializer, struct StorageKeyEntry, func registerCatalog, func validateCatalogRegistration - Removed symbols: struct Serializer Stats: - 8 files changed, 43 insertions(+), 9 deletions(-)
This commit is contained in:
parent
91aa0df7f4
commit
59e2be1306
12
README.md
12
README.md
@ -159,10 +159,10 @@ LocalData can generate a catalog of all configured storage keys, even if no data
|
|||||||
|
|
||||||
```swift
|
```swift
|
||||||
struct AppStorageCatalog: StorageKeyCatalog {
|
struct AppStorageCatalog: StorageKeyCatalog {
|
||||||
static var allKeys: [StorageKeyDescriptor] {
|
static var allKeys: [StorageKeyEntry] {
|
||||||
[
|
[
|
||||||
.from(StorageKeys.AppVersionKey(), serializer: .json),
|
StorageKeyEntry(StorageKeys.AppVersionKey()),
|
||||||
.from(StorageKeys.UserPreferencesKey(), serializer: .json)
|
StorageKeyEntry(StorageKeys.UserPreferencesKey())
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,6 +175,12 @@ let report = StorageAuditReport.renderText(for: AppStorageCatalog.self)
|
|||||||
print(report)
|
print(report)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
3) Register the catalog to enforce usage:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
StorageRouter.shared.registerCatalog(AppStorageCatalog.self)
|
||||||
|
```
|
||||||
|
|
||||||
For dynamic key names, use a placeholder name and a note to describe how it is generated.
|
For dynamic key names, use a placeholder name and a note to describe how it is generated.
|
||||||
|
|
||||||
## Sample App
|
## Sample App
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public struct Serializer<Value: Codable & Sendable>: Sendable {
|
public struct Serializer<Value: Codable & Sendable>: Sendable, CustomStringConvertible {
|
||||||
public let encode: @Sendable (Value) throws -> Data
|
public let encode: @Sendable (Value) throws -> Data
|
||||||
public let decode: @Sendable (Data) throws -> Value
|
public let decode: @Sendable (Data) throws -> Value
|
||||||
public let name: String
|
public let name: String
|
||||||
@ -15,6 +15,8 @@ public struct Serializer<Value: Codable & Sendable>: Sendable {
|
|||||||
self.name = name
|
self.name = name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var description: String { name }
|
||||||
|
|
||||||
public static var json: Serializer<Value> {
|
public static var json: Serializer<Value> {
|
||||||
Serializer<Value>(
|
Serializer<Value>(
|
||||||
encode: { try JSONEncoder().encode($0) },
|
encode: { try JSONEncoder().encode($0) },
|
||||||
|
|||||||
@ -10,6 +10,7 @@ public enum StorageError: Error {
|
|||||||
case invalidUserDefaultsSuite(String)
|
case invalidUserDefaultsSuite(String)
|
||||||
case dataTooLargeForSync
|
case dataTooLargeForSync
|
||||||
case notFound
|
case notFound
|
||||||
|
case unregisteredKey(String)
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ public struct StorageKeyDescriptor: Sendable {
|
|||||||
public let syncPolicy: SyncPolicy
|
public let syncPolicy: SyncPolicy
|
||||||
public let notes: String?
|
public let notes: String?
|
||||||
|
|
||||||
public init(
|
init(
|
||||||
name: String,
|
name: String,
|
||||||
domain: StorageDomain,
|
domain: StorageDomain,
|
||||||
security: SecurityPolicy,
|
security: SecurityPolicy,
|
||||||
@ -35,14 +35,13 @@ public struct StorageKeyDescriptor: Sendable {
|
|||||||
|
|
||||||
public static func from<Key: StorageKey>(
|
public static func from<Key: StorageKey>(
|
||||||
_ key: Key,
|
_ key: Key,
|
||||||
serializer: Serializer<Key.Value>,
|
|
||||||
notes: String? = nil
|
notes: String? = nil
|
||||||
) -> StorageKeyDescriptor {
|
) -> StorageKeyDescriptor {
|
||||||
StorageKeyDescriptor(
|
StorageKeyDescriptor(
|
||||||
name: key.name,
|
name: key.name,
|
||||||
domain: key.domain,
|
domain: key.domain,
|
||||||
security: key.security,
|
security: key.security,
|
||||||
serializer: serializer.name,
|
serializer: key.serializer.name,
|
||||||
valueType: String(describing: Key.Value.self),
|
valueType: String(describing: Key.Value.self),
|
||||||
owner: key.owner,
|
owner: key.owner,
|
||||||
availability: key.availability,
|
availability: key.availability,
|
||||||
|
|||||||
7
Sources/LocalData/Models/StorageKeyEntry.swift
Normal file
7
Sources/LocalData/Models/StorageKeyEntry.swift
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
public struct StorageKeyEntry: Sendable {
|
||||||
|
public let descriptor: StorageKeyDescriptor
|
||||||
|
|
||||||
|
public init<Key: StorageKey>(_ key: Key, notes: String? = nil) {
|
||||||
|
self.descriptor = .from(key, notes: notes)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,3 @@
|
|||||||
public protocol StorageKeyCatalog {
|
public protocol StorageKeyCatalog {
|
||||||
static var allKeys: [StorageKeyDescriptor] { get }
|
static var allKeys: [StorageKeyEntry] { get }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import Foundation
|
|||||||
|
|
||||||
public struct StorageAuditReport: Sendable {
|
public struct StorageAuditReport: Sendable {
|
||||||
public static func items<C: StorageKeyCatalog>(for catalog: C.Type) -> [StorageKeyDescriptor] {
|
public static func items<C: StorageKeyCatalog>(for catalog: C.Type) -> [StorageKeyDescriptor] {
|
||||||
catalog.allKeys
|
catalog.allKeys.map(\.descriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func renderText<C: StorageKeyCatalog>(for catalog: C.Type) -> String {
|
public static func renderText<C: StorageKeyCatalog>(for catalog: C.Type) -> String {
|
||||||
|
|||||||
@ -9,6 +9,8 @@ public actor StorageRouter: StorageProviding {
|
|||||||
|
|
||||||
public static let shared = StorageRouter()
|
public static let shared = StorageRouter()
|
||||||
|
|
||||||
|
private var registeredKeyNames: Set<String> = []
|
||||||
|
|
||||||
private init() {}
|
private init() {}
|
||||||
|
|
||||||
// MARK: - Key Material Providers
|
// MARK: - Key Material Providers
|
||||||
@ -21,6 +23,12 @@ public actor StorageRouter: StorageProviding {
|
|||||||
await EncryptionHelper.shared.registerKeyMaterialProvider(provider, for: source)
|
await EncryptionHelper.shared.registerKeyMaterialProvider(provider, for: source)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Registers a catalog of known storage keys for audit and validation.
|
||||||
|
/// When registered, all storage operations will verify keys are listed.
|
||||||
|
public func registerCatalog<C: StorageKeyCatalog>(_ catalog: C.Type) {
|
||||||
|
registeredKeyNames = Set(catalog.allKeys.map { $0.descriptor.name })
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - StorageProviding Implementation
|
// MARK: - StorageProviding Implementation
|
||||||
|
|
||||||
/// Stores a value for the given key.
|
/// Stores a value for the given key.
|
||||||
@ -29,6 +37,7 @@ public actor StorageRouter: StorageProviding {
|
|||||||
/// - key: The storage key defining where and how to store.
|
/// - key: The storage key defining where and how to store.
|
||||||
/// - Throws: Various errors depending on the storage domain and security policy.
|
/// - Throws: Various errors depending on the storage domain and security policy.
|
||||||
public func set<Key: StorageKey>(_ value: Key.Value, for key: Key) async throws {
|
public func set<Key: StorageKey>(_ value: Key.Value, for key: Key) async throws {
|
||||||
|
try validateCatalogRegistration(for: key)
|
||||||
try validatePlatformAvailability(for: key)
|
try validatePlatformAvailability(for: key)
|
||||||
|
|
||||||
let data = try serialize(value, with: key.serializer)
|
let data = try serialize(value, with: key.serializer)
|
||||||
@ -43,6 +52,7 @@ public actor StorageRouter: StorageProviding {
|
|||||||
/// - Returns: The stored value.
|
/// - Returns: The stored value.
|
||||||
/// - Throws: `StorageError.notFound` if no value exists, plus domain-specific errors.
|
/// - Throws: `StorageError.notFound` if no value exists, plus domain-specific errors.
|
||||||
public func get<Key: StorageKey>(_ key: Key) async throws -> Key.Value {
|
public func get<Key: StorageKey>(_ key: Key) async throws -> Key.Value {
|
||||||
|
try validateCatalogRegistration(for: key)
|
||||||
try validatePlatformAvailability(for: key)
|
try validatePlatformAvailability(for: key)
|
||||||
|
|
||||||
guard let securedData = try await retrieve(for: key) else {
|
guard let securedData = try await retrieve(for: key) else {
|
||||||
@ -57,6 +67,7 @@ public actor StorageRouter: StorageProviding {
|
|||||||
/// - Parameter key: The storage key to remove.
|
/// - Parameter key: The storage key to remove.
|
||||||
/// - Throws: Domain-specific errors if removal fails.
|
/// - Throws: Domain-specific errors if removal fails.
|
||||||
public func remove<Key: StorageKey>(_ key: Key) async throws {
|
public func remove<Key: StorageKey>(_ key: Key) async throws {
|
||||||
|
try validateCatalogRegistration(for: key)
|
||||||
try validatePlatformAvailability(for: key)
|
try validatePlatformAvailability(for: key)
|
||||||
try await delete(for: key)
|
try await delete(for: key)
|
||||||
}
|
}
|
||||||
@ -65,6 +76,7 @@ public actor StorageRouter: StorageProviding {
|
|||||||
/// - Parameter key: The storage key to check.
|
/// - Parameter key: The storage key to check.
|
||||||
/// - Returns: True if a value exists.
|
/// - Returns: True if a value exists.
|
||||||
public func exists<Key: StorageKey>(_ key: Key) async throws -> Bool {
|
public func exists<Key: StorageKey>(_ key: Key) async throws -> Bool {
|
||||||
|
try validateCatalogRegistration(for: key)
|
||||||
try validatePlatformAvailability(for: key)
|
try validatePlatformAvailability(for: key)
|
||||||
|
|
||||||
switch key.domain {
|
switch key.domain {
|
||||||
@ -91,6 +103,13 @@ public actor StorageRouter: StorageProviding {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func validateCatalogRegistration<Key: StorageKey>(for key: Key) throws {
|
||||||
|
guard !registeredKeyNames.isEmpty else { return }
|
||||||
|
guard registeredKeyNames.contains(key.name) else {
|
||||||
|
throw StorageError.unregisteredKey(key.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Serialization
|
// MARK: - Serialization
|
||||||
|
|
||||||
private func serialize<Value: Codable & Sendable>(
|
private func serialize<Value: Codable & Sendable>(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user