Converting error: Type 'Any' has no subscript members - swift

After converting the for loop to Swift 3 I got the error "Type 'Any' has no subscript members"
for inputKey in inputKeys where attributes[inputKey]?[kCIAttributeClass] == "NSNumber"
.....................^
{
}
I would expect to add something like
for inputKey in inputKeys where attributes[inputKey]as?[String:Any][kCIAttributeClass] == "NSNumber"
but this doesn't work :-(
Still have some problems with the Swift syntax.

It looks like you want attributes to actually be [String: [String: String]] - a dictionary of dictionaries.
Either that, or you can cast attributes[inputKey] to [String:String].
I think this would work:
for inputKey in inputKeys where (attributes[inputKey] as? [String:String])?[kCIAttributeClass] == "NSNumber"
Edit per comments:
Since attributes isn't actually guaranteed to be [String: [String: String]], but only [String: [String: Any]] (and maybe not even that), you'll need an extra as? cast to be safe.
With that many casts on one line, I think it would be better to put the test into a guard statement at the beginning of the for body instead of having such a huge where clause.

Related

What is the return type of JSONSerialization.jsonObject(with:options:) in Swift?

I made the json parser in swift language.
But, many of people are using like below.
let jsonParsed = JSONSerialization.jsonObject(with: data, options: [])
guard let jsonDict = jsonParsed as? Dictionary<String, AnyObject> else { return }
...
Then, I wonder the type of jsonParsed. The JSONSerialization.jsonObject(with:options:) function reference describes that the result type is just Any.
I know the type is Dictionary because JSON. The KEY is String type but, VALUE is AnyObject? How about Any?
I know the difference between AnyObject and Any. Any also includes value type, function type.
Number is also value type in swift: Int, Float, Double...
Is that impossible return type is value type?
Json can be array also. This is valid josn
[
"1",
"2",
"3"
]
You can use this site to verify [https://jsoneditoronline.org/][1].
When you give fragmentsallowed option in the function json result can be Int,Float or any other primitive data type.
So it is not possible to return type in compile time. At run time it is possible. But return type is decided at run time.

Iterating dictionary swift 3

I have below code in my project.
for (key, value) in photoDic {
if let url = URL.init(string: value as! String){
let photo : PhotoRecord = PhotoRecord.init(name:key as! String, url:url)
self.photoRecords.append(photo)
}
}
My question is how can I make key and value in for loop optional, or check if either of them are nil?
I am not able to check if they are nil, getting warning saying any cannot be nil because it is nonoptional.
I was thinking of using something like
for(key:String?, value:String?){}
But it is not working.
The key in a dictionary can't be an optional. (The key must conform to the Hashable protocol, and optionals don't.) So you CAN'T make the keys in your dictionary optional
If you want the values of your dictionary to be Optionals then you need to declare them as Optionals.
So, for example, change
let photoDic: [String: String] = ["key1": "http://www.someDomain.com/image.jpg"]
to
let photoDic: [String: String?] = ["key1": "http://www.someDomain.com/image.jpg"]
(Note that the type of photoDic is changed to [String: String?].)
As mentioned already all keys in a dictionary are non-optional by definition.
Further in NSDictionary all values are non-optional by definition, too.
Be happy about that because
There is no need to check for nil.
The code will never crash.
A Swift dictionary can theoretically contain optional values but practically you are discouraged from using it. For compatibility reasons to NSDictionary a nil value indicates key is missing.

Swift 2 : How to downcast an Array of Objects

I have this Array and I am trying to POST it to backend, but got really confused with all the casting
valuesDictionary=["medication": Optional("Novocain"), "dateOfBirth": Optional(2001- 01-01 00:00:00 +0000), "lastName": Optional("Berthold"), "allergies": Optional("Heuschnupfen"), "firstName": Optional("Alexander"), "Blutgruppe": Optional("A"), "PostalAddress": Optional(Eureka.PostalAddress(street: Optional("Gleimstraße"), state: nil, postalCode: Optional("10123"), city: Optional("Berlin"), country: Optional("DE")))]
trying to feed it into:
let request = Alamofire.request(.POST, Config.profileUpdate, parameters: valuesDictionary , encoding: .JSON)
I tried different things like:
let valuesDictionary = form.values() as! [String:AnyObject]
to downcast into the expected form but it just showing:
fatal error: can't unsafeBitCast between types of different sizes
Optionals aren't AnyObjects because Optional is an enum (a value type). You'll need to unwrap your optionals before you shove them in your dictionary.
I had some crazy stuff happening with the values I wanted to throw into Firebase. I ended up finding where they originally got away from their expectations and fixed that.
Wherever you've declared something that is different than what you want the final result to be, make it what the final result should be. In this case, wherever it's declared as anything other than an object, make it an object.
It's more work but it would save you some time in the end.

type 'Any' has no subscript members

