Type Any has no subscript members while adding an array to [String: Any] in Swift3 - swift

In my Swift3 code I have an array:
var eventParams =
[ "fields" :
[ "photo_url":uploadedPhotoURL,
"video_url":uploadedVideoURL
]
]
Later on I want to add another array to this array, I thought I could just do:
eventParams["fields"]["user_location"] = [
"type":"Point", "coordinates":[appDelegate.longitude, appDelegate.latitude]
]
but I'm getting error here:
Type Any? has no subscript members
How can I add that array to my previously declared array fields?

Since your dictionary is declared as [String : Any], the compiler doesn't know that the value for "fields" is actually a dictionary itself. It just knows that it's Any. One very simple way to do what you're trying is like this:
(eventParams["fields"] as? [String : Any])?["user_location"] = [
"type":"Point", "coordinates":[appDelegate.longitude, appDelegate.latitude]
]
This will just do nothing if eventParams["fields"] is nil, or if it's not actually [String : Any].
You can also do this in a couple steps to allow for troubleshooting later on like this:
//Get a reference to the "fields" dictionary, or create a new one if there's nothig there
var fields = eventParams["fields"] as? [String : Any] ?? [String : Any]()
//Add the "user_location" value to the fields dictionary
fields["user_location"] = ["type":"Point", "coordinates":[appDelegate.longitude, appDelegate.latitude]]
//Reassign the new fields dictionary with user location added
eventParams["fields"] = fields

Related

How can I save a dictionary to Firebase in swift?

