How to convert a Swift dictionary with enum keys to an NSDictionary? - ios

I can't convert a Swift dictionary to an NSDictionary. I'm using a Swift enumerate as the key of my dictionary:
enum StringEnum : String {
case Lemon = "lemon"
case Orange = "orange"
}
var swiftDictionary = [StringEnum: AnyObject]()
swiftDictionary[.Lemon] = "string value"
swiftDictionary[.Orange] = 123
When I try to convert it to a NSDictionary with the as keyword:
let objcDictionary: NSDictionary = swiftDictionary as NSDictionary
I get the compiler error:
'[StringEnum : AnyObject]' is not convertible to 'NSDictionary'
Can these types of dictionaries be converted or do I need to loop the Swift dictionary and create an NSDictionary manually?

I think if your dictionary have enum values then you can not convert it to NSDictionary but another way to do that is:
//change the type of your dict to [String: AnyObject]()
var swiftDictionary = [String: AnyObject]()
//you can store rawValue as a key
swiftDictionary[StringEnum.Lemon.rawValue] = "string value"
swiftDictionary[StringEnum.Orange.rawValue] = 123
let objcDictionary = swiftDictionary as NSDictionary //["lemon": "string value", "orange": 123]
Hope this will help.

None of them (StringEnum, String, Dictionary) are obj-c types, therefore you cannot do that implicitly. You definitely need a loop for that.

It is complaint because you cannot save values type to NSDictionary (enums is a value type). You have to wrap it to a NSNumber or any other type.

Related

Getting error while trying to store Int, arrayof [string] pair in userDefaults