let employerName = snapshot.value! ["employerName"] as! String
let employerImage = snapshot.value! ["employerImage"] as! String
let uid = snapshot.value! ["uid"] as! String
I reviewed previous posts but cannot seem to find a way to solve this problem. All three lines of code give the "type 'Any' has no subscript members" error. Fairly new to this so any help is appreciated.
snapshot.value has a type of Any. A subscript is a special kind of function that uses the syntax of enclosing a value in braces. This subscript function is implemented by Dictionary.
So what is happening here is that YOU as the developer know that snapshot.value is a Dictionary, but the compiler doesn't. It won't let you call the subscript function because you are trying to call it on a value of type Any and Any does not implement subscript. In order to do this, you have to tell the compiler that your snapshot.value is actually a Dictionary. Further more Dictionary lets you use the subscript function with values of whatever type the Dictionary's keys are. So you need to tell it you have a Dictionary with keys as String(AKA [String: Any]). Going even further than that, in your case, you seem to know that all of the values in your Dictionary are String as well, so instead of casting each value after you subscript it to String using as! String, if you just tell it that your Dictionary has keys and values that are both String types (AKA [String: String]), then you will be able to subscript to access the values and the compiler will know the values are String also!
guard let snapshotDict = snapshot.value as? [String: String] else {
// Do something to handle the error
// if your snapshot.value isn't the type you thought it was going to be.
}
let employerName = snapshotDict["employerName"]
let employerImage = snapshotDict["employerImage"]
let uid = snapshotDict["fid"]
And there you have it!
Since you want to treat snapshot.value as an unwrapped dictionary, try casting to one and, if it succeeds, use that dictionary.
Consider something like:
func findElements(candidate: Any) {
if let dict: [String : String] = candidate as? Dictionary {
print(dict["employerName"])
print(dict["employerImage"])
print(dict["uid"])
}
}
// Fake call
let snapshotValue = ["employerName" : "name", "employerImage" : "image", "uid" : "345"]
findElements(snapshotValue)

Chaining Optionals in Swift

Up until now, I've been unwrapping Optionals in Swift 2.1 like so:
#IBOutlet var commentTextView: UITextView!
if let comment = user["comment"] as? String {
commentTextView.text = comment
}
I never really thought about it, but I think the reason I was doing this was because I was worried that this statement would throw an error if user["comment"] returned something other than a String:
commentTextView.text = user["comment"] as? String
If user["comment"] isn't a String, will the variable on the left of the assignment operator be assigned and throw an error or will the assignment be skipped?
I guess user is in fact a dictionary [String: Any] and what you really do with if let comment = user["comment"] as? String { ... } is not just unwrapping the optional but a conditional type casting (and then unwrapping an optional result of it):
Use the conditional form of the type cast operator (as?) when you are not sure if the downcast will succeed. This form of the operator will always return an optional value, and the value will be nil if the downcast was not possible. This enables you to check for a successful downcast.
Now, to answer your question, if user["comment"] isn't a String then the result will be that commentTextView.text will be assigned nil value, which is bad because its type is String! (implicitly unwrapped optional) about which we hold a promise that it will never be nil. So, yes, there will be an error, an exception actually, but not at the place you would like it to be but at the moment your application will try to access its value assuming that it's not going to be nil.
What you should really do depends on a particular case.
E.g. if you can make user to be a dictionary like [String: String], then you would be able to truly get to unwrapping the optionals and use something like if let comment = user["comment"] { ... }. Or, if you are totally sure that the value for "comment" key will always be there, then you could just do let comment = user["comment"]!.
But if that's not possible then you have to stick with down-casting and the only other thing you can do is to use forced form of it, that is commentTextView.text = user["comment"] as! String. This one at least will produce an exception right at the spot in case if the value at "comment" happens to be not a String but something else.
nil will be assigned to the variable.
If the type of the variable is a non-optional, you'll get a runtime error.
However if user["comment"] is a String you'll get a compiler error about missing ! or ?.
First we need to know of what type the dictionary "user" is.
I assume it is of an unknown type like [String: AnyObject], otherwise why would you try to unwrap it as an String. Let us write a short test to see what happens:
let dict: [String: AnyObject] = ["SomeKey" : 1]
if let y = dict["SomeKey"] as? String {
print(y)
}
You can see clearly that the value of "SomeKey" is an Integer. Trying to unwrap it as an String triggers no error, the "if" statement is just skipped. If an assignment actually happened is hard to prove (maybe by looking at the assembler code) because the variable "y" simply does not exist after the if statement. I assume it will not be created at all.
If the type of the dictionary is known as [String: String] you can omit the try to unwrap it as a String because it's always clear that the type is String.
let dict2: [String: String] = ["SomeKey" : "SomeValue"]
if let y = dict2["WrongKey"] {
// In this case print(y) will not be called because the subscript operator of the dictionary returns nil
print(y)
}
// In this case print(y) will be called because the key is correct
if let y = dict2["SomeKey"] {
print(y)
}