I am trying to convert a PFUser (from Parse.com) to a dictionary of [String : AnyObject] type, but I got the following error that seems very confusing to me. I did google search and found the following two SO questions, which, however, still not helpful in solving my problem:
Error:
'(String) -> AnyObject?' does not have a member named 'subscript'
Found these two posts:
relevant SO post 1,
relevant SO post 2
My PFUser has the keys: fullName, email, latitude, longitude, and linkedInUrl.
Since the types of the values for the above keys are a mix of String and Double. so I am trying to create the following Dictionary:
var userData : [String: AnyObject] = [
"fullName" : pfUser.valueForKey("fullName") as? String,
"latitude" : pfUser.valueForKey("latitude") as? Double,
"longitude" : pfUser.valueForKey("longitude") as? Double,
"email" : pfUser.valueForKey("email") as? String,
"linkedInUrl" : pfUser.valueForKey["linkedInUrl"] as? String
]
The version of my XCode is 6.4. I appreciate your help.
You are declaring wrong type of userData. It must be [String: AnyObject?] because your values are optionals.
And don't forget to change this line "linkedInUrl" : pfUser.valueForKey["linkedInUrl"] as? String with this line "linkedInUrl" : pfUser.valueForKey("linkedInUrl") as? String.
And your code will be:
var userData : [String: AnyObject?] = [
"fullName" : pfUser.valueForKey("fullName") as? String,
"latitude" : pfUser.valueForKey("latitude") as? Double,
"longitude" : pfUser.valueForKey("longitude") as? Double,
"email" : pfUser.valueForKey("email") as? String,
"linkedInUrl" : pfUser.valueForKey("linkedInUrl") as? String
]
There are two mistakes in your current code
First one is, use ( ) instead of using [ ] to get the value using the key in the following line.
"linkedInUrl" : pfUser.valueForKey["linkedInUrl"] as? String
Second one is, use an exclamation point instead to unwrap the optional.
Your code should look like what's shown below.
var userData : [String: AnyObject] = [
"fullName" : pfUser.valueForKey("fullName") as! String,
"latitude" : pfUser.valueForKey("latitude") as! Double,
"longitude" : pfUser.valueForKey("longitude") as! Double,
"email" : pfUser.valueForKey("email") as! String,
"linkedInUrl" : pfUser.valueForKey("linkedInUrl") as! String
]
The other solution is, making the dictionary declaration as [String: AnyObject?] to hold the optional values.
Related
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
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
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,
]
}
]
}
I am trying to save data to my plist file which contains definition of array of strings. My plist -enter image description here
My code for writing the data to my plist is --
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask,true) as NSArray
let documentsDirectory = paths.objectAtIndex(0) as! NSString
let path= documentsDirectory.stringByAppendingPathComponent("authors.plist")
let fileManager = NSFileManager.defaultManager( )
var plistArray = NSArray(contentsOfFile: path) as! [Dictionary<String , String>]
var addItem: Dictionary = ["link": linkBook.text , "Cover": cover.text , "title" : titlePage.text , "year" : year.text , "isbn" : isbn.text]
plistArray.append(addItem)
// I am getting the error here
(plistArray as NSArray).writeToFile(path, atomically: true)
My guess is that linkBook, cover, titlePage, year and isbn all represent text fields in your UI? The text property of one or more of whatever those things has a type of Optional String (String?). The plistArray variable is of type [String, String] – neither the keys nor the values can be nil.
Fundamentally you need to answer the question of what to do if the text property of one of the things mentioned above is nil. It could be the case that they are never empty in which case you could add exclamation points to them linkBook.text! and cover.text! etc.
If you do that, however, and any of them ever is nil, that will lead to a crash.
I suspect instead that what you want to do is use the ?? operator. Set up your dictionary as:
var addItem: Dictionary = ["link": linkBook.text ?? "",
"Cover": cover.text ?? "",
"title" : titlePage.text ?? "",
"year" : year.text ?? "",
"isbn" : isbn.text ?? ""]
That way if any of the ever is nil then you will get the empty string in your plist.
The textfield.text is an optional. So you should unwrap it first.
var addItem: Dictionary = ["link": linkBook.text! , "Cover": cover.text! , "title" : titlePage.text! , "year" : year.text! , "isbn" : isbn.text!]
Recently i update Xcode to 7 and it contains swift 2.0 compiler. Before this i made my project with older version of swift. In that version i had create NSMutableDictionary like bellow
let dictParams:NSMutableDictionary? = ["test" : "test",
"username" : txtEmail.text,
"password" : txtPassword.text,
"version" : "1.0",
"appId" : "1",
"deviceId" : "fasdfasdfrqwe2345sdgdfe56gsdfgsdfg"
];
in above code txtEmail.text and txtPassword.text is my text field and fill tha value at run time.
This code is properly working in older version of swift but after update to swift 2.0 it gives me an error like bellow
Cannot convert value of type '[String : String?]' to specified type 'NSMutableDictionary?'
what's wrong with it please guide me.
Simply by opening NSMutableDictionary or NSDictionary class interfaces from Xcode 7, you could easily see that the underlying type is actually [NSObject: AnyObject]. It means that the value can't be nil.
Unwrapping text values like txtEmail.text! or txtPassword.text! might look ok and help you to get rid of the compiling error, but it's a bad choice because technically text property of UITextField is optional and your app can crash in that case!
Do this for your safety:
let dictParams: NSMutableDictionary? = ["test" : "test",
"username" : txtEmail.text ?? "", // Always use optional values carefully!
"password" : txtPassword.text ?? "",
"version" : "1.0",
"appId" : "1",
"deviceId" : "fasdfasdfrqwe2345sdgdfe56gsdfgsdfg"
]
By the way, in case it's not critical to use NSMutableDictionary, please consider using Swift dictionary like this:
var mutableDictionary = [String: AnyObject]
// OR this if the value can be nil
var mutableDictionary = [String: AnyObject?]
simple change in
"username" : txtEmail.text! instead "username" : txtEmail.text
so final code is like bellow
let dictParams:NSMutableDictionary? = ["test" : "test",
"username" : txtEmail.text!, //Add ! here
"password" : txtPassword.text!, //Add ! here
"version" : "1.0",
"appId" : "1",
"deviceId" : "fasdfasdfrqwe2345sdgdfe56gsdfgsdfg"
];
txtEmail.text and/or txtPassword.text are returning an optional which is why its telling you to change the type. You could rewrite the dict as a swift dictionary (which is what I would recommend). When you do so, you can either make the object optional, or leave it as just String and bang out the optional string when you're creating the dict.
var optionalString: String?
var someDict: [String: String?] = ["test": "test", "test1": optionalString]