Swift3, How to add AnyObject in a dictionary? - swift

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.

Related

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.

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.

Type "Any" has no subscript members despite casting as AnyObject on Swift 3?

Recently converted code from earlier version of swift to swift 3. Got a lot of errors signifying that type "any" has no subscript members and I thought this could be fixed by casting as AnyObject, but the error persists (and therefore the code I post here does not have this cast in it). Here is the relevant code:
func textfieldTextWasChanged(_ newText: String, parentCell: CustomCell) {
let parentCellIndexPath = tblExpandable.indexPath(for: parentCell)
var address = ""
address = "\(newText)"
// TODO: add a pin to the map from input address
cellDescriptors[0][11].setValue(address, forKey: "primaryTitle")
location = cellDescriptors[0][11]["primaryTitle"]! as! String
tblExpandable.reloadData()
}
Note that cellDescriptors is defined earlier in the code as an NSMutableArray. The error shows up right after cellDescriptors[0] in both lines that it is in. Not sure how to fix this.
It's because you're using more than one subscript operator, because presumably this is something like an array of arrays. But NSMutableArray's subscript operator returns Any. As a result, cellDescriptors[0] is Any. You try to use [11] on the result, but Any doesn't accept subscripts because it's Any, not a collection type.
Casting to AnyObject doesn't help because AnyObject is also not a collection type.
What you should do is cast cellDescriptors[0] to something that accepts subscripts. The right choice depends on what kind of data you're storing in cellDescriptors, but it's presumably some kind of collection, probably an array type.
Another approach would be to change cellDescriptors to be a Swift type instead of NSMutableArray. You could specifically declare the types for each part of your data structure, and then type casting wouldn't be needed.

Ambiguous use of a subscript when accessing a 2-dimensional array bridged from Obj-C

This code which ran perfectly in Xcode 7.0 now complains with a error : Ambiguous use of a subscript in Xcode 7.3.1 on the second line.
let ar = sender.draggingPasteboard().propertyListForType("ABLinkedPeopleUIDsPboardType") as! NSArray?
let uniqueID = ar![0][0] as! String
I understand that the NSArray on its own is now considered bad practice, but what do I need to do to get this to compile and run?
NSArray is a single-dimensioned array, but you're trying to use it as a two-dimensional array. I can't see how this would ever compile.
You need to translate into Swift types immediately so you can continue programming in Swift, not go adrift in a sea of force-unwrapped Optionals.
How about:
if let ar = sender.draggingPasteboard().propertyListForType("ABLinkedPeopleUIDsPboardType") as? [[String]] {
// I'm assuming you're expecting to get back a two-dimensional array of Strings, or in the Obj-C realm, an NSArray of NSArrays of NSStrings
let uniqueID = ar[0][0]
}
else {
print("Property List for ABLinkedetc. is not a 2D String Array!")
}
Hayden's link is the correct general discussion but if you're new to bridging Obj C to Swift it may be difficult to apply in your particular case.

Using setValue(value, forKey: key) on Int? types fires non key value coding-compliant method

I'm successfully using the setValue(value, forKey: key) method in my NSKeyValueCoding compliant Swift NSObject subclass.
This works perfectly well on String optionals, e.g.
var name:String?
However, on Int optionals, it fails, triggering the undefined key method that I have overridden for debugging purposes:
override func setValue(value: AnyObject!, forUndefinedKey key: String!) {
println("\(self) this class is not key value coding-compliant for the key \(key)")
}
So for example, a key of myId with a good integer value would trigger the undefined key method above.
var myId:Int?
If I change the above definition to be non-optional, then everything works fine:
var myId:Int = 0
With myId as an optional, I have tried absolutely everything I can think of in the way of casting, unwrapping, initialising, and so on. It just doesn't see the class as key value compliant for those numeric values.
I know that it is a good numeric value. Changing the var declaration to String? crashes. It also looks fine in lldb:
Printing description of key:
myId
key NSObject 0x00007fb8d530ca20 0x00007fb8d530ca20
k NSString "myId" 0x00007fa2aa942f20
value __NSCFNumber * Int64(4348129) 0xb000000004258e13
Printing description of value:
4348129
(lldb)
So, the question is, has anyone used - in Swift - the NSKeyValueCoding method setValue(value, forKey: key) on an Int type successfully?
KVO cannot function with pure Swift optionals because pure Swift optionals are not Objective-C objects. Swift forbids the use of dynamic or #objc with generic classes and structures because there is no valid Objective-C equivalent, and so the runtime is not setup to support KVO on instances of those kinds of objects. As for why it works with String?, that type is toll-free bridged to NSString, so it is semantically equivalent to NSString *, an Objective-C type that the runtime knows full-well how to deal with. But compare that to Int? whose semantic equivalent would be Optional<Int>, not UnsafePointer<Int> or NSNumber * like you might expect. For now, you'll need to convince the typechecker that it is safe to represent in Objective-C by using NSNumber!.
This is completely backwards and, in my opinion, an unfortunate limitation of the type system. For any engineers that come across this post, see rdar://18624182.
If you're willing to ditch Swift types change:
var myId:Int?
to:
var myId:NSNumber?