Swift 2 : How to downcast an Array of Objects - swift

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.

Related

How to show Array of Dictionaries in a SwiftUI List?

After a few months of learning Swift by migrating parts of a legacy ObjC app, I'm looking forward to starting a new app in pure Swift - I'm hoping that working with pure Swift base classes will lead to less unwrapping and other bridging shenanigans.
However, very quickly I've found myself facing similar problems.
I want to read some JSON from a web service, and show in in a list implemented with SwiftUI - should be simple, right?
The data (actually read from the Twitter API) comes in, and I deserialise it,
do {
if let results = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments ) as? [String: Any] {
print(results)
if let followers = results["users"] as? [[String: Any]] {
print (followers.count)
print("followers class \(type(of: followers))")
} else {
print(results["errors"])
}
}
} catch let error as NSError {
print(error.localizedDescription)
}
You'll see I print the class of followers, and this shows,
followers class Array<Dictionary<String, Any>>
...a nice array of Dictionaries, using Swift base classes. That would seem a reasonable data structure to present via a List in SwiftUI. However, it doesn't seem to be so simple, as the data elements need to be Identifiable (fair enough) but I don't have a struct for each element, and I don't want the overhead of processing the array into an array of structs carrying identifiers.
A bit of research, and it seems there's a solution available, as I can initialise List with an Array, something like the following,
var body: some View {
List (twitter.followers!, id: \.self) { follower in // <<< Compilation error
Text("\(follower["name"])")
}
}
However, that code gives the compilation error on the flagged line,
Protocol type 'Any' cannot conform to 'Hashable' because only concrete
types can conform to protocols
I think the issue is that the compiler sees followers as 'Any', rather than an Array, but why?
btw, I've seen the answer to this question, but it seems the List initialiser should be a more elegant solution, if I can get it to work...
You have to create a struct for follower/user and declare it as Decodable and Identifiable.
Then you will be able to use JSONDecoder to read the data from the struct into an array for you.
As a result your twitter.followers will be an array of identifiable objects and you will be able to use it in ForEach().

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.

Swift3, How to add AnyObject in a dictionary?

I had my files conveted to Swift3, one dictionary was like
var Detail: [String:[String:AnyObject]] = [
"0":["0":["number":0,"imageName":"40"]],
"1":["0":["number":1,"imageName":"3setting"],"1":["number":1,"imageName":"3private"],"2":["number":1,"imageName":"3currency"]],
"2":["0":["number":1,"imageName":"3favourite"],"1":["number":1,"imageName":"3favourite"]],
"3":["0":["number":1],"1":["number":1]],
]
I works perfectly in the past, but today it reminded
Contextual type 'AnyObject' cannot be used with dictionary literal
But why it doesn't work now?
Why this happeded and how to solve it?
Write [String:Any] instead of [String:AnyObject] and you'll be fine.
As for "what happened": automatic bridging went away (e.g. between Int and NSNumber, or String and NSString). So a literal dictionary like ["imageName":"3setting"] is inferred as a [String:String], and cannot be assigned where a [String:AnyObject] is expected — because a String is not an AnyObject. But a String is certainly an Any, because everything is an Any.
You could alternatively work around this by writing ["number":1 as NSNumber, "imageName":"3setting" as NSString] (because an NSNumber or an NSString is an AnyObject), but there seems little point in doing that here. In the general case, a dictionary is an [AnyHashable:Any] now, and you should use that as the catch-all type; AnyObject is basically going away, slowly but surely.

Converting error: Type 'Any' has no subscript members

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.

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)
}