How to retrieve a value from dictionary in Swift 3 - swift

I have this function that fetch users from FireBase and convert them in Dictionary:
let leaderBoardDB = FIRDatabase.database().reference().child("scores1").queryOrderedByValue().queryLimited(toLast: 5)
leaderBoardDB.observe( .value, with: { (snapshot) in
print("scores scores", snapshot)
if let dictionary = snapshot.value as? [String: Any] {
for playa in dictionary {
let player = Player()
print("plaaaaayyyyyaaaaa", playa)
print("plaaaaayyyyyaaaaa key", playa.key)
print("plaaaaayyyyyaaaaa.value", playa.value)
player.id = playa.key
print(playa.key["name"])
}
}
}, withCancel: nil)
}
and I get this result:
plaaaaayyyyyaaaaa ("inovoID", {
name = Tatiana;
points = 6; }) plaaaaayyyyyaaaaa key inovoID plaaaaayyyyyaaaaa.value {
name = Tatiana;
points = 6; } aaaa i id Optional("inovoID")
the problem is that i can't obtain the name and the points of the user. when i try it with:
print(playa.key["name"])
it gaves me this error:
Cannot subscript a value of type 'String' with an index of type 'String'
can anyone help me with this, please?

Since your JSON is
"inovoID" : { "name" : "Tatiana", "points" : 6 }
playa.key is "inovoID"
playa.value is { "name" : "Tatiana", "points" : 6 }
The key is String and cannot be subscripted. That's what the error says.
You need to subscribe the value and safely cast the type to help the compiler.
if let person = playa.value as? [String:Any] {
print(person["name"] as! String)
}

I think you're looking for:
player.name = dictionary["name"] as? String
You don't need to iterate through the dictionary to access it's values. If you're looking for the value of a key, just get it.

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()
}
}
}

How to get Data from AnyHashable type of Dictionary?

