Assigning a value to an optional variable - swift

To set a dictionary value, I am doing like below.
var dic: [String:Any]?
dic = [String:Any]()
dic!["name"] = "my name"
dic!["email"] = "aaa#bbb.com"
dic!["nickname"] = "Grrrrrrrr"
I think the exclamation mark (!) is superfluous.
That is, I'd like to set without '!'.
dic = [String:Any]()
dic["name"] = "my name"
dic["email"] = "aaa#bbb.com"
dic["nickname"] = "Grrrrrrrr"
Is there any way to use in short to set a value to optional variable?

Having read the comments, the solution below could be what you are looking for.
var dic = [String: Any]() //Empty dictionary
func processResultOfAlamofire(_ result: [String: Any]?) {
dic = result ?? [String: Any]()
}
If the result of Alamo is nil, dicwill be empty instead of nil. The ?? operator could be written as:
if result == nil {
dic = [String: Any]()
} else {
dic = result!
}

I think that you should update your code, Don't use direct dictionary in your viewcontroller. set optional variable in your model. and when ever you use those values use it with conditional checking like, if let value = somethingOptional{}
or use guard in your API call function so it will never update or effect any view if you are getting values null from API

Related

Type 'Any' has no subscript members when accessing dictionary in UserDefaults

I am trying to access the values of a dictionary which I passed into UserDefaults, however I get this error:
Type 'Any' has no subscript members.
I have looked around on StackOverflow for answers but everything I tried is failing.
This is the Dictionary object that I stored in UserDefaults and I am trying to access Authorities.levelAcess:
{
accountEnabled = 0;
accountLocked = 0;
Authorities = {
levelAccess = "tier2";
accessType = "3rd party";
};
message = "Logged in Successfully";
profile = "trust";
roles = (
{
authority = "admin";
}
);
status = 1;
}
This the code for UserDefaults:
let keyDict: Dictionary<String, AnyObject> = json
UserDefaults.standard.set(json, forKey: "dict")
This is how I am trying to access the dictionary:
The reason of showing this error in your code is: the compiler cannot recognize result as dictionary, if you tried to option and click on result you would see that its type is Any. You have to cast it first as [String: AnyObject] and then get "Authorities" form it.
It should be like this:
if let result = UserDefaults.standard.dictionary(forKey: "dict") {
print(result)
}
That's how you should "optional binding" the dictionary, thus (assumeing that "Authorities" is a [String: String]):
if let addresses = result["Authorities"] as? [String: String] {
print(addresses)
}
You could also do it as one step:
if let result = UserDefaults.standard.dictionary(forKey: "dict"),
let addresses = result["Authorities"] as? [String: String] {
print(result)
print(addresses)
let levelAccess = addresses["levelAccess"]
print(levelAccess) // optional
}
Finally, you could get the levelAcess from addresses as:
let levelAccess = addresses["levelAccess"]
Again, note that levelAccess would be an optional string (String?), which means you should also handle it.
For fetching a dictionary from UserDefaults, use
dictionary(forKey: key)
In your case:
var result = UserDefaults.standard.dictionary(forKey: "dict")

Check for nil in dictionary

