Swift Dictionary [String:String] to NSMutableDictionary? - swift

I am trying to create and assign a Swift Dictionary of type [String : String] at the SKSpriteNode property userData which requires an NSMutableDictionary. When I try this I get the error:
'[String : String]' is not convertible to 'NSMutableDictionary'
Can anyone point me in the right direction?
// USER DATA
var dictionary: [String : String] = Dictionary()
dictionary["Orbit"] = orbit
dictionary["Zone"] = zone
dictionary["Impact"] = impact
var foundationDictionary = dictionary as NSMutableDictionary
neoSprite.userData = foundationDictionary

There’s no built-in cast for this. But instead you can use NSMutableDictionary’s initializer that takes a dictionary:
var foundationDictionary = NSMutableDictionary(dictionary: dictionary)

One alternative answer to the answers above is to cast your dictionary to NSDictionary create a mutable copy of it and cast it to NSMutableDictionary. Too complicated, right ? Therefore I recommend creating new NSMutableDictionary from Dictionary this is just an alternative
var foundationDictionary = (dictionary as NSDictionary).mutableCopy() as! NSMutableDictionary

Can you try to replace this line:
var foundationDictionary = dictionary as NSMutableDictionary
With this code:
var foundationDictionary = NSMutableDictionary(dictionary: dictionary)

Dictionaries
Create immutable dictionary
let dictionary = ["Item 1": "description", "Item 2": "description"]
Create mutable dictionary
var dictionary = ["Item 1": "description", "Item 2": "description"]
Append new pair to dictionary
dictionary["Item 3"] = "description"

I know I am so late in answering this question and one answer is already accepted, but still to help someone.
Sometime converting swift dictionary ([String : String] or [AnyHashable : String] or any other) to Objective-C NSMutableDictionary, if there are some nested objects like array or dictionaries within that will not get mutable as so when updating the values will get error of [NSDictionaryI setObject:forKey:]: unrecognised selector sent to instance
To prevent this error one can convert the object in objective-c class.
In my case I am converting a JSON string into swift dictionary and passing it to objective c class by casting it in NSMutableDictionary as per my core requirement. This gives me an above error when I try to update the NSMutableDictionary in Objective C class.
After surfing and trying much I found that if I try to convert my JSON string in NSMutableDictionary in Objective-C class itself than it worked for me.
So try this: In aVC.swift
let Str: String = nsmanagedobject.value(forKey: "<key>") as? String {
if let mutableDict: NSMutableDictionary = ApplicationData.convertJsonObject(fromJsonString: Str) as? NSMutableDictionary {
......
<Your code to pass NSMutableDictionary to respected Objective-C class>
......
}
ApplicationData.h
+ (NSObject *)convertJsonObjectFromJsonString:(NSString *)jsonString;
ApplicationData.m
+ (NSObject *)convertJsonObjectFromJsonString:(NSString *)jsonString {
NSObject *jsonObject = nil;
if ([jsonString length] > 0) {
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
if (jsonData) {
jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];
}
}
NSLog(#"jsonObject: %#",jsonObject);
return jsonObject;
}
I hope this will help someone and save some time.

Related

Swift JSON to Dictionary<String: Any>. Then cast "Any" as NSMutableArray

I am trying to read from a JSON String and draw a graph with its data:
{"y-axis-data":{"min":0.0,"max":1000,"Step":100.0},"x-labels":[1994,2000,2005],"y-values":[20,305,143]}
I wrote a function to create a dictionary from the string:
func jsonToDictionary(jsonString: String) -> [String: Any]? {
if let jsonData: Data = jsonString.data(using: String.Encoding.utf8) {
do {
return try (JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any])!
} catch {
some bla bla
}
}
return nil
}
The return dictionary should count 3 elements inside when I pass my JSON string, and it does.
I can then change of some variables (Double) which are 0 until now and give them the values of min max and Step from the "y-axis-data" key of my dictionary, using {"min":0.0,"max":1000,"Step":100.0} as a dictionary it self. Works fine.
My trouble comes when trying to initialize other atributes:
self.my_view!.x-labels = (jsonToDictionary!["x-labels"]) as? NSMutableArray
my_view has already been initialized as UIViewCustomClass(frame: someFrame)
myview.x-labels is an NSMutableArray and it is initialized as nil. After executing that line of code it is still nill, of course myview.x-labels.count is nil. if I do it this way:
self.my_view!.x-labels = (jsonToDictionary!["x-labels"]) as! NSMutableArray
I get a warning :
Treating a forced downcast to NSMutableArray as optional will never produce nil.
It then crashes on runtime with this error:
Could not cast value of type '__NSArrayI' (0x110ed5448) to 'NSMutableArray' (0x110ed4598).
of course the exact same thing happens with "y-values"
What is the right way to do this?
It was because your json!["x-labels"] is implicitly treated as NSArray, so you somehow had to do a "double-force-cast"
// get the converted json
let j = jsonToDictionary(jsonString: dict)
// double cast
let m = (j!["x-labels"] as! NSArray).mutableCopy() as! NSMutableArray
Result:
I think, JSONSerialization class converts into Array, and then Swift cannot cast Array to NSMutableArray. You can do this (Swift4):
let array = (jsonToDictionary!["x-labels"]) as? [Int]
if array != nil {
self.my_view!.x-labels = NSMutableArray(array: array!)
}