how to get all userid from this type of dictionary
Optional([AnyHashable("contact_unique_id"): 235567, AnyHashable("full_name"): A. Erd insonaa, AnyHashable("userid"): 15019, AnyHashable("company_name"): pord, Derek & Co., mmd (Asset Management), AnyHashable("Client_Phone"): +61 434367243434331676213266661, AnyHashable("email_address"): rana.kumari#ting.com])
Optional([AnyHashable("contact_unique_id"): 2353223567, AnyHashable("full_name"): A. Edward radadoi, AnyHashable("userid"): 15019, AnyHashable("company_name"): Chod, Gameal & Co., Tom (Asset Management), AnyHashable("Client_Phone"): +61 6778989876213266661, AnyHashable("email_address"): ramul#rose.com])
I have Tried the following Code
let contactDictionary = Utils.getSharedManagerObject().arraySelectAssignees[0] as? [AnyHashable : Any]
print(contactDictionary)
let contactName = contactDictionary!["full_name"] as! String
print(contactName)
for id in contactDictionary! {
print(id["userid"]) as! Int
}
But it give this type of error
Value of tuple type '(key: AnyHashable, value: Any)' has no member 'subscript'
let contactDictionary = Utils.getSharedManagerObject().arraySelectAssignees[0] as? [String : Any]
print(contactDictionary)
let contactName = contactDictionary!["full_name"] as! String
print(contactName)
for id in contactDictionary! {
print(id["userid"]) as! Int
}
contactDictionary is not an array type, it's simple key-value, so can't have subscript.
If you wish to use for loop, you can get the value of userID using
for id in contactDictionary {
if id.key == "userid" {
print(id.value)
break
}
}
More simple approach would be to directly get the value using
print(contactDictionary!["userid"] as! Int)

Get child of dictionary with unknown parent key

I have a dictionary from type [String: Any] that looks like this
"arExtensions" : {
"images" : {
"-LmgO2yG_TWbfOM4Y8X3" : {
"imagePath" : "https://firebasestorage.googleapis.com/v0/b/gpk-citycards.appspot.com/o/ARResources%2F78E88F6D-52F0-43A3-B585-9760D19F0B81?alt=media&token=be3a664f-a94b-4ead-bea4-1197155c016e",
"position" : "bottom"
},
"-LmgO4qaMKupHZIAEoLk" : {
"imagePath" : "https://firebasestorage.googleapis.com/v0/b/gpk-citycards.appspot.com/o/ARResources%2FC7303CF9-0E86-4DC6-A5F5-2761537F0A30?alt=media&token=1f928774-8221-474b-881e-7f395e439131",
"position" : "rightMiddle"
},
"-LmgO9vLT8rEx9Ndog4S" : {
"imagePath" : "https://firebasestorage.googleapis.com/v0/b/gpk-citycards.appspot.com/o/ARResources%2F9132B5B6-E904-4BCE-B56A-0271CB901A7D?alt=media&token=bd66cd1d-494b-4a82-8d74-6e00ac8c8ae6",
"position" : "leftMiddle"
}
}
}
I want to add the images into an object.
var imageObjects: [ImageObject] = []
I know that I can get the values with the keys like this
let dictImages: [String: Any] = dictArExtensions["images"] as! [String : Any]
unfortunately I don't know the key of the children of images.
with two iteration, first to get the key, second to get the needed values i solved the problem like this
var imageKeys: [String] = []
for dict in dictImages{
print(dict.key)
imageKeys.append(dict.key)
print(dict.value)
}
for keys in imageKeys{
let dictImage: [String: Any] = dictImages[keys] as! [String : Any]
let imagePath = dictImage["imagePath"] as? String
let position = dictImage["position"] as? String
imageObjects.append(ImageObject(imagePath: imagePath!, position: position!))
}
but, it seems like a bad solution.
Is there a better or rather more professional solution to this?
You can enumerate the dictionary very simply with key and value but the key is actually unused.
And if you declare the child dictionary as [String:String] you can get rid of the type cast
for (_, value) in dictImages {
let dictImage = value as! [String:String]
let imagePath = dictImage["imagePath"]
let position = dictImage["position"]
imageObjects.append(ImageObject(imagePath: imagePath!, position: position!))
}
Please read the section about dictionaries in the Language Guide

Swift child is returning Null when nested or ObserveSingleEvent, returns fine when not nested

I am trying to retrieve a child of a child. the entire snapshotValue returns null. When I retrieve the same data as a child (not nested) it retrieves fine.
I'm Using XCode 10 and Swift 4
To troubleshooting purposes, I have two nodes called 'Promoters'. One at the root and one nested inside a 'Partners' child (preferred). I will remove the top level node when I get the nested node working.
Here is the data structure:
"Partners" : {
"Acts" : [hidden],
"Promoters" : [ null, {
"Cell" : hidden,
"Contact Name" : “hidden”,
"Email" : “hidden”,
"Facebook" : “hidden“,
"Title" : "CHORD Productions"
} ]
},
"Promoters" : {
"chord" : {
"Title" : "Chord Productions"
}
}
This retrieves the data I'm looking for (a list of Titles to populate a picker):
let promotersDB = Database.database().reference().child("Promoters")
promotersDB.observe(.childAdded) { (snapshot) in
let snapshotValue = snapshot.value as! Dictionary<String, String>
let promoterName = snapshotValue["Title"]!
let promoter = PromoterClass()
promoter.promoterName = promoterName
self.promoterArray.append(promoter)
let isSuccess = true
completion(isSuccess)
}
This returns nil:
let promotersDB = Database.database().reference().child("Partners").child("Promoters")
promotersDB.observe(.childAdded) { (snapshot) in
let snapshotValue = snapshot.value as! Dictionary<String, String>
let promoterName = snapshotValue["Title"]!
let promoter = PromoterClass()
promoter.promoterName = promoterName
self.promoterArray.append(promoter)
let isSuccess = true
completion(isSuccess)
}
I'd prefer observeSingleEvent, but this also returns nil:
let promotersDB = Database.database().reference().child("Promoters")
promotersDB.observeSingleEvent(of: .value, with: { (snapshot) in
let snapshotValue = snapshot.value as! Dictionary<String, String>
let promoterName = snapshotValue["Title"]!
let promoter = PromoterClass()
promoter.promoterName = promoterName
self.promoterArray.append(promoter)
let isSuccess = true
completion(isSuccess)
})
The error is:
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
**I am using force unwrapping for now to be reviewed at a later date after investigating how much of the data integrity can be done with backend rules :)
Any assistance would be greatly appreciated.
I think firebase usually suggests the Fan Out method when dealing with data trees, so you shouldn't nest array like "Promoters" anyways.
You'll also want to identify each of the Promoters by a uid instead of by their name (incase you have to change their name in the system).
So if you want, try to restructure your data like this:
let uid = Database.database().reference().child("Promoters").childByAutoId()
// uid will be some super long string of random letters and numbers, for example like: 1hjK2SCRV2fhCI0Vv3plGMct3mL2
"Promoters" : {
"1hjK2SCRV2fhCI0Vv3plGMct3mL2" : {
"Title" : "Chord Productions"
}
}
And then when you want to query values within that promoter's branch:
let titleRef = Database.database().reference().child("Promoters").child(uid)
titleRef.observeSingleEvent(of: .value) { (snapshot) in
if let dict = snapshot.value as? [String: AnyObject] {
let promoter = PromoterClass()
promoterName = dict["Title"] as? String
promoter.promoterName = promoterName
}
}

For Loop Swift: Cast from to unrelated type always fails

I have an error that says Cast from '[String : AnyObject]' to unrelated type '[[String : AnyObject]]' always fails
for dict in json as! [[String: AnyObject]] {
let VideoCategory = videoCategory()
VideoCategory.setValuesForKeys(dict)
videoCategories.append(VideoCategory)
}
I tried wrapping it in an if statement, still it shows an error
What am I doing wrong?
here's the JSON feed
{
"action":"videos",
"result": {
"count":2,"videos":
[
{
"id":"427","asset_id":"279804413","asset_title":"title_1"
}
{
"id":"428","asset_id":"279804413","asset_title":"title_2"
}
]
}
I think I got it right. I declared it as a string object first. Is
this a good approach?
let videos = json["result"] as! [String:AnyObject]
for dict in videos["videos"] as! [[String: AnyObject]] {
let VideoCategory = videoCategory()
VideoCategory.setValuesForKeys(dict)
videoCategories.append(VideoCategory)
}