// // Preference.swift // TuskerPreferences // // Created by Shadowfacts on 4/13/24. // import Foundation import Combine // TODO: once we target iOS 17, use Observable for this @propertyWrapper final class Preference: Codable { @Published private(set) var storedValue: Key.Value? var wrappedValue: Key.Value { get { storedValue ?? Key.defaultValue } set { fatalError("unreachable") } } init() { self.storedValue = nil } init(from decoder: any Decoder) throws { if let container = try? decoder.singleValueContainer() { self.storedValue = try? container.decode(Key.Value.self) } } func encode(to encoder: any Encoder) throws { if let storedValue { var container = encoder.singleValueContainer() try container.encode(storedValue) } } static subscript( _enclosingInstance instance: PreferenceStore, wrapped wrappedKeyPath: ReferenceWritableKeyPath, storage storageKeyPath: ReferenceWritableKeyPath ) -> Key.Value { get { get(enclosingInstance: instance, wrapped: wrappedKeyPath, storage: storageKeyPath) } set { set(enclosingInstance: instance, wrapped: wrappedKeyPath, storage: storageKeyPath, newValue: newValue) Key.didSet(in: instance, newValue: newValue) } } // for testing only @inline(__always) static func get( enclosingInstance: Enclosing, wrapped: ReferenceWritableKeyPath, storage: ReferenceWritableKeyPath ) -> Key.Value where Enclosing: ObservableObject, Enclosing.ObjectWillChangePublisher == ObservableObjectPublisher { let pref = enclosingInstance[keyPath: storage] return pref.storedValue ?? Key.defaultValue } // for testing only @inline(__always) static func set( enclosingInstance: Enclosing, wrapped: ReferenceWritableKeyPath, storage: ReferenceWritableKeyPath, newValue: Key.Value ) where Enclosing: ObservableObject, Enclosing.ObjectWillChangePublisher == ObservableObjectPublisher { enclosingInstance.objectWillChange.send() let pref = enclosingInstance[keyPath: storage] pref.storedValue = newValue } var projectedValue: some Publisher { $storedValue.map { $0 ?? Key.defaultValue } } }