UserDefaults is not saved with Swift
Asked Answered
S

5

19

I'm trying to use the UserDefaults to persistently save a boolean value. This is my code :

public static var isOffline = UserDefaults.standard.bool(forKey: "isOffline") {
    didSet {
        print("Saving isOffline flag which is now \(isOffline)")
        UserDefaults.standard.set(isOffline, forKey: "isOffline")
        UserDefaults.standard.synchronize()
    }
}

Why doesn't work? What is the problem in this code?

The problem is that when I try to retrieve "isOffline" key from UserDefaults I always get a false.

I set the isOffline in the .onChange method of the row (I'm using Eureka as framework to create forms). The flag keep the right value during the lifecycle of the app, but when I close it that value is probably removed in some way.

Seethrough answered 23/12, 2016 at 13:2 Comment(9)
In what way doesn't your code work? Please show us a minimal reproducible example.Gallican
Also note that you don't need to call synchronize() every time you change a value in the user defaults – only when you specifically need the system to save the user defaults immediately (e.g when your app is about to be terminated) – see this Q&A.Gallican
Yes, you're right. But now the problem is that the "isOffline" flag always returns falseSeethrough
How and where are you setting isOffline?Gallican
@Gallican I've added more detailsSeethrough
I assume by "The flag keep the right value during the lifecycle of the app" you mean that the print statement is successfully printing out true when you update the value? Are you updating the value for the "isOffline" key anywhere else in your app? Although note that I would agree with Venkat's suggestion of using a computed property for this, as a stored property won't be updated if the user default for "isOffline" is changed from elsewhere in your program – although that doesn't explain why your current code is failing to save values.Gallican
The value is not changed anywhere else in the app.Seethrough
Xcode 8 simulator had a problem with saving data to UserDefaults, and it was fixable by rebooting mac os. Maybe that's the case? #37824690Huysmans
I found the problem. UserDefaults can be saved only on the main thread. I wrote the line which saves the isOffline based on the value of the row outside the onChange block putting it in the NSUserDefaults and everything works goodSeethrough
E
32

I had the same problem and the issue was in the "didSet" block itself. I don't know why, but it does not work with userDefaults - it does not persist it properly and after killing the application all changes were gone.

Synchronize() does not help. I found out, this method is no longer necessary and it will be deprecated in future (this is comment in UserDefaults class):

-synchronize is deprecated and will be marked with the NS_DEPRECATED macro in a future release.

By trial and error I found out, that it works, if I call it from main thread:

public static var isOffline = UserDefaults.standard.bool(forKey: "isOffline") {
    didSet {
        print("Saving isOffline flag which is now \(isOffline)")
        DispatchQueue.main.async {
            UserDefaults.standard.set(isOffline, forKey: "isOffline")
        }
    }
}

If anyone can explain, why it works on main thread and no other, I would be glad to hear it.

Exordium answered 2/6, 2017 at 7:39 Comment(1)
"At runtime, you use UserDefaults objects to read the defaults that your app uses from a user’s defaults database. UserDefaults caches the information to avoid having to open the user’s defaults database each time you need a default value. When you set a default value, it’s changed synchronously within your process, and asynchronously to persistent storage and other processes." It might be because of the cache and making changes on the main thread might be the reason that the defaults is written instantaneously.Kris
B
8

try to change

UserDefaults.standard.set(isOffline, forKey: "isOffline")

to

UserDefaults.standard.setValue(isOffline, forKey: "isOffline")

without the dispatch code

Benzo answered 2/4, 2019 at 12:35 Comment(3)
Do you know why the set() function doesn't work? Because it seems kind of random on my devices. Sometimes it works, sometimes it works for a few values. Sometimes it doesn't. The strange thing is. Sometimes I store the values, then load them and get nothing. Then I restart the app and suddenly some of the values are back. With setValue it looks like those problems went away. But I would really like to know WHY! :)Mandrill
i am sorry, i dont know the reason, as I'm happy its working for all of us. Apple works in mysterious ways :pBenzo
Yeah, the problem went away with setValue than set.Warbler
F
2

Do Like this,

public static var isOffline:Bool {
    get {
       return UserDefaults.standard.bool(forKey: "isOffline")
    }
    set(newValue) {
        print("Saving isOffline flag which is now \(isOffline)")
        UserDefaults.standard.set(newValue, forKey: "isOffline")
        UserDefaults.standard.synchronize()
    }
}
Flaming answered 23/12, 2016 at 13:15 Comment(4)
Your first example won't work as the so called "newValue" you're binding is actually the old value of the property. But neither example answers OP's question of "What is the problem in this code?".Gallican
First example also works. I personally tested and then only I posted.Flaming
I've been struggling and debugging for days... and all it needed was .synchronizeValentin
However, if you take a look at the function description developer.apple.com/documentation/foundation/nsuserdefaults/… : "Waits for any pending asynchronous updates to the defaults database and returns; this method is unnecessary and shouldn't be used."Engross
S
2

Don't ask me why, but I had this issue also in a project in Xcode 14.

After hours of debugging I realized that the length of my key exceeded 18 characters, it would no longer be saved. However, I couldn't reproduce this in a vanilla project, so I guess it had something to do with the project.

But anyway: if you're experiencing this, try decreasing the character count of your key.

Savoirvivre answered 1/11, 2022 at 11:56 Comment(0)
H
0

Had the same issue and realized that keys with a mix of uppercase and lowercase characters that also had underscores were not saving. Removing the underscores worked. Keys that were all uppercase and had underscores were working. Keys containing periods were not working. Very strange, but worth a try if you are stuck.

Haarlem answered 20/3 at 14:22 Comment(3)
You are using Objective-C keys here; they have rules and conventions, and if you don't know about them, you can fall foul of them.Pusher
@Pusher do you have details or a link about these "Objective-C keys"?Drug
@Drug The key value coding guide is a good place to start, though it is not exhaustive on the matter.Pusher

© 2022 - 2024 — McMap. All rights reserved.