How do I convert NSDictionary to Dictionary?

I have already updated to XCode 8 and now I need to convert my code from Swift 2 to Swift 3.
Before, when I want to convert NSDictionary to Dictionary, I just wrote the following:
let post_paramsValue = post_params as? Dictionary<String,AnyObject?>
where post_params is NSDictionary.
But now with Swift 3, I am receiving this error:
NSDictionary is not convertible to Dictionary
Why? What's changed?
Edit 1
I've also tried the following:
let post_paramsValue = post_params as Dictionary<String,Any>
But that gives this error:
Edit 2
I've also tried the following:
let post_paramsValue = post_params as Dictionary<String,Any>
Where I declare NSDictionary instead of NSDictionary!, but it doesn't work; I got this error:
Edit 3
I've also tried the following:
let post_paramsValue = post_params as Dictionary<String,Any>!
But I received this error:
NSDictionary in Objective-C has always non-optional values.
AnyObject has become Any in Swift 3.
Considering the first two "rules" NSDictionary can be bridge cast to Dictionary
let post_paramsValue = post_params as Dictionary<String,Any>
If the source NSDictionary is an optional you might use as Dictionary<String,Any>? or as? Dictionary<String,Any> or as! Dictionary<String,Any> or as Dictionary<String,Any>! depending on the actual type of the NSDictionary
For those who have a problem with NSDictionary, simply use this extension:
Swift 3.0
extension NSDictionary {
var swiftDictionary: Dictionary<String, Any> {
var swiftDictionary = Dictionary<String, Any>()
for key : Any in self.allKeys {
let stringKey = key as! String
if let keyValue = self.value(forKey: stringKey){
swiftDictionary[stringKey] = keyValue
}
}
return swiftDictionary
}
}
You just need to declare the NSDictionary properly in objc
For example: NSDictionary<NSString *, NSString*> gets translated automatically to [String: String] in swift interfaces

Convert from NSDictionary to [String:Any?]

I am using xmartlabs/Eureka to build an app with a dynamic form.
In order to fill the form I have to use setValues(values: [String: Any?]).
But I have the form values in an NSDictionary variable and I cannot cast it to [String:Any?].
Is there a way to convert an NSDictionary to [String:Any?] ?
Just an example:
if let content = data["data"] as? [String:AnyObject] {
print(content)
}
The data is a JSON object here. Use it accordingly.
Hope this helps:
let dict = NSDictionary()
var anyDict = [String: Any?]()
for (value, key) in dict {
anyDict[key as! String] = value
}

