Right way to sync a variable with UserDefaults

I would like to keep a variable sync with UserDefaults directly when I set a value. I’ve done it like this:

var userId: String {
    get {
        return UserDefaults.standard.string(forKey: kUserIdKey) ?? ""
    }
    set {
        UserDefaults.standard.set(newValue, forKey: kUserIdKey)
    }
}

It is working fine but is it the right way to do it?

If so, what about Array?

var tagsList: [Int] {
    get {
        return (UserDefaults.standard.object(forKey: kTagsList) as? [Int]) ?? [Int]()
    }
    set {
        UserDefaults.standard.set(newValue, forKey: kTagsList)
    }
}

If I add an element tagsList.append(0), is the new value stored in UserDefaults?

Update.

Here is what I have in the playground:

Short answer: Yes.

Long answer: Arrays and Strings are structs in Swift, which semantically make a whole new copy when mutated.

The newValue in the setter therefore is the resulting array of whatever mutation you applied to the array, whether it being an append or other mutating functions.

You can easily verify this behavior in a playground like this:

var array: [Int] {
    get { return [1, 2, 3] }
    set { print(newValue) }
}

array.append(4)
// Prints [1, 2, 3, 4]

If I add an element tagsList.append(0), is the new value stored in UserDefaults?

YES.

At a glance, you have a setter and, as a setter, it only observes the value whenever you make a new assignment, like:

tagsList = [1,2,3] // new value.

If Array<Int> ([Int]) was class we could say that, even though you do modify the existing array by calling append, this is not an assignment, per se. However, [Int] is not a class, it’s a struct which means that any function that modifies (mutates, to be correct) existing struct, basically leads to a new assignment. This is the reason why you would see setter to trigger.