I have a dictionary of type [Int: Int] that I am trying to save to firebase. My function for writing to firebase looks like this:
func writeToFirebase() {
setScoreToBadge()
setScoreToVehicle()
if GameManager.instance.getUsername() != "" {
self.ref?.child("user").child(
GameManager.instance.getUsername()
).setValue(
[
"email": GameManager.instance.getEmail(),
"userNameLowered": GameManager.instance.getUsername().lowercased(),
"userName": GameManager.instance.getUsername(),
"topScore": GameManager.instance.getTopScores()[0],
"topScores": GameManager.instance.getTopScores(),
"totalCash": GameManager.instance.getTotalCash(),
"friends": GameManager.instance.getFriendsAdded(),
"achievements": GameManager.instance.getAchievementsCompletedBool(),
"badge": GameManager.instance.getBadgeLevel(),
"scoreBadgeDictionary" : GameManager.instance.getTopScoreWithBadgeDictionary(),
"scoreVehicleDictionary" : GameManager.instance.getTopScoreWithVehicleDictionary()
])
} else {
print ("Not signed in, score not saved to firebase")
}
}
The issue I'm having is with scoreBadgeDictionary and , scoreVehiceDictionary. GameManager.instance.getTopScoreWithBadgeDictionary() is a dictionary of type [Int: Int] and GameManager.instance.getTopScoreWithVehicleDictionary() is a dictionary of type [Int: String]. If I try to run this function it will crash.
Is there a way to convert the dictionary to a format that I can save to firebase?
This issue is how the array is defined. A simple example
let myArray = ["some_key": "some_value"]
is defined as an array of [String: String]
and
let myArray = ["some_key": 5]
is defined as an array of [String: Int]
Your array is
[
"email": GameManager.instance.getEmail(),
"userNameLowered": GameManager.instance.getUsername().lowercased(),
.
.
"scoreBadgeDictionary" : ["some key": "some value"]
]
where, I assume GameManager.instance.getTopScoreWithBadgeDictionary() returns a dictionary.
So that array doesn't conform to [String: String] or [String: Int]. However it does conform to a more generic [String: Any] so that's what you need to 'tell' the var.
let myArray: [String: Any] = [
"email": "Hello",
"userNameLowered": "hello",
"scoreBadgeDictionary" : [1: "some score"],
"scoreVehicleDictionary" : [2: "some vehicle"]
]
However, and this is the important bit, the dictionaries returned in getTopScoreWithVehicleDictionary which is a [Int: String] or [Int: Int] is not going to work with Firebase.
Firebase keys must be strings so attempting to write this [1: "Hello, World] will crash. Noting that arrays in Firebase have numeric indexes and
if the data looks like an array, Firebase will render it as an array.
I would suggest rethinking that structure - possibly cast the Int's to a string would be an option but that may not work for your use case.

Expected ':' in dictionary literal

This is my first attempt to create a Dictionary:
var powerup: Dictionary = [String: Any]()
powerup = [
"dynamite" :
["tier0":
["effect" : 0]],
["tier1" :
["effect" : 1]]
]
I get the error 'Expected ':' in dictionary literal'. What is wrong with my dictionary?
It should be like that:
var powerup: Dictionary = [String: Any]()
powerup = [
"dynamite" :
[
"tier0":
["effect" : 0],
"tier1" :
["effect" : 1]
]
]
Count your brackets
It's fairly normal since you forget something in you dictionary literal.
I think this will work better :
powerup = ["dynamite":
[
["tier0":
["effect": 0]
],
["tier1" :
["effect": 1]
]
]
]
This is what you need to do if you want to describe a dictionary containing one key named dynamite linked to an array that contains two dictionary each one containing one key linked to a dictionary containing one key linked to one integer value.
You may want to rethink you data structure to since it's quite complicated.

How to Append to complex dictionary types in swift 3

How to append some empty array values to the below dictionary.
var results:Dictionary<String, Array<Dictionary<String, Any>> = [:]
results["key"] = nil doesn't work for me.
I want to have a dictionary like
{
"key1" : [],
"key2" : [],
"key3" : []
}
And should be possible to add Dictionary<String, Any> later in the place of ()
First of all, your first line is illegal. It won't even compile. Please try always to show real code. So, your declaration might look like this:
var results : Dictionary<String, Array<Dictionary<String, Any>>> = [:]
Then to add an empty array to the dictionary, you would say:
results["key"] = []

Append values to [String : AnyObject] or [String : String]

I want to append values to a variable of type [String : AnyObject] how do I do that? cause I can't do the ff.
var someObject:[String : AnyObject] = ["name":"John Diggle", "location":"Star City Prison"]
someObject.append(["work":"soldier"]) // does not work (by that I mean there is no .append function like that of other collection types such as NSMutableArray and NSMutableDictionary
someObject = someObject + ["work":"soldier"] // does not work
I'm finding a way to make easier parameters for Alamofire since Alamofire.request uses [String : AnyObject]
The data type you have mentioned is called Dictionary. And Swift dictionary does not provide API append(_ newElement:). Dictionary is a type of hash table. Each entry in the table is identified using its key.
If you want to add new item (key,value), simply add new value under a new key like that:
object["Work"] = "Soldier"
For more information read this doc Dictionary
Try this:
someObject["work"] = "soldier" as NSString
The problem we're trying to solve is that we don't get automatic bridging from String to NSString (NSString is an AnyObject, but String is not), so we have to cross the bridge ourselves by casting.
Initialize dictionary
var dic = [String: Any]()
or
var dic : [String: Any] = ["firstName": "John", "lastName": "Doe"]
Append value
dic["age"] = 25
Update Value
dic.updateValue("Jan", forKey: "firstName")

Dictionary reference not updating dictionary

I have a reference to a dictionary and when I debug the code I can see that the reference is being populated but the actual dictionary is not getting any of the values.
var newItems = [String:Dictionary<String,AnyObject>]()
for item in self.items{
let itemRef = ref.child("items").childByAutoId()
//let itemOptions: NSMutableArray = []
//let itemRemovedOptions: NSMutableArray = []
//let itemDiscounts: NSMutableArray = []
newItems.updateValue([String:AnyObject](), forKey: "\(itemRef.key)")
newItems["\(itemRef.key)"]?.updateValue(item.parentCategory.id, forKey: "parentCategoryId")
newItems["\(itemRef.key)"]?.updateValue(item.item.id, forKey: "itemId")
newItems["\(itemRef.key)"]?.updateValue(item.item.name, forKey: "name")
newItems["\(itemRef.key)"]?.updateValue(item.qty, forKey: "qty")
newItems["\(itemRef.key)"]?.updateValue(item.discountAmount, forKey: "discountAmount")
newItems["\(itemRef.key)"]?.updateValue(item.notes, forKey: "notes")
newItems["\(itemRef.key)"]?.updateValue(item.price, forKey: "price")
//set options
newItems["\(itemRef.key)"]?.updateValue([String:Dictionary<String,AnyObject>](), forKey: "options")
for option in item.options{
if var optionsRef = newItems["\(itemRef.key)"]?["options"] as? [String:Dictionary<String,AnyObject>]{
optionsRef.updateValue([String:AnyObject](), forKey: "\(option.item.id)")
optionsRef["\(option.item.id)"]?.updateValue(option.item.name, forKey: "name")
optionsRef["\(option.item.id)"]?.updateValue(option.group.id, forKey: "group")
optionsRef["\(option.item.id)"]?.updateValue(option.qty, forKey: "qty")
optionsRef["\(option.item.id)"]?.updateValue(option.price, forKey: "price")
}
}
}
The primary issue here is that when you say if var optionsRef = newItems["\(itemRef.key)"]?["options"] as? [String:Dictionary<String,AnyObject>], you're making a copy of the Dictionary, since Dictionaries are value types in Swift. As a result, all subsequent modifications of the dictionary are applied only to your local copy, and not the one you intended. To alleviate this, you must assign the modified version of the dictionary back into the data structure where you wanted it changed.
But in addition to that primary problem, there are many other, softer, issues with this code. There's a reason this question over 50 views and no answers. People see this, say "Nope!" and run away screaming. Here are some key points:
Be consistent with your use of shorthand notation. [String : [String : AnyObject], not [String : Dictionary<String, AnyObject>].
Don't use string interpolation ("\(foo)") solely to convert types to strings. If the values are already strings, use them directly. If they're not, convert them with String(foo).
Don't use updateValue(_:forKey:). There's pretty much never a reason. Prefer subscript syntax.
When initializing a complex structure (such as your dictionary), initialize it first, then add it. Don't add it, then repeatedly access it through cumbersome copy/pasting.
In your case, consider newItems["\(itemRef.key)"]?.updateValue(.... With every line, the key must be hashed, and used to retrieve the value from the newItems dictionary. Then, the returned value must be checked for nil (by the ?. operator), doing nothing if nil, otherwise executing a method. You know the value won't be nil because you set it, but it has to do all this checking anyway.
Prefer Dictionary literals over mutation wherever possible.
Don't use Foundation data structures (NSArray, NSDictionary, etc.) it's absolutely necessary.
Here's my take on this code. It might need some touch ups, but the idea is there:
var newItems = [String : [String : AnyObject]() //fixed inconsistent application of shorthand syntax
let itemRef = ref.child("items").childByAutoId() //this shouldn't be in the loop if it doesn't change between iterations
for item in self.items {
// There's pretty much no reason to ever use updateValue(_:forKey:), when you can just use
// subscript syntax. In this case, a Dictionary literal is more appropriate.
var optionsDict = [String : [String : AnyObject]();
for option in item.options {
optionsDict[option.item.id] = [
"name" : option.item.name,
"group" : option.group.id,
"qty" : option.qty,
"price" : option.price,
]
}
newItems[itemRef.key] = [
"parentCategoryId" : item.parentCategory.id,
"itemId" : item.item.id,
"name" : item.item.name,
"qty" : item.qty,
"discountAmount" : item.discountAmount,
"notes" : item.notes,
"price" : item.price,
"options" : optionsDict
]
}
Here's an alternative that avoids having to define optionsDict separately:
let itemRef = ref.child("items").childByAutoId()
for item in self.items {
newItems[itemRef.key] = [
"parentCategoryId" : item.parentCategory.id,
"itemId" : item.item.id,
"name" : item.item.name,
"qty" : item.qty,
"discountAmount" : item.discountAmount,
"notes" : item.notes,
"price" : item.price,
"options" : item.options.reduce([String : AnyObject]()) { (var dict, option) in
optionsDict[option.item.id] = [
"name" : option.item.name,
"group" : option.group.id,
"qty" : option.qty,
"price" : option.price,
]
}
]
}