Unable to retrieve dictionary data from Userdefaults in Swift 3 - ios

Do you guys can bring some light on the use of dictionaries with UserDefaults ?
What I want to get is to pass a certain dictionary from a view controller to another one using UserDefaults.
var dict = [String : AnyObject?]()
dict = ["Name": "Henri", "Group": "A", "Category": "4"]
UserDefaults.standard.set.(dict, forKey: "MyDict")
Then I try to retrieve the data
let retrieveDict = UserDefaults.standard.dictionary(forKey:"MyDict")
I got a nice nil answer in the console. When I try with int or float types it is fine... Is there anything I am missing to make this dictionary ?

First of all, your code won't compile.
Second, a dictionary with type [String, AnyObject?] make no sense because you'll be not able to set nil in UserDefaults. You should use empty String maybe for that purpose.
The working code could look like that :
var dict = [String : Any]() // or maybe [String : String] if you sure about value type
dict = ["Name": "Henri", "Group": "A", "Category": "4"]
UserDefaults.standard.set(dict, forKey: "MyDict")
let retrieveDict = UserDefaults.standard.dictionary(forKey:"MyDict")
Hope it helps you :)

Related

Getting error while trying to store Int, arrayof [string] pair in userDefaults

self.fetchMin(forStartDate: start, toEndDate: end) { (min, chal) in
guard let mins = min, let challenges = chal else {
return
}
let dict: [Int : [String]] = [mins:challenges]
UserDefaults.standard.set(dict, forKey: "WeekStates")
}
Hi, In the above program I'm trying to store key and array pair of string in userDefaults but if I do so it crashes unexpectedly.
If I try with one value, its working.
eg:
`let dict: [String] = challenges
UserDefaults.standard.set(dict, forKey: "WeekStates")
https://developer.apple.com/documentation/foundation/userdefaults
The UserDefaults class provides convenience methods for accessing
common types such as floats, doubles, integers, Booleans, and URLs. A
default object must be a property list—that is, an instance of (or for
collections, a combination of instances of): NSData , NSString ,
NSNumber , NSDate , NSArray , or NSDictionary . If you want to
store any other type of object, you should typically archive it to
create an instance of NSData. For more details, see Preferences and
Settings Programming Guide.
You can do something like this.
let dict: [Int : [String]] = [1:["iOS Dev"]]
UserDefaults.standard.set(NSKeyedArchiver.archivedData(withRootObject: dict as NSDictionary) as NSData, forKey: "4WeekStates")
if let unarchivedObject = UserDefaults.standard.object(forKey: "4WeekStates") as? Data {
let dic = NSKeyedUnarchiver.unarchiveObject(with: unarchivedObject as Data) as? NSDictionary
print(dic!)
}
As you mentioned at last point i tried [String] it works.
I'm not sure whether you can store data like type of [Int : [String]], As of my knowledge UserDefaults Property lists can only accept certain types of variables, so it has that limitation. You can store these types of variables:
Array
Data
Date
Dictionary
NSNumber
String

Dictionary Contain NSNull Swift

Is there any way to detect that an NSDictionary contains a NSNull value?
Be careful when you put NSDictionary and Swift in the same sentence. NSDictionary is a Foundation (i.e. Objective-C) reference type. Swift has its own native Dictionary, which is a value type (i.e. struct). You can bridge between the two easily, but it takes some CPU time doing so.
Now on to your question:
let dict: [String: Any] = [
"firstName": "John",
"lastName": "Smith",
"age": NSNull()
]
let containsNSNull = dict.contains { $0.1 is NSNull } // true

Optional Values in NSMutableDictionary

I'm new to Swift, very experienced in Objective-C.
In my app, I am receiving data from a server and mapping it to an NSMutableDictionary. The reason I am using an NSMutableDictionary is that the values are not consistent coming from the server, it's a mix of strings and numbers. And that appears to break a Swift Dictionary that expects only one type of value.
Sometimes the server is not sending a value that the NSMutableDictionary is expecting and it crashes the app.
In my research, it appears that I have to check every object to see if the value exists in Swift before setting it into the NSMutableDictionary.
This is my current code:
let userDictionary:NSMutableDictionary = [
"name": data.objectForKey("name") as! String,
... // many more values
This crashes if there is no "name" value in the response from the server.
It appears the solution would be:
if let nameValue = data.objectForKey("name") as! String {
//not sure what to do in here since the var is assigned as I need
}
// ... check many more values
let userDictionary:NSMutableDictionary = [
"name": nameValue,
// ... assign many more values that I checked above
This seems like a lot of extra code to check every single value from the server. Is there a simpler solution?
Thank you for your time.
#Matt below. Here is the code in detail (took out some of the values in the userDictionary for brevity). I'm taking data from Facebook, adding additional info and saving it to Firebase:
//check all of the values
var birthdayValue:String? = "";
if let value:String? = data.objectForKey("birthday") as? String {
birthdayValue = value;
}
let userDictionary:NSMutableDictionary = [
"name": data.objectForKey("name") as! String,
"birthday": birthdayValue!,
"email": data.objectForKey("email") as! String,
"firstName": data.objectForKey("first_name") as! String,
"lastName": data.objectForKey("last_name") as! String,
"description": "",
"distance": 50,
"facebookID": data.objectForKey("id") as! String,
"location":[ 37.12314, -122.49182 ], //TODO: Get location
"points" : 0,
"rating" : 1,
"school" : "",
]
//we need to convert the FB profile pic to a base 64 string and add it here
let imagePath:String = "https://graph.facebook.com/\(data.objectForKey("id") as! String)/picture?width=375&height=667"
self.getDataFromUrl(NSURL(string: imagePath)!) { (imageData, response, error) -> Void in
//convert the data to base 64
let imgString:String = self.convertDataToBase64(imageData);
let images:Array<String> = [imgString];
userDictionary.setValue(images, forKey: "profilePics")
//save the user to Firebase
userRef.childByAppendingPath(data.objectForKey("id") as! String).setValue(userDictionary)
self.currentUserID = (data.objectForKey("id")) as! String
}
Swift actually support multiple types in a dictionary. The following is legal.
let arr: [NSObject: AnyObject] = [1: "hello", "a": 19, 2: "Swift"]
And you can store optional object in the dictionary:
let arr: [NSObject: AnyObject?] = [1: "hello", "a": 19, 2: nil]
And yes, you might need to check the existence of the value if you do care about it. Instead of if, I would use guard to make sure you can use the variable later.
guard let nameValue = data.objectForKey("name") as? String else {
return
}
// Now you can safely use nameValue.
In my app, I am receiving data from a server and mapping it to an NSMutableDictionary
There is no need for this at all. The data is coming to you as a dictionary (at least I presume it is; if it weren't, you could hardly be calling objectForKey, it seems to me). So now just proceed to use it. There is nothing to "map".
For example, suppose that data is the result of calling NSJSONSerialization's JSONObjectWithData:options: on an original NSData d. And suppose what you expect this to serialize to is a dictionary. So after you've said
var data = try! NSJSONSerialization.JSONObjectWithData(
d, options:[]) as! [NSObject:AnyObject]
...you are finished: that's a mutable dictionary and you are off to the races.
If your data is a mix of strings and numbers you could perhaps try to store it as AnyObject in the dictionary, e.g. [String: AnyObject]
Then you could just try to save your data like this:
var userDictionary: [String: AnyObject] = [:]
userDictionary["name"] = data.objectForKey("name")
And when you need to access it:
if let name = userDictionary["name"] as? String {
// Do something with name
}
If you don't want to save it as AnyObject I'd suggest you create a struct instead.
as! is a force downcast, which causes an exception if this downcast isn't possible (which obviously is the case if the value is nil). as?, on the other hand, only downcasts where possible, and results in nil otherwise (which sounds like what you want).
let userDictionary:NSMutableDictionary = [
"name": data.objectForKey("name") as? String,
... // many more values
]
This should work.
Edit: Never mind, this would only work if you'd use a native Swift dictionary. It's still useful to know about the difference between as? and as!, though.

Update multiple labels with values from a dictionary?

I'm trying to make an app that will select an index at random, something like 0-1000 and then print the key, value, and link of the selected number to three seperate labels on the iPhone simulator.
So from my example, I want to randomly select "0" or "1" and if for instance "1" was chosen; then the key, value, and link information would each be printed to three separate labels on the simulator. The following is what I've been working on in playgrounds. Is there a better way to go about this?
var spDictionary: [String: [String:String]] = [
"0": ["key": "AMZN", "value": "AMAZON", "link": "yahoo"],
"1": ["key": "AAPL", "value": "APPLE", "link": "yahoo2"],
]
And for the random aspect I think it would be something like this but I'm not sure? Sorry for the newbie question.
let randomIndex: Int = Int(arc4random_uniform(UInt32(spDictionary.count)))`
Even for small data structures it's worth it to create a custom class or struct
struct Data {
let key : String
let value : String
let link : String
}
Create an object
let data = Data(key: "AMZN", value: "AMAZON", link: "yahoo")
Get a property
let link = data.link
and you can declare your dictionary
var spDictionary : [String: Data] = ...