I have a class in my app, where the user inputs values and i set them to an instance of the class, then i upload this data to Database, but i have to convert the class to something the database accepts and i'm converting to dictionary using Mirror Reflection. Some properties in my class can be nil, because by design not all properties are required. But i can't pass nil values to the database.
I have recreated my example is very simplified playground. i didn't set a value for the name property of the class
I tried to check for nil before adding the key, value pair to the dictionary
Below is my code
import UIKit
class Color: NSObject {
var name: String?
var code: Int?
var shade: String?
}
let cl = Color()
cl.code = 3456
cl.shade = "DARK"
var colorDict = [String: Any]()
for x in Mirror(reflecting: cl).children.makeIterator() {
if let val = x.value as Any? {
print(type(of: val))
colorDict[x.label!] = val
}
}
print (colorDict)
the output in console is as below
Optional<String>
Optional<Int>
Optional<String>
["name": nil, "code": Optional(3456), "shade": Optional("DARK")]
how can i check for nil values and skip adding that property to the Dictionary
i have tried to loop through the dictionary after i add all values including nils and check for values too but i get the below warning
Comparing non-optional value of type 'Any' to nil always returns false
declaring the dictionary as below
var colorDict = [String: Any?]()
for x in colorDict {
if x.value == nil {
colorDict.removeValue(forKey: x.key)
}
}
removes the warning but it doesn't remove anything.
I would really appreciate your help.
The way of unwrapping objects of type Any that contain optionals is kind of weird but you can check that the values aren't nil in your mirror like this:
for x in Mirror(reflecting: cl).children {
if case Optional<Any>.some(let val) = x.value {
print(type(of: val))
colorDict[x.label!] = val
}
}
You can do this really easily in a one-liner, using filter:
let dict: [String : Any?] = ["Foo" : 3, "Bar" : nil, "Baz" : "Qux"]
let noNils = dict.filter { $0.value != nil }
print(noNils) // prints ["Foo": Optional(3), "Baz": Optional("Qux")]
As i have suggested, initialise all values.
If you decide not to store the nil values you will end up with children that some of them will have 1, some 2 and some 3 nodes, nothing wrong with that, BUT what happens when you go to read them?
You havent shared any info as to how these values will be used by the app, but assuming you have one function to read the properties/nodes of stored colors, it will go to read all 3 :
ref.child("colors").child("someSpecificColor").observeSingleEvent(of: .value, with: { (snapshot) in
// Get color values
let value = snapshot.value as? NSDictionary
let name = value?["name"] as? String ?? ""
let code = value?["code"] as? String ?? ""
let shade = value?["shade"] as? String ?? ""
// ...
}) { (error) in
print(error.localizedDescription)
}
See the issue?
Here is a simple solution :
var colorDict = [String: Any?]()
for x in Mirror(reflecting: cl).children.makeIterator() {
if let val = x.value, val != nil {
print(type(of: val))
colorDict[x.label!] = val
}
}
Here before to print and add you val, you check if the val is different than nil. As your output suggests in your console log you print :
Optional<String>
Optional<Int>
Optional<String>
val is an optional. So, if it's nil it won't be added. If not, you enter into the if statement and that's it.

Swift Optional Dictionary [String: String?] unwrapping error

So here I have a basic setup
var preferenceSpecification = [String : String?]()
preferenceSpecification["Key"] = "Some Key"
preferenceSpecification["Some Key"] = nil
preferenceSpecification["DefaultValue"] = "Some DefaultValue"
print(preferenceSpecification)
var defaultsToRegister = [String : String]()
if let key = preferenceSpecification["Key"], let defaultValueKey = preferenceSpecification["DefaultValue"] {
defaultsToRegister[key] = preferenceSpecification[defaultValueKey]!
}
But the error points out where it demands that I force unwrap this, to be like this:
defaultsToRegister[key!] = preferenceSpecification[defaultValueKey!]!
Which doesn't make sense, because keyValue and defaultValue already are unwrapped
When you extract a value from a dictionary like this using subscript
[String: String?]
you need to manage 2 levels of optional. The first one because the subscript returns an optional. The second one because the value of you dictionary is an optional String.
So when you write
if let value = preferenceSpecification["someKey"] {
}
you get value defined as an optional String.
Here's the code to fix that
if let
optionalKey = preferenceSpecification["Key"],
key = optionalKey,
optionalDefaultValueKey = preferenceSpecification["DefaultValue"],
defaultValueKey = optionalDefaultValueKey,
value = preferenceSpecification[defaultValueKey] {
defaultsToRegister[key] = value
}
Suggestions
You should avoid force unwrapping as much as possible. Instead you managed to put 3 ! on a single line!
You should also try to use better name for your constants and variables.
You could also define an extension which helps get rid of the double optional situation.
extension Dictionary where Value == Optional<String> {
func flattened(_ key: Key) -> Value {
if let value = self[key] {
return value
}
return nil
}
}
Usage: preferenceSpecification.flattened("someKey")

Updating a nested value in an NSDictionary

