Immutable value error when appending to array within dictionary after downcasting - swift

var someDict = [String:Any]()
someDict["foo"] = ["hello"]
(someDict["foo"] as? [String])?.append("goodbye") // error here
I am trying to add a value to an existing dictionary containing an array. The dictionary also contains other non-array values, so it has to have value type Any. The problem is that, when I do this, I get an error Cannot use mutating member on immutable value of type '[String]'. Some Googling turned up a few references such as this suggesting that arrays within dictionaries are always immutable, but the compiler doesn't complain if I do this:
var someDict = [String:[String]]()
someDict["foo"] = ["hello"]
someDict["foo"]?.append("goodbye")
so I suspect that information is outdated and it's something specific to the downcasting. Is there any way I can get around this without copying and re-assigning the entire dictionary value?

Yes, it is related the the downcasting. Try this instead:
var someDict = [String:Any]()
someDict["foo"] = ["hello"]
if var arr = someDict["foo"] as? [String] {
arr.append("goodbye")
someDict["foo"] = arr
}

Related

Array keeps coming back as Nil from Parse

I'm trying to determine if a Parse value has been defined and then if the object has a value assign the Array to my arr variable.
But it keeps coming back as nil. And yes, there is a value inside blockedUsers which is a Parse Array.
if let blockedUser2 = currentUser?["blockedUsers"] as! [String]{
let arr = currentUser?["blockedUsers"] as! [String]
print(arr)
}
You don't need arr: if the condition is matched, you can access blockedUser2. You are already unwrapping it safely with the if let declaration.
About your issue... could you provide some more code? Where do you get your currentUser array from?

Accessing values in a dictionary containing AnyObject

I have some data I stored into a dictionary which is defined as:
let data = Dictionary<String, AnyObject>()
In this dictionary the value is always a string, but the value can be an array or integer or string. But when I try to access an item in a array in this dictionary, like:
let item = data["key"][0]
It gives me this error:
Cannot subscript value of type "AnyObject"
How should I access that item?
You need to tell the compiler that you're expecting an array:
if let array = data["key"] as? [Int] {
let item = array[0]
}
Without that, the compiler only knows that there MAY be an AnyObject in data["key"] (it might also be nil).

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.

Swift various ways of creating empty array what are differences between them?

I looked through docs some forums and found I can create an array in various ways. I am confused which one should we be using?
var testArray = [Int]()
testArray.append(1)
var anotherTestArray: [Int] = []
anotherTestArray.append(1)
var yetAnotherTestArray: Array<Int> = []
yetAnotherTestArray.append(1)
var yetYetYetAnotherTestArray = Array<Int>()
yetYetYetAnotherTestArray.append(1)
This is not empty array but It keeps it's type for each element to be strictly to an Int
var yetYetAnotherTestArray = [1]
I think the cleanest way to create an array is var testArray: [Int] = []
In swift array objects must be the same type. If you want to store different objects of different types use [AnyObject]. However, you should always know what is coming out of it. Since the returning type value will be AnyObject, you have to cast down the value to the type you want.
I really don't recommend using AnyObject as the type of your arrays unless you really know what you are doing.
Here is an example anyway:
let a: [AnyObject] = [1, "a"]
let b = a[0] // An Int
let c = a[1] // A String

(String: AnyObject) does not have a member named 'subscript'

I've been through similar questions but still do not understand why my code is throwing an error.
var dict = [String:AnyObject]()
dict["participants"] = ["foo", "bar"]
dict["participants"][0] = "baz"
The error is on line 3: (String: AnyObject) does not have a member named 'subscript'
I'm setting the participants key to an array and then trying to update the first element of it without any luck. The code above is shortened for example purposes, but I am using [String:AnyObject] because it is not only arrays that are stored in the dictionary.
It's probably something really trivial but I am still new to Swift. Thanks for any help in advance!
The error message tells you exactly what the problem is. Your dictionary values are typed as AnyObject. I know you know that this value is a string array, but Swift does not know that; it knows only what you told it, that this is an AnyObject. But AnyObject can't be subscripted (in fact, you can't do much with it at all). If you want to use subscripting, you need to tell Swift that this is not an AnyObject but rather an Array of some sort (here, an array of String).
There is then a second problem, which is that dict["participants"] is not in fact even an AnyObject - it is an Optional wrapping an AnyObject. So you will have to unwrap it and cast it in order to subscript it.
There is then a third problem, which is that you can't mutate an array value inside a dictionary in place. You will have to extract the value, mutate it, and then replace it.
So, your entire code will look like this:
var dict = [String:AnyObject]()
dict["participants"] = ["foo", "bar"]
var arr = dict["participants"] as [String] // unwrap the optional and cast
arr[0] = "baz" // now we can subscript!
dict["participants"] = arr // but now we have to write back into the dict
Extra for experts: If you want to be disgustingly cool and Swifty (and who doesn't??), you can perform the mutation and the assignment in one move by using a define-and-call anonymous function, like this:
var dict = [String:AnyObject]()
dict["participants"] = ["foo", "bar"]
dict["participants"] = {
var arr = dict["participants"] as [String]
arr[0] = "baz"
return arr
}()