I'm seeking a more streamlined syntax for the given code. I'd like to consolidate all UserDefaults properties within a distinct UserSettings struct.
Both method 1 and method 4 are functioning correctly. I'm exploring the feasibility of adopting a cleaner syntax akin to method 3. In other words, is it possible to utilize a property wrapper conforming to DynamicProperty from another struct within a View?
I acknowledge the use of AppStorage, but it has restricted functionality, such as requiring the default value to be set in each view where it is used or a custom setter. That's why I prefer to have a centralized location to manage it in Usersettings.
import SwiftUI
@propertyWrapper
public struct MyUserDefault<Value>: DynamicProperty {
let key: String
let defaultValue: Value
var container: UserDefaults = .standard
@State var cachedValue: Value
public init(key: String, defaultValue: Value) {
self.key = key
self.defaultValue = defaultValue
self._cachedValue = State(initialValue: (container.object(forKey: key) as? Value ?? defaultValue))
}
public var wrappedValue: Value {
get { cachedValue }
nonmutating set { setNewValue(newValue) }
}
public var projectedValue: Binding<Value> {
.init(get: getValue, set: { n in setNewValue(n)} )
}
private func getValue() -> Value { cachedValue }
private func setNewValue(_ newValue: Value) {
container.set(newValue, forKey: key)
container.synchronize()
cachedValue = newValue
print("UserDefaults updated to: \(newValue)") ///
}
}
struct UserSettings {
@MyUserDefault(key: "number", defaultValue: 0)
static var number: Int
}
struct SwiftUIView10: View {
/// Method 1: Using dynamic ProperyWrapper in View
@MyUserDefault(key: "number", defaultValue: 10)
var number: Int
/// Method 2: Using Setting struct
// var number = Binding {
// UserSettings.number
// } set: {
// UserSettings.number = $0
// }
// var number = Binding(projectedValue: UserSettings.$number) //or this
/// Method 3: Using possible cleaner syntax
//@Binding var number = UserSettings.number
/// Method 4: Using possible cleaner syntax
// @State var number = UserSettings.number /// along with `onChange` to update UserSettings.number
var body: some View {
Text("#\(number)") // use number.wrappedValue in method 2
Button("increase") {
number += 1 // use number.wrappedValue in method 2
print("number in view \(number)")
}
}
}
#Preview {
SwiftUIView10()
}
Thank you all. I thought of a way to do it, and I'm sharing it here in case you're interested.