// // LazyDecoding.swift // Tusker // // Created by Shadowfacts on 4/11/20. // Copyright © 2020 Shadowfacts. All rights reserved. // import Foundation private let decoder = PropertyListDecoder() private let encoder = PropertyListEncoder() // todo: invalidate cache on underlying data change using KVO? @propertyWrapper public struct LazilyDecoding { private let keyPath: ReferenceWritableKeyPath private let fallback: Value private var value: Value? init(from keyPath: ReferenceWritableKeyPath, fallback: Value) { self.keyPath = keyPath self.fallback = fallback } public var wrappedValue: Value { get { fatalError("called LazilyDecoding wrappedValue getter") } set { fatalError("called LazilyDecoding wrappedValue setter") } } public static subscript(_enclosingInstance instance: Enclosing, wrapped wrappedKeyPath: ReferenceWritableKeyPath, storage storageKeyPath: ReferenceWritableKeyPath) -> Value { get { var wrapper = instance[keyPath: storageKeyPath] if let value = wrapper.value { return value } else { guard let data = instance[keyPath: wrapper.keyPath] else { return wrapper.fallback } do { let value = try decoder.decode(Value.self, from: data) wrapper.value = value instance[keyPath: storageKeyPath] = wrapper return value } catch { return wrapper.fallback } } } set { var wrapper = instance[keyPath: storageKeyPath] wrapper.value = newValue instance[keyPath: storageKeyPath] = wrapper let newData = try? encoder.encode(newValue) instance[keyPath: wrapper.keyPath] = newData } } } extension LazilyDecoding { init(arrayFrom keyPath: ReferenceWritableKeyPath) { self.init(from: keyPath, fallback: [] as! Value) } }