Search code examples
swiftuiswift5xcode11

SwiftUI: How to persist @Published variable using UserDefaults?


I want a @Published variable to be persisted, so that it's the same every time when I relaunch my app.

I want to use both the @UserDefault and @Published property wrappers on one variable. For example I need a '@PublishedUserDefault var isLogedIn'.

I have the following propertyWrapper

import Foundation

@propertyWrapper
struct UserDefault<T> {
    let key: String
    let defaultValue: T

    init(_ key: String, defaultValue: T) {
        self.key = key
        self.defaultValue = defaultValue
    }

    var wrappedValue: T {
        get {
            return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
        }
        set {
            UserDefaults.standard.set(newValue, forKey: key)
        }
    }
}

This is my Settings class

import SwiftUI
import Combine

 class Settings: ObservableObject {

   @Published var isLogedIn : Bool = false

 func doLogin(params:[String:String]) {

        Webservice().login(params: params) { response in

            if let myresponse = response {                    
                    self.login = myresponse.login
                    }
               }
         }

}

My View class

struct HomeView : View {
    @EnvironmentObject var settings: Settings
    var body: some View {
        VStack {
            if settings.isLogedIn {
            Text("Loged in")
            } else{
            Text("Not Loged in")
            }
        }
    }
}

Is there a way to make a single property wrapper that covers both the persisting and the publishing?


Solution

  • import SwiftUI
    import Combine
    
    fileprivate var cancellables = [String : AnyCancellable] ()
    
    public extension Published {
        init(wrappedValue defaultValue: Value, key: String) {
            let value = UserDefaults.standard.object(forKey: key) as? Value ?? defaultValue
            self.init(initialValue: value)
            cancellables[key] = projectedValue.sink { val in
                UserDefaults.standard.set(val, forKey: key)
            }
        }
    }
    
    class Settings: ObservableObject {
        @Published(key: "isLogedIn") var isLogedIn = false
        ...
    }
    

    Sample: https://youtu.be/TXdAg_YvBNE

    Version for all Codable types check out here