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

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

Related

How to add/Update key/values in dict in swift?

func exercise() {
var stockTickers: [String: String] = [
"APPL" : "Apple Inc",
"HOG": "Harley-Davidson Inc",
"BOOM": "Dynamic Materials",
"HEINY": "Heineken",
"BEN": "Franklin Resources Inc"
]
stockTickers["WORK"] = ["Slack Technologies Inc"]
stockTickers["BOOM"] = ["DMC Global Inc"]
print(stockTickers["WORK"]!)
print(stockTickers["BOOM"]!)
}
Error: Cannot assign value of type '[String]' to subscript of type 'String'
I do not understand the error, I'm new to swift, can someone guide me through this and tell mw why I see this error.
Alexander explained what you need to do in abstract terms. (Voted)
Specifically, change
stockTickers["WORK"] = ["Slack Technologies Inc"]
stockTickers["BOOM"] = ["DMC Global Inc"]
To
stockTickers["WORK"] = "Slack Technologies Inc"
stockTickers["BOOM"] = "DMC Global Inc"
The expression ["Slack Technologies Inc"] defines a String array containing a single string. That's not what you want. you defined a dictionary of type [String:String]
If you wanted your dictionary to have String keys and values that contained arrays of strings, you'd have to change the way you declared your dictionary:
var stockTickers: [String: [String]] = [
"APPL" : ["Apple Inc"],
"HOG": ["Harley-Davidson Inc"],
"BOOM": ["Dynamic Materials"],
"HEINY": ["Heineken"],
"BEN": ["Franklin Resources Inc"]
]
["DMC Global Inc"] is an array literal containing a string. It evaluates to a value of type [String] (a.k.a. Array<String>).
Thus the error makes sense: you’re trying to a assign an array of strings in a place where only a String is expected.
Just remove the square brackets.

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

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.

Contextual type 'AnyObject' cannot be used with dictionary literal multi level dictionary

I'm running into an issue with creating a multi-level dictionary in Swift and have followed some of the suggestions presented here:
var userDict:[String:AnyObject]? = ["SystemId": "TestCompany",
"UserDetails" : ["firstName": userDetail.name, "userAddress" : "addressLine1" userDetail.userAdd1]]
The use of [String:AnyObject]? works for the first level of the Dict, but Swift is throwing the same error at the next level Dict, UserDetail[]. Any suggestions would be greatly appreciated
I'm not pretty sure what are the types of userDetail.name and userDetail.userAdd1 but if they are Strings you should let it [String:String]?
However, [String:Any]? should solve your problem. If you are sure that you want to let it [String:AnyObject]? (I don't think that you want to), you can do something like this:
var userDict:[String:AnyObject]? = ["SystemId": "TestCompany" as AnyObject,
"UserDetails" : ["firstName": userDetail.name, "userAddress" : userDetail.userAdd1] as AnyObject]
Hope this helped.

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.

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
]