User Defaults Not Working When Trying to Save the Settings Information

Hi,


When I try to save the information provided by the user on the Settings view and come back to the settings again it does not load the previous stored information. This is how I am saving:


After entering all the fields and pressing the "submit" button, I save the information using User Defaults:


let advancedSettingArray = [self.custCodeTextField.text, self.pcsAddrTextField.text]

UserDefaults.standard.set(advancedSettingArray, forKey: "ArrayKey")

UserDefaults.standard.synchronize()


When I navigate back to the Settings page, I use the following code in ViewDidLoad() :



let defaults = UserDefaults.standard

if let myArray = defaults.object(forKey: "ArrayKey") as? [String] {

print(myArray)

}


It does not work, somehow. Can someone please tell what I am doing wrong.


Thanks,

Marc.

Post not yet marked as solved Up vote post of marc12 Down vote post of marc12
9.5k views

Replies

“does not work” isn’t enough information to go on. Do you have compiler errors? Does it crash? What does that print statement print?

It's always nil. It does not either crash or give any compiler crash.

Well then it’s probably something to do with the way you’re handling optionals. I’m not a Swift guy so I can’t comment on that with any authority. But be careful with them. For example the “text” property of a UITextField returns an optional, not a String. Not sure how user defaults handles that when you pass an array of them. Likewise object(forKey:) returns an Any? and I don’t know the ins and outs of trying to cast that to [String]?.


You could try using stringArray(forKey:) which returns the type you’re looking for (well, an optional of that type).

I understand, what you are saying, but when I simply pass a string value instead of an array of strings, still User Deafluts gives me nil. Is, there something I am missing?

Can someone please help me with this?


Thanks,

Marc.

Are you sure UserDefaults is returning nil, or is it the “as? [String]” attempted cast that turns it to nil?

Yes, I am sure. When I am doing simply this, still I am getting nil, it never prints the string value.

//set

UserDefaults.standard.set("test", forKey: "ArrayKey")

UserDefaults.standard.synchronize()

//get

if let temp = defaults.string(forKey: "ArrayKey") {

print(temp)

}

Taking a different tactic. How do you “navigate back to the Settings page”? If it‘s a tab in a tab bar controller, for example, then viewDidLoad wouldn’t be called again. If you set a breakpoint on the code that is supposed to be retrieving the value, is that code actually being executed when you expect?

Settings page is in a different navigation controller, yes I did put a breaking point, it do go through ViewDidLoad(). But somehow I don't have the value. I have no clue what to do? My main activity is on one Navigation controller and Settings is on another.

The code you posted doesn’t compile, so I tweaked it as follows:

let defaults = UserDefaults.standard
//set
defaults.set("test", forKey: "ArrayKey")
defaults.synchronize()
//get
if let temp = defaults.string(forKey: "ArrayKey")  {
    print(temp)
}
print("done")

I then created a new test project from the Single View App template and added that code to the

viewDidLoad
method. As you’d expect, it prints:
test
done

In my experience there are some wacky edge cases where

UserDefaults
will lose data but the vast majority of problems like this are related to folks not using it correctly. For example, the
set(_:forKey:)
method takes an optional value for the first parameter, which deletes the user default, which makes it easy to delete the default by accident.

To track down these problems you need to track down all the code that sets the default and verify that it’s working as expected. I typically do that by centralising that code into my own ‘preferences’ type. I can then add logging and debugging code to that type as necessary.

This centralisation isn’t a bad idea anyway. The standard defaults API is stringly typed, which makes it easy to accidentally use the wrong string and get confused. Rather than define a bunch of symbolic constants for your defaults keys, just encode them within your preferences type.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hi Quinn,


I am using two navigation controllers connected by SWREvealViewController. One navigation controller is connected to MainViewController and second navigation controller is connected to SettingViewController. In SettingViewController when user types in the information and cleck on save that's where I save my information using UserDefaults. When I navigate back to Settings ViewDidLoad() is called but there is no stored information in UserDefaults. If I try to that information in MainViewController it's there.


I don't understand how is it happening. I am also new in iOS Development.


I know I am using two navigation controller which means there are two navigation stacks. If I am storing my information in one navigation stack, it should persist, right?


Please help me!


Thanks,

Marc

I’m presuming that all of the entities you reference are in the same process. If not, let me know ’cause that’s a whole different kettle of fish.

I don't understand how is it happening.

I can’t explain that based on the evidence you have presented so far, which is why I recommend that you centralise the work to get and set these preferences. You can then instrument the getters and setters to see what’s going on. And by “instrument” I mean either:

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

So, instead of using userDefaults, I ended up using NSKeyedArchiver and stored my information in a directory. It worked 🙂


Thanks,

Marc

For Swift 5

You can use DefaultsKit in order to store key - value pairs.

First Create a key for specific value


import DefaultsKit

extension DefaultsKey {

    static let soundKey = Key("userKey")

}

Call & Set it from anywhere

    public var user:User? {

        get {

            if Defaults.shared.has(.userKey) {

                return Defaults.shared.get(for: .userKey)

            } else {

                return nil

            }

        }

        set {

            if let val = newValue {

                Defaults.shared.set(val, for: .userKey)

            } else {

                Defaults.shared.clear(.userKey)

            }

        }

    }