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

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")

Related

How to get key and value of Firestore mapped object

I have an app where users can rate films they have watched, and I want to be able to put these in a tableView. The rating is stored in Firestore, and I want to put both the KEY and value into a Struct so I can access it for the tableView.
However any site/tutorial/stack question I have seen only gets the Maps value, but not the key (in this case, the title name). I can access the value, but only by using the field key, but that is what I am trying to get (see attempt 1)
Struct:
struct Rating: Codable {
var ratedTitle: String
var ratedRating: Int
}
Variable:
var ratedList = [Rating]()
Load data function (attempt 1):
let dbRef = db.collection("Users").document(userID)
dbRef.getDocument { document, error in
if let error = error {
print("There was an error \(error.localizedDescription)")
} else {
if let docData = document!.data() {
let titleRating = docData["Title Ratings"] as? [String: Int]
let midnightMass = titleRating!["Midnight Mass"]
print("Rating given to Midnight Mass: \(midnightMass!) stars")
}
}
}
//Prints: Rating given to Midnight Mass: 2 stars
Also tried (but I don't know how to get this array onto a tableView and have the first index as the Title label, and the second index a Rating label for each movie in the array) attempt 2:
if let docData = document!.data() {
let titleRating = docData["Title Ratings"] as? [String: Int]
self.userRatedList = titleRating!
print("userRatedList: \(self.userRatedList)")
}
//Prints: userRatedList: ["Midnight Mass": 2, "Bly Manor": 5]
Attempt 3:
if let docData = document!.data() {
let titleRating = docData["Title Ratings"] as? [String: Int]
self.ratedList = [Rating(ratedTitle: <#T##String#>, ratedRating: <#T##Int#>)]
//Don't know what I would put as the ratedTitle String or ratedRating Int.
self.ratedList = [Rating(ratedTitle: titleRating!.keys, ratedRating: titleRating!.values)]
//Cannot convert value of type 'Dictionary<String, Int>.Keys' to expected argument type 'String'
//Cannot convert value of type 'Dictionary<String, Int>.Values' to expected argument type 'Int'
}
Firstly, I am not sure why you need the struct to conform to Codable?
Now, based off what I see, "Title Ratings" is a dictionary with a String key and an Int value. You are overcomplicating this. If you want to access the key and value of each element individually, use a for-in loop.
//Declare your global variable
var ratedList = [Rating]()
//If you are using an if let, there is not need to force unwrap
if let docData = document.data() {
if let userRatingList = docData["Title Ratings"] as? [String: Int] {
for (key, value) in userRatingList {
let rating = Rating(ratedTitle: key, ratedRating: value)
ratedList.append(rating)
}
//reload your tableView on the main thread
DispatchQueue.main.async {
tableView.reloadData()
}
}
}

Assigning a value to an optional variable

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

How to access nested dictionary?

I can print this in the debugger:
(lldb) print params["message"]!
([String : String]) $R5 = 2 key/value pairs {
[0] = (key = "body", value = "iPadUser has started a new stream")
[1] = (key = "title", value = "Stream started")
}
But I am trying to figure out how to access the body and title separately.
I construct params in this way:
let recipients = ["custom_ids":[recips]]
let notificationDetails = "hello there"
let content = [
"title":title,
"body":details
]
let params: [String:Any] = [
"group_id":"stream_requested",
"recipients": recipients,
"message": content
]
print((params["message"] as! [String: Any])["title"] as! String)
You need to cast the Dictionary value as specific type, since the compiler doesn't know what to expect. (Please mind that you mustn't use force unwrap in other way than example code.)
Considering you need to fetch array values when recipients dictionary looks like this:
let recipients = ["custom_ids":["recipe1", "recipe2", "etc"]]
get to the ids like this:
guard let recipients = params["recipients"] as? [String: Any],
let customIDs = recipients["custom_ids"] as? [String]
else { return }
for id in customIDs {
print(id) // Gets your String value
}

Swift Array to Set : Showing Recent Messages Command failed due to signal: Segmentation fault: 11

I am using firebase to retrieve a list of data then convert it to an NSDictonary array. I want to parse the data by a property e.g name
func getAllMyModels() {
if let e = email {
_ = ref.child("childName").queryOrdered(byChild: "email").queryEqual(toValue: e).observe(.value) { snapshot in
var dictionary = [NSDictionary]()
let children = snapshot.children
while let rest = children.nextObject() as? DataSnapshot, let value = rest.value {
dictionary.append(NSDictionary(dictionary: value as! [String: Any]))
}
let names = dictionary.flatMap {$0["name"]} // names print correct values
let set = Set(names)
print(set)
}
}
}
This code can't be complied the error is:
Showing Recent Messages
Command failed due to signal: Segmentation fault: 11
If i removed this line:
let set = Set(Array(names))
all works fine.
I also tested by replace it by this block
let ar = ["name1","name2"].flatMap { return $0 }
Set(ar)
No errors.
Not sure why? Who can tell, thanks!
EDIT: Even though the element in the array is String type but the names array is [Any], so the solution is
let names = dictionary.flatMap {$0["name"]} as! [String]
I think this errors occurs because the Array you generate from the dictionary with flatMap is an Array of Any and not a String Array, try to cast to a String like this:
...
let names = dictionary.flatMap {$0["name"] as? String}
let set = Set(Array(names))
...
Hope this help you
Cast names to [String]:
let names = dictionary.flatMap {$0["name"]} as! [String]

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")