Swift add object in to AnyObject

I need add an object into AnyObject
let swiftArray: [String: AnyObject] = ["one": "asd", "two": "asd", "three": "asd"]
How to add more objects?
Like,
let swiftArray: [String: AnyObject] = ["one": "asd", "two": "asd", "three": "asd"]
print(swiftArray)
swiftArray.append("test": "test") // NOT WORKING
Swift syntax is rather confusing. You are actually trying to create a Dictionary instead.
To use an Array:
var swiftArray: [String] = ["one", "two", "three"]
swiftArray.append("test")
--> Notice that you must use var instead of let for the Array to be mutable.
To use a Dictionary:
var swiftDict: [String: String] = ["one": "value"]
swiftDict["newElement"] = "newValue"
Please throughly read the Swift 2.1 documentation here.
You should probably read up on how swift works!!!
I'm gonna hold your hand on this one, and show you all the things that are off here.
FIRST, you're declaring your variable with a let statement, which creates Immutable variables (i.e., they can't change!). If you'd like to create a variable that can change, user var ..., so
var swiftDict: [String: AnyObject] = ["one": "asd", "two": "asd", "three": "asd"]
SECOND, what you're thinking of here is a dictionary, not an array. Arrays can be thought of as lists, or stacks of data, where each piece of data has a number, and is stacked on the last piece of data. A dictionary is more like a pool of data, where each piece has a name, or key, you call to retrieve it.
Rule of thumb, if you're got values tied together, you've got a dictionary.
THIRD, it's really easy to add to a dict, just use the following syntax.
swiftDict["four"] = "asd"
If you're still confused, try google, and feel free to ask for more info.

Resources