CFDictionary won't bridge to NSDictionary (Swift 2.0 / iOS9)

OK, this is a case I came across when working with CGImageSource and noticed that the toll-free-bridging between CFDictionary and NSDictionary seems to run into problems in certain cases. I've managed to construct the below example to show what I mean:
func optionalProblemDictionary() -> CFDictionary? {
let key = "key"
let value = "value"
var keyCallBacks = CFDictionaryKeyCallBacks()
var valueCallBacks = CFDictionaryValueCallBacks()
let cfDictionary = CFDictionaryCreate(kCFAllocatorDefault, UnsafeMutablePointer(unsafeAddressOf(key)), UnsafeMutablePointer(unsafeAddressOf(value)), 1, &keyCallBacks, &valueCallBacks)
return cfDictionary
}
Fairly straightforward (and a bit silly) but its a function returning and optional CFDictionary. The "fun" starts when trying to create an NSDictionary from this function:
Why won't the following work?
if let problemDictionary = optionalProblemDictionary() as? NSDictionary {
print(problemDictionary) // never enters, no warnings, compiles just fine
}
While this works fine?
if let cfDictionary = optionalProblemDictionary() {
let problemDictionary = cfDictionary as NSDictionary
print(problemDictionary)
}
XCode 7.0 (7A220)
The reason seems to be that the function returns an optional
CFDictionary? and that can not be cast to a (non-optional)
NSDictionary.
Here is a simpler example demonstrating the same problem with CFString vs NSString:
let cfString = "foobar" as CFString?
if let s1 = cfString as? NSString {
print("s1 = \(s1)") // not executed
}
(The question remains why this does not give a compiler error or
at least a compiler warning because this optional cast can
never succeed.)
But a casting to an optional NSString? works:
if let s2 = cfString as NSString? {
print("s2 = \(s2)") // prints "s2 = foobar"
}
In your case, if you change the "problematic case" to
if let problemDictionary = cfDict as NSDictionary? {
print(problemDictionary)
}
then the if-block is executed.
Note that your method to build a CFDictionary in Swift is not correct
and actually caused program crashes in my test. One reason is that
the dictionary callbacks are set to empty structures.
Another problem is that unsafeAddressOf(key) bridges the Swift
string to an NSString which can be deallocated immediately.
I don't know what the best method is to build a CFDictionary in Swift,
but this worked in my test:
func optionalProblemDictionary() -> CFDictionary? {
let key = "key" as NSString
let value = "value" as NSString
var keys = [ unsafeAddressOf(key) ]
var values = [ unsafeAddressOf(value) ]
var keyCallBacks = kCFTypeDictionaryKeyCallBacks
var valueCallBacks = kCFTypeDictionaryValueCallBacks
let cfDictionary = CFDictionaryCreate(kCFAllocatorDefault, &keys, &values, 1, &keyCallBacks, &valueCallBacks)
return cfDictionary
}

Why is Swift Dictionary not bridgeable?

Dictionary is a bridged type, why is it that I can switch from Swift dictionary to NSDictionary but not the other way around? (Compile Error: NSDictionary is not convertible to 'Dictionary')
According to Apple's doc:
All NSDictionary objects can be bridged to Swift dictionaries, so the Swift compiler replaces the NSDictionary class with [NSObject: AnyObject] when it imports Objective-C APIs.
import Foundation
var swiftDict = ["0": "zero"]
let nsDict:NSDictionary = swiftDict
let backToSwiftDict:Dictionary = nsDict
This is correct but you have to perform a type safe cast from NSDictionary to a Dictionary
var swiftDict = ["0": "zero"]
let nsDict: NSDictionary = swiftDict
let backToSwiftDict: Dictionary = nsDict as Dictionary
... or you can cast it back into a dictionary with type-safe fields ...
var swiftDict = ["0": "zero"]
let nsDict:NSDictionary = swiftDict
let backToSwiftDict = nsDict as! [String:String]