83 lines
2.3 KiB
Swift

//
// KeychainStorage.swift
// KeychainStorage
//
// Created by Javier Cicchelli on 11/12/2022.
// Copyright © 2022 Röck+Cöde. All rights reserved.
//
import Foundation
import KeychainAccess
import SwiftUI
@propertyWrapper
public struct KeychainStorage<Model: Codable>: DynamicProperty {
// MARK: Type aliases
public typealias Value = Model
// MARK: States
@State private var value: Value?
// MARK: Properties
private let key: String
private let keychain: Keychainable
private let decoder: JSONDecoder = .init()
private let encoder: JSONEncoder = .init()
public var wrappedValue: Value? {
get { value }
nonmutating set {
value = newValue
do {
if let newValue {
let encodedValue = try encoder.encode(newValue)
try keychain.set(encodedValue, key: key, ignoringAttributeSynchronizable: true)
} else {
try keychain.remove(key, ignoringAttributeSynchronizable: true)
}
} catch {
assertionFailure("The '\(key)' key should have either be set or removed from the keychain storage.")
}
}
}
public var projectedValue: Binding<Value?> {
.init { wrappedValue } set: { wrappedValue = $0 }
}
// MARK: Initialisers
public init(
key: String,
defaultValue: Value? = nil,
keychain: Keychainable = Keychain()
) {
self.key = key
self.keychain = keychain
do {
guard let data = try keychain.getData(key, ignoringAttributeSynchronizable: true) else {
self._value = .init(initialValue: defaultValue)
return
}
do {
let model = try decoder.decode(Value.self, from: data)
self._value = .init(initialValue: model)
} catch {
assertionFailure("The data for the '\(key)' key should have been decoded properly.")
}
} catch {
assertionFailure("The data of the '\(key)' key should have been obtained from the keychain storage.")
}
}
}