UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: How to use UserDefaults.publisher in SwiftUI?

Forums > SwiftUI

UserDefaults.publisher works really well in UIKit, and even Apple's documentation mainly revolves around that. However, I'm unable to get the desired behaviour of continous observing of values using UserDefaults.publisher in SwiftUI.

Example UIKit Code that works perfectly:

import UIKit
import Combine

extension UserDefaults {
    @objc dynamic var test: Int {
        return integer(forKey: "test")
    }
}

class ViewController: UIViewController {
    var subscriber: AnyCancellable?   // Subscriber of preference changes.

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        subscriber = UserDefaults.standard
        .publisher(for: \.test)
            .sink() {
                print($0)
        }

        UserDefaults.standard.set(5, forKey: "test")
        UserDefaults.standard.set(10, forKey: "test")
        UserDefaults.standard.set(100, forKey: "test")
    }
}

Output of above code:
[initial value]
5
10
100

Problematic SwiftUI code:

import SwiftUI
import Combine

extension UserDefaults {
    @objc dynamic var userValue: Int {
        return integer(forKey: "value")
    }
}

struct ContentView: View {
    //    @ObservedObject var auth = AuthModel()
    @State var cancellable: AnyCancellable? = UserDefaults.standard
        .publisher(for: \.userValue)
        .sink() {
            print($0)
    }

    var body: some View {
         Text("hello!") 
    }
}

Output: Prints the value stored once, however, after that, it doesn't reprints the values automatically on changes.

I've tried using a separate Model class, but the issue stays and the core reason remains the same I guess.

Any help would be greatly appreciated, thanks!

Correct Answer:

https://forums.developer.apple.com/message/423512#423512

In the UserDefault extension, the variable name of the @obj dynamic var and the key used in return statement shall be the same. Another point: we can use a normal variable/constant or a property wrapper like @State, all work.

Thanks!

2      

For some reason cancellables don't work with property wrappers (or at least @State). If you remove @State to make the whole declaration be just

private let cancellable = UserDefaults.standard.publisher(for: \.value).sink {
    print($0)
}

it should work.

2      

Thanks a lot for your reply! However, changing @State to a a normal constant/variable is giving me the same behaviour.

2      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.