I want a variable to be an EnvironmentObject and I also want it to be persisted, so that it's the same every time that I relaunch my app.
To achieve that, I have already created the following propertyWrapper:
import Foundation
@propertyWrapper
struct UserDefault<T: Codable> {
    let key: String
    let defaultValue: T
    var wrappedValue: T {
        get {
            if let encodedValue = UserDefaults.standard.object(forKey: key) as? Data {
                let decoder = JSONDecoder()
                let decodedValue = try! decoder.decode(T.self, from: encodedValue)
                return decodedValue
            } else {
                return defaultValue
            }
        } set {
            let encoder = JSONEncoder()
            let encodedValue = try! encoder.encode(newValue)
            UserDefaults.standard.set(encodedValue, forKey: key)
        }
    }
}
But already having a property wrapper means that I can't use the @Published property wrapper from Combine. (Using two property wrappers on one variable doesn't sound like a good idea, and I haven't found a way to get that working.)
I solved that problem by making a custom objectWillChange let constant and calling its .send(input:) method in willSet for every variable. 
So this is my DataStore class:
import SwiftUI
import Combine
final class DataStore: ObservableObject {
    let objectWillChange = PassthroughSubject<DataStore, Never>()
    @UserDefault(key: "the text", defaultValue: "Hello world!")
    var text: String {
        willSet {
            objectWillChange.send(self)
        }
    }
}
And this is my View:
struct StartView : View {
    @EnvironmentObject var dataStore: DataStore
    var body: some View {
        VStack {
            TextField("Enter text", text: $dataStore.text)
            Button("Reset text", action: {
                self.dataStore.text = "Hello World!"
            })
        }
    }
}
But somehow I really believe that there should be a more beautiful way than making a custom objectWillChange. Is there a way to make a single property wrapper that covers both the persisting and the "publishing"? Or should I do something completely different, to reach my goal?
Thanks!
Based on the guessed implementation of Genetec Tech (https://medium.com/genetec-tech/property-wrappers-in-swift-5-1-the-missing-published-implementation-1a466ebcf660) you could combine the two property wrappers into one @PublishedUserDefault.
Example:
This is the normal propertyWrapper for the UserDefaults
@propertyWrapper
struct UserDefault<T> {
    let key: String
    let defaultValue: T
    var wrappedValue: T {
        get {
            UserDefaults.standard.value(forKey: key) as? T ?? defaultValue
        } set {
            UserDefaults.standard.set(newValue, forKey: key)
        }
    }
}
This viewModel also updates, when UserDefaults.set(_ ,forKey:) is called.
final class UserSettings: ObservableObject {
    let objectWillChange = ObservableObjectPublisher()
    @UserDefault(key: "text", defaultValue: "")
    var text: String
    private var notificationSubscription: AnyCancellable?
    init() {
        notificationSubscription = NotificationCenter.default.publisher(for: UserDefaults.didChangeNotification).sink { _ in
            self.objectWillChange.send()
        }
    }
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With