Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I convert an NSDictionary to a Swift Dictionary<String, NSObject>?

I am rewriting an Objective-C class in Swift to get a feel for the language. In Objective-C my class included the following method:

- (id) initWithCoder:(NSCoder *)aDecoder
{
    return [self initWithActionName:[aDecoder decodeObjectOfClass:[NSString class] forKey:@"actionName"]
                            payload:[aDecoder decodeObjectOfClass:[NSDictionary class] forKey:@"payload"]
                          timestamp:[aDecoder decodeObjectOfClass:[NSDate class] forKey:@"timestamp"]];
}

After some trial and error with the compiler, I managed to rewrite it like this:

convenience init(aDecoder: NSCoder) {
    let actionName = aDecoder.decodeObjectOfClass(NSString.self, forKey: "actionName") as NSString
    let payload = aDecoder.decodeObjectOfClass(NSDictionary.self, forKey: "payload") as NSDictionary
    let timestamp = aDecoder.decodeObjectOfClass(NSDate.self, forKey: "timestamp") as NSDate
    self.init(actionName: actionName, payload: payload, timestamp: timestamp)
}

However, this still gives me an error on the last line: “Could not find an overload for init that accepts the supplied arguments.” The method signature of init is

init(actionName: String, payload: Dictionary<String, NSObject>, timestamp: NSDate)

so I assume the problem is that I am trying to pass an NSDictionary instead of a Dictionary<String, NSObject>. How can I convert between these two not-quite-equivalent types? Should I just be using NSDictionary for all of the code that has to interact with other Objective-C components?

like image 593
bdesham Avatar asked Jun 07 '14 16:06

bdesham


2 Answers

Decode your dictionary as Dictionary<String, NSObject> instead of NSDictionary.

let payload = aDecoder.decodeObjectOfClass(NSDictionary.self, forKey: "payload") as! Dictionary<String, NSObject>

Since you are using Cocoa Touch to serialize and deserialize, they are serialized as NSDictionary, but you can cast it to a Swift Dictionary<,>, as per WWDC 2014 Intermediary Swift and Advanced Swift videos.

You can also decode as NSDictionary and then explicitly cast the object like so:

self.init(actionName: actionName, payload: payload as! Dictionary<String, NSObject>, timestamp: timestamp)

Basically, you are playing here with covariance/contravariance.

like image 200
Léo Natan Avatar answered Nov 09 '22 15:11

Léo Natan


Are you sure that that's the correct declaration for init? The argument order is different than the one you're calling in your Objective-C code.

So, if you want a precise reimplementation, the last call should probably be self.init(actionName: actionName, timestamp: timestamp, payload: payload). Calling the initializer with the wrong order of arguments would result in exactly the error message you're getting.

like image 1
Lukas Avatar answered Nov 09 '22 15:11

Lukas