I've initialized a dictionary of type [NSObject: AnyObject] so I can save it into NSUserDefaults.
Here's what it looks like:
var allMetadata: [NSObject: AnyObject] = [
String: [String: String]
// Example: "project30": ["deliverablepath": "hello"]
]
I give deliverablepath a value from the very beginning, and later on I want to update it. I've tried this:
allMetadata[arrayOfProjectIDs[index]]!["deliverablepath"]! = "goodbye"
But I get the error
Operand of postfix '!' should have optional type; type is '(NSObject,
AnyObject)'
I know about updateValue(), but it seems to overwrite adjacent keys in the first nested layer, so it's not working for me.
Any ideas?
Use question optional to avoid "let pyramid"
var allMetadata: [String: [String: String]] = ["a": ["b": "c"]]
allMetadata["a"]?["b"] = "z" // ok!
allMetadata["q"]?["b"] = "d" // nil
UPD:
If you want to cast directly, you should try this:
var allMetadata: [NSObject: AnyObject] = ["a": ["b": "c"]]
if var dict = allMetadata["a"] as? [String: String] {
dict["b"] = "z"
// for dict update, because it's value typed
allMetadata["a"] = dict
}
Mention, that I've written "var", not "let" in condition.
To do this in a safe way, it is best to do this in an if let pyramid as follows:
if let projectId = arrayOfProjectIDs[index] {
if var project = allMetadata[projectId] as? [String:String] {
project["deliverablePath"] = "Goodbye"
}
}
That is not too bad actually.
I want to give an alternative answer here.
I understand the original question is about how to deal with nested arrays and dictionaries, but I think it is worth mentioning that this kind of data model may be better implemented with a more formal API.
For example, how about this:
class Project {
var id: String
var deliverablePath: String
... etc ...
}
class ProjectRepository {
func getProjectWithId(id: String) -> Project? {
...
}
}
Then you can use high level code like:
if let project = repository.getProjectWithId("") {
project.deliverablePath = "Goodbye"
}
Underneath you can still implement this with dictionaries and arrays of course.

'AnyObject?' does not have a member named 'count'

I'm trying to loop through an (as I would interpret) array by arrayname.count like:
if var storedToDoItems: AnyObject? = NSUserDefaults.standardUserDefaults().objectForKey("toDoItems") {
toDoItems = []
for var i = 0; storedToDoItems.count; i++ {
toDoItems.append(storedToDoItems[i] as NSString)
}
}
and receive the error in the title marked by storedToDoItems.count. I'm populating first storedToDoItems like this:
...
toDoItems.append(tb_toDoItem.text)
let fixedToDoItems = toDoItems
// Store items
NSUserDefaults.standardUserDefaults().setObject(fixedToDoItems, forKey: "toDoItems")
// Save the stored stuff
NSUserDefaults.standardUserDefaults().synchronize()
...
If you store an array to the user defaults, you should try to cast it to an array when you extract it.
If toDoItems is defined as an array of strings [String], then you just have to use optional binding combined with optional cast, and just copy the extracted array to toDoItems:
if let storedToDoItems: [String] = NSUserDefaults.standardUserDefaults().objectForKey("toDoItems") as? [String] {
toDoItems = storedToDoItems
}
Being an array a struct, i.e. a value type, it is assigned by copy, so when assign storedToDoItems to toDoItems, a copy of it is created and assigned - so you don't need to manually add each element individually.
Your code instead has 2 errors:
you have defined storedToDoItems as an optional AnyObject, and optionals don't have a count property.
even if it is not defined as optional, AnyObject doesn't have a count property as well. It's irrelevant that the actual type stored in the variable is an array, the variable is declared as AnyObject and that is how is treated by the compiler.
You need to tell what's inside:
toDoItems = [String]()
if let storedToDoItems = NSUserDefaults.standardUserDefaults().objectForKey("toDoItems") as? [String] {
for item in storedToDoItems {
toDoItems.append(item)
}
}
and even shorter:
toDoItems = [String]()
if let storedToDoItems = NSUserDefaults.standardUserDefaults().objectForKey("toDoItems") as? [String] {
toDoItems = storedToDoItems
}
I just used this Bold line will work for you hopefully
if var toDoStored: [String] = NSUserDefaults.standardUserDefaults().objectForKey("toDoSaved") as? [String]{
tblItems = []
for var i = 0; i < toDoStored.count ; ++i {
tblItems.append(toDoStored[i] as NSString)
}
}