Difference between UserDefaults() and UserDefaults.standard - swift

Is there a difference between UserDefaults() and UserDefaults.standard in Swift 3.0?

UserDefaults - Gives you a new object, each object is allocated a different memory and deallocated when object scope is finished.
UserDefaults.standard - Gives you the singleton object by using the class method standard the object received by this method is allocated single memory throughout the application.
And the usage of them if you´re interesedted in that:
// Set
UserDefaults.standard.set("YOUR STRING", forKey: "key")
UserDefaults().set("YOUR STRING", forKey: "key")
// Get
UserDefaults.standard.string(forKey: "key")
UserDefaults().string(forKey: "key")

let ud = UserDefault()
let uds = UserDefaults.standard
Both objects will use the same underlying plist file to get/set
values.
Setting a value for a key with "ud" gives the same value
using the "uds" object.
You can create and use instances of "UserDefault" created with the convenience "init" method, but the Foundation documentation encourages a developer to use the singleton accessible by the "standard" variable.

Related

Swift Userdefaults converting String to __NSCFString

I have code that save a dictionary of [String: Any] in UserDefaults. On retrieval String are changed to __NSCFString. I am using Mixpanel to track events and sends this dictionary as events properties. Now the problem is __NSCFString is not a valid MixpanelType so Mixpanel is discarding my dictionary.
Questions:
Is there a way to get same datatypes that are saved using dictionary in UserDefaults?
Is there a way Mixpanel accepts converted datatypes?
Here is a code I am using
var mixpanelProperties: [String: Any] {
get { defaults.dictionary(forKey: "\(#function)") ?? [:] }
set { defaults.set(newValue, forKey: "\(#function)") }
}
mixpanelProperties = ["a-key": "value for the key"]
let prop = mixpanelProperties
print("Type of: \(String(describing: prop["a-key"]))")
asdad
MacOS and iOS use a variety of different classes to represent strings, which are all compatible. x as? String should just work, no matter what the concrete class of x is. Unless you use code that explicitely checks the class which you shouldn't do.

How would you use .updateValue() to add a sub-dictionary to a UserDefaults dictionary?

Is this proper syntax for this line of code? If not what would be the correct syntax and why so?
UserDefaults.standard.dictionary(forKey: "mainDict")?.updateValue(subDict, forKey: "subDictTitle")
First, you have to store Userdefault dictionary to a temporary dictionary. Then you have to add data to a temporary dictionary.
No need to update the dictionary to Userdefault. When you store Dictionary to the Usedefault with the same key, it will replace the older dictionary to the new one.
UserDefaults.standard.set(YOUR_TEMPORARY_DICTIONARY, forKey: YOUR_KEY_NAME)
The updateValue(_:forKey:) is a mutating instance method for the dictionary, which means that it updates the value of the dictionary. Obviously, In order to mutate an instance, it has to be mutable, which is not the case when calling UserDefaults.standard.dictionary(forKey: "mainDict").
Even if you did:
let myDict = ["k1": "Hello"]
UserDefaults.standard.register(defaults: ["myDict": myDict])
var mutable = UserDefaults.standard.dictionary(forKey: "myDict")!
mutable["k1"] = "HEY"
print(UserDefaults.standard.dictionary(forKey: "myDict")) // Optional(["k1": Hello])
the value of the dictionary set in the user default won't change because simply mutable is a copy of it.
To clarify, it's similar to implementing:
UserDefaults.standard.register(defaults: ["k2": "this is my string"])
UserDefaults.standard.string(forKey: "k2") = "new string"
which generates the error of
Expression is not assignable: function call returns immutable value
So, in order to resolve this issue, what you should do is to set a new value (updated dictionary) to the user defaults with the same key:
var myDict = UserDefaults.standard.dictionary(forKey: "myDict")
myDict?.updateValue("Hey", forKey: "k1")
UserDefaults.standard.set(myDict, forKey: "myDict")

Unable to access userDefaults with standard

I have trouble with UserDefaults.
I already tried userDefaults.standard but it's not working. The userDefeaults with standard shows an error:
"which is "Static member 'standard' cannot be used on instance of type 'UserDefaults'"
if user != nil {
if userDefaults.standard.object
}
Static member 'standard' cannot be used on instance of type 'UserDefaults'
Why can not accept the standard with userDefaults?
Here is how you can work with UserDefaults.
Get a reference to user defaults:
let defaults = UserDefaults.standard
Set an object for key "age":
defaults.set(31, forKey: "age")
Unwrap the value of the stored key. If it is nil (key does not exist), it will not enter the block.
if let object = defaults.object(forKey: "age") {
}

Catching exceptions when unarchiving using NSKeyedUnarchiver

We've got a Swift class which inherits from NSObject and implements NSCoding. We need to change the name of the class in code and in the archives on disk. Fortunately, we don't need to retain the data. Wiping the cached files and returning default data will be fine. The trouble is detecting when it fails and handling it appropriately.
Ok, to start with the data is written out like this (ignore liberties with force unwrapping etc. it's just to keep the question concise):
let someObject: [Int: MyClass] = ...
let data = NSKeyedArchiver.archivedData(withRootObject: someObject)
try! data.write(to: someUrl, options: .atomic)
This works fine. Our existing method of decoding is this (again, our code is safer in practice):
let someObject = NSKeyedUnarchiver.unarchiveObject(withFile: someUrl.path)! as! [Int: MyClass]
Now, when we rename our class, we are going to have to deal with the cases where it fails to decode. With NSKeyedUnarchiver, you can set a delegate which will have a method called if decoding fails. Before changing it, I wanted to re-write our existing decoder to make sure it can decode as is before I make any changes. Unfortunately, it can't. Here is what we've got:
let fileData = fileManager.contents(atPath: fileUrl.path)!
let unarchiver = NSKeyedUnarchiver(forReadingWith: fileData)
guard let myObject = try! unarchiver.decodeTopLevelObject() as? [Int: MyClass] else {
return [Int: MyClass]()
}
Now, I didn't expect any issues with this. However, the call to decodetopLevelObject() fails every time, simply returning nil (it's definitely not the cast that's the problem). I have no idea why. The documentation for this is effectively non-existent. There is a similar call which is decodeObject(), but that also fails for no obvious reason. I've tried setting the delegate and implementing the method, but it never gets called.
Where are we going wrong?
Try using decodeObject(forKey:) with NSKeyedArchiveRootObjectKey or similar API.

Thread 1: EXC_BAD INSTRUCTION with NSUserDefaults

I have it so that when my game ends, it switches to a separate SKScene which shows the new high score. Here is my code:
func saveState() {
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setInteger(highScore, forKey: "labelScore") //this line says "unexpectedly found nil while unwrapping an optional value
defaults.setInteger(stars, forKey: "SNOW")
NSUserDefaults.standardUserDefaults().synchronize()
}
What could be nil? I have values for everything in this function. Will post more code if necessary.
This means that it was probably saved wrong to begin with. I would recommend using constants for things such as accessing user defaults so that this type of thing doesn't happen.
EDIT:
What I usually do is create a separate file that is a global constants file as part of a struct
In this file you can define constants like so:
struct GlobalConstants {
static let defaultsHighScore = "labelScore"
}
Then, when I try to read from defaults, instead of typing in everything every time, I can just use the constant like this: defaults.setInteger(highScore, forKey: GlobalConstants.defaultsHighScore)