Can't add UInt64 to dictionary of type: [String : AnyObject?] - swift

I am trying to make a dictionary like this:
func someFunc() -> [String : AnyObject?] {
var dic = [
"Name": someString_Variable,
"Sum": someUInt64_Variable
]
Problem is when I add someUInt64_Variable I get error:
Cannot convert value of type UInt64 to expected dictionary value type Optional<AnyObject>
What to do here, I must use UInt64 I can't convert it to String.
Why am I getting this error anyway?

This used to work in an earlier version of Swift when Swift types were automatically bridged to Foundation types. Now that that feature has been removed, you have to do it explicitly:
You can just explicitly cast them to AnyObject:
let dic : [String: AnyObject?] = [
"Name": someString_Variable as AnyObject,
"Sum": someUInt64_Variable as AnyObject
]
and Swift will convert them to Foundation types NSString for String and NSNumber for UInt64.
It might be clearer if you just cast to those types yourself:
let dic : [String: AnyObject?] = [
"Name": someString_Variable as NSString,
"Sum": someUInt64_Variable as NSNumber
]

It’s important to understand what’s really happening here: Int64 is not compatible to AnyObject because it’s simply not an object.
By bridging to Foundation types, like someUInt64_Variable as NSNumber, you wrap your Int64 in an object. A new NSNumber object is allocated and your Int64 is stored in it.
Perhaps you really need AnyObject, but normally you would use Any in this case:
func someFunc() -> [String : Any] {
var dic = [
"Name": someString_Variable,
"Sum": someUInt64_Variable
]
This offers better performance (no additional heap allocations) and you don’t rely on Foundation-bridging.

Related

Cast Swift Dictionary to CFDictionary

What is the right way to cast Dictionary in Swift 3 to a CFDictionary? Is it legal to write like this?
var dictionary:Dictionary<NSString, Int> = [:]
and then
dictionary as CFDictionary
Yes you can do that. your solution will work and you can also do it like this.
var dictionary = [
key:value
] as CFDictionary
You can refer further from here

Value of type 'String' does not conform to expected dictionary value type 'AnyObject'

I am getting this error when creating a dictionary in Swift:
Value of type 'String' does not conform to expected dictionary value
type 'AnyObject'
Code:
let joeSmith : [String : AnyObject] = ["Name" : "Joe Smith", "Height" : 42, "Soccer Expo" : true, "Guardian" : "Jim and Jan Smith"]
Swift 3
First of all a String in Swift is a struct and does not conform to AnyObject.
Solution #1
The best solution in Swift 3 is changing the type of the Dictionary Value from AnyObject to Any (which includes the String struct).
let joeSmith : [String : Any] = ["Name" : "Joe Smith", "Height" : 42, "Soccer Expo" : true, "Guardian" : "Jim and Jan Smith"]
Solution #2
However if you really want to keep the value fo the Dictionary defined as AnyObject you can force a bridge from the String struct to the NSString class adding as AnyObject as shown below (I did the same for the other values)
let joeSmith : [String : AnyObject] = [
"Name" : "Joe Smith" as AnyObject,
"Height" : 42 as AnyObject,
"Soccer Expo" : true as AnyObject,
"Guardian" : "Jim and Jan Smith" as AnyObject]
Swift 2
The problem here is that you defined the value of your dictionary to be AnyObject and String in Swift is NOT an object, it's a struct.
The compiler is complaining about String because is the first error but if your remove it, it will give you an error for the 42 which again is an Int an then a Struct.
And you will have the same problem with true (Bool -> Struct).
You can solve this problem in 2 ways:
Foundation #1
If you add import Foundation then the Swift struct is automatically bridged to NSString (which is an object) and the compiler is happy
Any #2
You replace AnyObject with Any. Now you can put any kind of value in your dictionary.
Considerations
IMHO we (Swift developers) should progressively stop relying on Objective-C bridging and use the second solution.
used it will helpfull
let plistDict: [String: Any] = ["key": "Value"]
OMHO If you are using Any or AnyObject you are missint the point of using a strongly typed language
because enums in swift can hold associated data a good answer here is to change the way you are using the language and create an enum that captures the different cases then use the enum as a type.
If the dictionary is actualy representing a class of its own than maybe you need to define the class.
either way what yo are doing should scream to you that something is wrong with your code.
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html

Why as? AVAudioSessionRouteChangeReason returns nil?

I'm subscribed to the AVAudioSessionRouteChangeNotification.
Why the following returns nil?
notification.userInfo?[AVAudioSessionRouteChangeReasonKey] as? AVAudioSessionRouteChangeReason
All the other options work, e.g. return Optional(2):
notification.userInfo?[AVAudioSessionRouteChangeReasonKey]
notification.userInfo?[AVAudioSessionRouteChangeReasonKey] as? UInt
notification.userInfo?[AVAudioSessionRouteChangeReasonKey] as? NSNumber
I find the first option the most clear/readable.
Edit 2021-04-15:
In the current Xcode version 12.4 (Swift 5.3.2) it does not return nil. The following works now:
notification.userInfo?[AVAudioSessionRouteChangeReasonKey] as? AVAudioSession.RouteChangeReason
It's because userInfo is an NSDictionary, and you can't add Swift enum constants to NSDictionary. You can add the raw values, though.
That is, it's not legal to declare userInfo as something like
let userInfo : NSDictionary = [ AVAudioSessionRouteChangeReasonKey : AVAudioSessionRouteChangeReason.NewDeviceAvailable ]
...because you get an error saying cannot convert value of type '[String : AVAudioSessionRouteChangeReason]' to specified type 'NSDictionary'. You can declare it like this:
let userInfo : NSDictionary = [ AVAudioSessionRouteChangeReasonKey : AVAudioSessionRouteChangeReason.NewDeviceAvailable.rawValue ]
This is what you have (or the equivalent of what you have, since the framework is almost certainly using C or Objective-C instead of Swift).
Since AVAudioSessionRouteChangeReason is declared as
public enum AVAudioSessionRouteChangeReason : Uint {
...
The raw values are unsigned integers. You can use as? to downcast them to integer types or NSNumber, but they're not the same as the actual enum type.

Swift nested dictionary with nil

How can I have nil in the middle of nest Swift dictionaries?
var d: [String: AnyObject?] = [
"name": "Rodrigo",
"number": 1,
"nil": nil,
"more": [
"a": 1,
"b": 2,
"nil": nil
]
]
The second nil (inside "more") gives me the error Type of expression is ambiguous without more context.
The problem you're having here is that Swift's Dictionary is a value type, and therefore doesn't automatically conform to AnyObject. The compiler tries to help you out here, and bridge over to NSDictionary, which is an AnyObject, but unfortunately NSDictionary can't hold nil values, so you're out of luck.
As I see it, you have a couple options:
Go the standard Objective-C route and use NSNull() instead of nil in your nested array.
Rethink the structure of your data. Maybe instead of nested dictionaries, with string keys, you can build a struct PersonData that holds all the information you need in typed properties (including optionals). You may find that works even better than this approach.

How do I put different types in a dictionary in the Swift Language?

Swift only allows a dictionary to contain a single type.
Here's the definition that is taken from the Swift book:
A dictionary is a container that stores multiple values of the same type
[...]
They differ from Objective-C’s NSDictionary and NSMutableDictionary classes, which can use any kind of object as their keys and values and do not provide any information about the nature of these objects.
If that’s the case then how are we going to create nested dictionaries?
Imagine we have a plist that holds String, Array and Dictionary items in it . If I’m allowed to hold only the same of type of items (either string, array etc.) then how am I going to use different types of items stored in the plist?
How do I put different types in the same dictionary in Swift?
You can achieve plist-like nested structures using Any type for dictionary values which is Swift's somewhat counterpart to Objective-C's id type but can also hold value types.
var response = Dictionary<String, Any>()
response["user"] = ["Login": "Power Ranger", "Password": "Mighty Morfin'"]
response["status"] = 200
EDIT:
Any seems to be better than AnyObject because in the above code response["status"] is of type Swift.Int, while using value type of AnyObject it is __NSCFNumber.
As has been suggested, you can use the Any type to represent a plist dictionary's values. But then how do you work with the data? Cast every value any time you look it up from the dictionary? That's really messy. A better, more type-safe way to model a plist would be to take advantage of Swift's enums, also known as algebraic data types or discriminated unions. They let you specify exactly what types are permitted in the dictionary and avoid ever having to cast. Here's an implementation, explained:
// An atomic (i.e. non-collection) data type in a plist.
enum PListNode {
case PLN_String(String)
case PLN_Integer(Int)
case PLN_Float(Double)
case PLN_Bool(Bool)
case PLN_Date(CFDate)
case PLN_Data(CFData)
}
At the most atomic level, only the above data types may be stored in a plist. Each 'node' in the plist can ultimately can only be one of these types. So we create an enum which lets us specify this.
// A value that can be stored in a plist Dictionary's key-value pair.
enum PListValue {
case PLV_Node(PListNode)
case PLV_Array(PListNode[])
case PLV_Dictionary(Dictionary<String, Box<PListValue>>)
}
typealias PList = Dictionary<String, Box<PListValue>>
A plist is basically a dictionary of key-value pairs, and each value can be either an atomic (i.e. non-collection) value; or it can be an array of atomic values; or it can be a dictionary of string-plist value pairs. The above enum expresses these constraints, and the typealias gives the plist type an easy-to-remember name.
Given the above types, we can completely express any given plist in a type-safe way, e.g.:
// Example translated from
// https://developer.apple.com/library/Mac/documentation/Darwin/Reference/ManPages/man5/plist.5.html
let myPlist: PList = [
"Year Of Birth": Box(PLV_Node(PLN_Integer(1965)))
, "Pets Names": Box(PLV_Array([]))
, "Picture": Box(PLV_Node(PLN_Data(...)))
, "City of Birth": Box(PLV_Node(PLN_String("Springfield")))
, "Name": Box(PLV_Node(PLN_String("John Doe")))
, "Kids Names": Box(
PLV_Array([PLN_String("John"), PLN_String("Kyra")])
)
]
What it means to be type-safe here is that you can process any given plist using a switch statement and cover all possibilities without the need for any casting. You're eliminating a whole class of potential runtime errors. E.g.:
// See https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html#//apple_ref/doc/uid/TP40014097-CH12-XID_189 for explanation
switch myPlist["Year Of Birth"] {
case Box(.PLV_Node(let plvNodeValue)):
...
case Box(.PLV_Array(let plvArrayValue)):
...
case Box(.PLV_Dictionary(let plvDictionaryValue)):
...
}
Note that it's necessary to wrap up recursive data structures in a 'box' (a pointer to the actual value) to keep their sizes finite.
NSObject works for my case while "Any" does not
var d:Dictionary<String,NSObject> = [:]
d["key1"] = "ddd"
d["key2"] = 111 //OK
NSLog("%#", d) //OK
var d2:Dictionary = Dictionary<String,Any>()
d2["key1"] = "ddd"
d2["key2"] = 111
NSLog("%#", d2) //I got error here
Use NSMutableDictionary like this :
var dictInfo : NSMutableDictionary = [ "lang_key": "1"]
dictInfo["food_type"] = lbl_TypeOfFood.text
dictInfo["search_text"] = txt_Search.text
dictInfo["date"] = lbl_Date.text
dictInfo["opening_hours"] = lbl_OpeningHours.text
hope this will work fine .
Use: Dictionary<String, AnyObject>
var dict: Dictionary<String, AnyObject> = [
"number": 1,
"string": "Hello",
]
NSMutableDictionary to Dictionary works like a charm and will allow you to put different types in a Dictionary in the Swift Language:
let nsMutableDictionary = NSMutableDictionary()
nsMutableDictionary[NSFontAttributeName] = UIFont(name: "HelveticaNeue", size: 12.0)!
nsMutableDictionary[NSForegroundColorAttributeName] = UIColor.redColor()
let dictionary: Dictionary<NSObject, AnyObject> = nsMutableDictionary
self.attributedPlaceholder = NSAttributedString(string: textParam, attributes: dictionary)
let dictionary : Dictionary = [
"key": "value",
"key2": 2,
"key3": NSString(),
2: "test",
]
One can specify types which restricts the dictionary
let dictionary : Dictionary<String, String> = [
"key": "value",
"key2": 2, // This errors
]