Cannot Subscript Value of Type JSON to an Index of Type JSON Error in Swift - swift

I am using Wunderground to gather history data, using SwiftyJSON. However, in the API, a section called "dailysummary" is curtained off in an array. Abbreviated, it looks something like this:
"history": {
"dailysummary": [{
"stuff": "here"
]}
I had tried to isolate the content inside the array with this:
var jsonData = json["history"]["dailysummary"]
var arrayData = json["history"]["dailysummary"][0]
jsonData = json["history"]["dailysummary"][arrayData]
This code returns an error saying it cannot subscript a value of type 'JSON' with an index of type 'JSON'. Is there any way to make this work, or a way to get the data from to array in this format?

You want something like:
var arrayData = json["history"]["dailysummary"][0]["stuff"].
arrayData is a dictionary.

Related

How to add value to dictionary without replacing it?

I'm trying to append values inside dictionary without replacing it for example:
var dict = [String:Any]()
dict["login"] = ["user" : "jon"]
//...
//...
//...
dict["login"] = ["password": "1234"]
When I'm trying to add a value to login at the second time , it's overwrite the first value.
How can I append this data?
I'm using Swift 3.
Edit: let me rephrase my question a little bit.
I want to build a dynamic dictionary which I'll post it to alamoFire as body parameters. so if I have an JSON that looks like this:
{
"name" : "Jon",
"details" : {
"occupation" : "lifeguard"
"years_of_ex" : 3
}
"more_details" : "extra info"
"inner_body" : {
"someInfo" : "extra info"
}
... // there might be lots of other fields since it's dynamic
... // the server expect to have missing fields and not empty ones
}
I want to add dynamically details since I don't know how my Dictionary would looks like.
so adding to values to dictionary without override them is a must for me.
Define an intermediate variable and assign to it:
var dict = [String:Any]()
dict["login"] = ["user" : "jon"]
if var login = dict["login"] as? [String: String] {
login["password"] = "1234"
dict["login"] = login
}
To reflect for the edit in the question: without knowing the exact structure of the dictionary, you cannot modify it as you wish. Anyways, modifying a JSON dictionary directly is really bad practice.
Parse the JSON response into a custom object, modify the object as you wish, then encode the object back to JSON and use it as your request's body.
If you don't want to write the JSON parsing function yourself, have a look at ObjectMapper. It can both map a JSON response to objects and can also map an object to JSON.
you cannot directly "append" new values to a dinamic dictionary without knowing the type and content.
Since
dict["login"]
returns you a Any? you have no way to directly manipulate it.
Do you have any ideas of the possible combinations of the content of each dictionary leaf?
you could write a recursive methods that tries to downcast the content of the leaf and base on it, try to do something
switch dict["login"] {
case is [String: Any?]:
// check that the key does not already exist, to not replace it
case is [Int: Any?]:
// Do something else
}

Iterating through an array of strings, fetched from MongoDB

I'm using the MongoKitten library to fetch documents from mongoDB.
I have the following document in my mongoDB:
{
...
foo: ["A", "B"]
...
}
I can query the db, but I can't loop through the foo array of the returned documents. For instance, let's say that I save the results of my query into mongoDocs.
for Doc in mongoDocs {
print(Doc["foo"] as Any) // prints ["A", "B"]
var tempFoos = [String]()
for foo in Doc["foo"] { // gives error: Type 'Value' does not conform to protocol "Sequence"
print("Foo: " + foo)
tempFoos.append(foo)
}
}
I understand the error. Basically, my foo array doesn't conform to the Sequence protocol that allows me to loop over it. But how do I fix that?
Edit - Here's the code I'm using to fetch the mongoDocs. I've printed the results and used other properties from them. I just can't seem to iterate through this array.
mongoDocs = try self.geographiesCollection!.find(matching: q, projecting: projection, limitedTo: 100)
Here's the relevant function in the MongoKitten source code. The function returns Cursor<Document>
Here you can check out how a framework dev explained handling this situation. MongoKitten closed issue 27
here are some quotes from his explanation incase the link becomes invalid.
"MongoKitten BSON library always returns an enum (Value) when subscripting a document."
"A BSON array is really just a document with keys from 0 to x, so the enum case for array has a document as it's associated value. Because Value can also be, say, a double or a date, it doesn't conform to the Sequence protocol.
The easiest way to iterate over the array is by using the document convenience accessor on Value. This returns the underlying document if Value is either an array or document, or an empty document if it's something else. You can then iterate like this:"
for (key, val) in doc["vals"].document {
print("Value is \(val)")
}
Convert it into an array:
for Doc in mongoDocs {
guard let arr = Doc["foo"] as? [String] else { continue }
for foo in arr {
// do your things
}
}
I think you're using BSON 3. Value is an enum with multiple cases, which you can see here. Here's my stab at it:
for doc in mongoDocs {
guard case Value.array(let fooArray)? = doc["foo"] {
fatalError("doc[\"foo\"] is not an array")
}
print(fooArray)
}

How to fetch array of string elements with SwiftyJSON?

I have a JSON that might contain an array of string elements and I want to save it to a variable. So far I did:
import SwiftyJSON
(...)
var myUsers = [""]
if(json["arrayOfUsers"].string != nil)
{
myUsers = json["arrayOfUsers"] //this brings an error
}
The error says:
cannot subscript a value of type JSON with an index of type string
How can I pass this array safely to my variable?
You have to get the array of Strings that SwiftyJSON has prepared when it parsed your JSON data.
I will use if let rather than != nil like you do in your question, and we're going to use SwiftyJSON's .array optional getter:
if let users = json["arrayOfUsers"].array {
myUsers = users
}
If for any reason you get a type error, you can explicitly downcast the SwiftyJSON object itself instead of using the getter:
if let users = json["arrayOfUsers"] as? [String] {
myUsers = users
}
Note that your array of Strings is also not created properly. Do like this:
var myUsers = [String]()
or like hits:
var myUsers: [String] = []
Both versions are equally valid and both create an empty array of strings.

Cannot convert value of type '[String]?' to expected argument type 'String'

What I want to do is set the image of firstCard to be equal to the image file with the name corresponding to firstCardString.
For instance, in the case below, the code could be setting self.firstCard.image to show the image named "card1" if it randomly picks it (I've clipped the rest of the array for brevity, the full thing contains 52 objects).
var deckArray = [
"card1": ["Bear","Ball"],
"card2": ["Bear","Ball"],
"card3": ["Bear","Ball"],
"card4": ["Bear","Ball"],
"card5": ["Bear","Ball"],
"card6": ["Bear","Ball"],
"card7": ["Bear","Ball"],
]
let firstRandomNumber = Int(arc4random_uniform(52))+1
let firstCardString = deckArray["card\(firstRandomNumber)"]
self.firstCard.image = UIImage(named: firstCardString)
Instead, I'm getting the following error:
Cannot convert value of type '[String]?' to expected argument type 'String'
I'm not entirely sure what this error message means, what is [String]??
[] is an array, and ? is an optional. Your firstCardString is not a string at all, but an optional array of strings. You've read the deckArray dictionary's value for that card, you see, and so your firstCardString actually looks something like this: Optional(["Bear", "Ball"]). I think you meant:
self.firstCard.image = UIImage(named: "card\(firstRandomNumber)")
This will set the image based on strings like "card1" or "card4". I assume you'll use your dictionary for something else, later. When you do, be sure to unwrap the optional value it returns:
if let cardArray = deckArray["card\(firstRandomNumber)"] {
//do something with bears and balls
}
Alternatively, consider making deckArray an array (which will make the name more reasonable), rather than a dictionary. Then you won't have to deal with optionals and will be able to access items as follows:
let cardArray = deckArray[firstRandomNumber]
//do something with bears and balls
Your deckArray is a Dictionary, and your firstCardString is an Array.
String = String
[String] = Array of strings.
It seems like deckArray is in fact, a dictionary of arrays of strings. Therefore if firstRandomNumber = 1, deckArray["card\(firstRandomNumber)"] will return ["Bear","Ball"]. That is definitely not a string!

Retrieve Parse Array of Objects in Swift

I'm storying an array of JavaScript key/value objects in a column of type Array on Parse like this:
[{"1432747073241":1.1},{"1432142558000":3.7}]
When I retrieve that column in Swift, I can see the data, but I'm unsure what data type to cast it as:
if let data = dashboardObject[graphColumn] as? [AnyObject]{
for pair in data{
println(pair)
}
}
That print yields this in the console (for the first pair):
{
1432747073241 = "1.1";
}
I can't seem to cast its contents as a Dictionary [Int:Double] and I'm guessing that means this is a string.
How do I parse this data in Swift? Thanks.
The Dictionary you should parse it to is [String: AnyObject]. It seems as if the keys of this dictionary are timestamps which you probably don't know. You could iterate through the dictionary like this:
for (key, value) in pair {
// do what you want in here with the value and/or the key
}