self.fetchMin(forStartDate: start, toEndDate: end) { (min, chal) in
guard let mins = min, let challenges = chal else {
return
}
let dict: [Int : [String]] = [mins:challenges]
UserDefaults.standard.set(dict, forKey: "WeekStates")
}
Hi, In the above program I'm trying to store key and array pair of string in userDefaults but if I do so it crashes unexpectedly.
If I try with one value, its working.
eg:
`let dict: [String] = challenges
UserDefaults.standard.set(dict, forKey: "WeekStates")
https://developer.apple.com/documentation/foundation/userdefaults
The UserDefaults class provides convenience methods for accessing
common types such as floats, doubles, integers, Booleans, and URLs. A
default object must be a property list—that is, an instance of (or for
collections, a combination of instances of): NSData , NSString ,
NSNumber , NSDate , NSArray , or NSDictionary . If you want to
store any other type of object, you should typically archive it to
create an instance of NSData. For more details, see Preferences and
Settings Programming Guide.
You can do something like this.
let dict: [Int : [String]] = [1:["iOS Dev"]]
UserDefaults.standard.set(NSKeyedArchiver.archivedData(withRootObject: dict as NSDictionary) as NSData, forKey: "4WeekStates")
if let unarchivedObject = UserDefaults.standard.object(forKey: "4WeekStates") as? Data {
let dic = NSKeyedUnarchiver.unarchiveObject(with: unarchivedObject as Data) as? NSDictionary
print(dic!)
}
As you mentioned at last point i tried [String] it works.
I'm not sure whether you can store data like type of [Int : [String]], As of my knowledge UserDefaults Property lists can only accept certain types of variables, so it has that limitation. You can store these types of variables:
Array
Data
Date
Dictionary
NSNumber
String

Cannot assign value of type String to type AnyObject? Swift 3.0

I am suddenly getting this error for a dictionary of type:
var parameters = [String: AnyObject]()
and then if I try:
parameters["cancelled_by"] = someUser ?? ""
I am getting the error as :
Cannot assign value of type String to type AnyObject?
This is for Swift 3.0. What am I doing wrong here? Why doesn't it work?
String is the value type. AnyObject only accepts reference types. So in order to add both value types and reference types in Dictionary use Any instead of AnyObject, i.e.
var parameters = [String: Any]()
This is an addition to Swift 3.0.
I'm starting with Swift 3 right now, a bit late... however, until Swift 2.2 some Swift value types were automatically bridged to the corresponding foundation reference types, such as String to NSString, Dictionary<> to NSDictionary, and so forth. It looks like this automatic bridging has been removed in Swift 3.
There are cases where turning a [String : AnyObject] into [String : Any] makes sense, in others it doesn't, depending on what you're doing. In my current case, where I need reference types, it doesn't.
So I am solving the problem by requesting an explicit bridging, by casting to AnyObject:
var dictionary: [String : AnyObject] = [:]
dictionary["valueType"] = "Value Type" // Error
dictionary["referenceType"] = "Reference Type" as AnyObject // OK
For reference:
let text = "Text"
let asAny = text as Any
let asAnyObject = text as AnyObject
let asNSString: NSString = text as NSString
type(of: text) // Prints String.Type
type(of: asAny) // Prints String.Type
type(of: asAnyObject) // Prints _NSContiguousString.Type
type(of: asNSString) // Prints _NSContiguousString.Type

How to cast Dictionary in Swift to related type?

This is what I am trying to do with the dictionary:
if let deliveries = dictionary["deliveries"] as? NSDictionary {
var castedDeliveries = [Double: Double]()
for delivery in deliveries {
if let value = delivery.value as? Double {
castedDeliveries[Double(delivery.key as! NSNumber)] = value //Could not cast value of type 'NSTaggedPointerString' (0x1a1e3af20) to 'NSNumber' (0x1a1e458b0).
}
}
settings!.deliveries = castedDeliveries
}
And this is what I try to cast, as a part of JSON response from server:
deliveries = {
2 = 0;
5 = "2.59";
7 = "3.59";
};
It doesnt work, because there is an error at commented line:
Could not cast value of type 'NSTaggedPointerString' (0x1a1e3af20) to 'NSNumber' (0x1a1e458b0).
You are trying to cast dictionary directly but instead you need to cast each key - value pair. If you want generic solution to this problem take a look at SwiftyJSON library which address JSON parsing problem for you.
Casting doens't mean data transformation from a type to another.
Your dictionary seems to be composed by Integer keys and String values.
If you want to transform in something else you ca use the map function.
let converted = deliveries.map{[Double($0) : Double($1)]}
But pay attention.
Here we are saying, iterate over the dictionary (in the $0 there is the dictionary key in the $1 there is the value) and create a new dictionary that has as a key a Double initialized at the key value and as a new value a Double initialized as the old dictionary value. The last conversion can fail, so the returned data is an optional.
As I noted in the comments, this isn't casting. You want a data conversion. You need to do that explicitly, especially in this case since it might fail.
Looking at the error, I think you really have a dictionary of [String:String] here (in NSDictionary form). That suggests the JSON is badly encoded, but such is life. Assuming that dictionary looks something like this:
let dictionary: NSDictionary = ["deliveries": ["2":"0", "5": "2.59", "7": "3.59"]]
You would convert it to [Double:Double] like this:
if let jsonDeliveries = dictionary["deliveries"] as? [String:String] {
var deliveries: [Double: Double] = [:]
for (key, value) in jsonDeliveries {
if let keyDouble = Double(key),
valueDouble = Double(value) {
deliveries[keyDouble] = valueDouble
}
}
// Use deliveries
}
This silently ignores any values that can't be converted to Double. If you would rather generate errors, use a guard let rather than an if let.

NSDictionary init with multiple objects and keys

I've been recently trying to figure out how to init a Dictionary in swift like i used to do in Objective-c:
NSMutableDictionary *loginDictionary = [[NSMutableDictionary alloc] initWithObjects:#[UsernameTextfield.text,PasswordTextfield.text] forKeys:#[#"username",#"password"];
i tried to write it in Swift :
let userDictionary = NSMutableDictionary.init(object: [usernameTextField.text,passwordTextField.text], forKey: ["username","password"])
But i get an error:
Contextual type AnyObject cannot be used with array literal.
First of all, you have to use the same method, with objects and forKeys (note the plural).
Then you need to tell the compiler what type is each object, in your case it's strings from Optional text labels, so you could do something like this:
if let name = usernameTextField.text as? String, let pass = passwordTextField.text as? String {
let userDictionary = NSMutableDictionary(objects: [name, pass], forKeys: ["username", "password"])
}
You are Passing Objects and Keys in NSMutableDictionary as below, replace your code as below.
let userDictionary = NSMutableDictionary(objects: [usernameTextField.text,passwordTextField.text], forKeys: ["username","password"])

How do I cast an NSDictionary to a swift dictionary if the NSDictionary has integers for keys

I have a dictionary stored in a Plist that has an imbedded dictionary in it. If I use strings as the keys I can use the code below to cast it to a swift dictionary:
let namesDict = NSDictionary(contentsOfFile: path!)
var names = namesDict as [String: [String : String]]
However if I try to use integers as the keys in the root dictionary I can't get it to work. The code below doesn't work and returns this error: "value type is not bridged to Objective-C"
let namesDict = NSDictionary(contentsOfFile: path!)
var names = namesDict as [Int: [String : String]]
I have tried intValue, but that doesn't work either. What am I missing?
From Apple's NSDictionary reference:
In general, a key can be any object (provided that it conforms to the
NSCopying protocol—see below), but note that when using key-value
coding the key must be a string (see Key-Value Coding Fundamentals).
Plist are Key-Value coding compliant

Resources