How to encode [Character: Int] property using NSCoder in Swift 3?
import UIKit
class Foo: NSObject, NSCoding {
var cx: [Character : Int]
init(cx: [Character : Int]) {
self.cx = cx
}
// MARK: - <NSCoding>
required convenience init(coder aDecoder: NSCoder) {
let cx = aDecoder.decodeObject(forKey: "cxKey") as! [Character : Int]
self.init(cx: cx)
}
func encode(with aCoder: NSCoder) {
aCoder.encode(cx, forKey: "cxKey")
}
}
vocation:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var foo = Foo(cx: ["C": 5, "X": 6])
let encodedData = NSKeyedArchiver.archivedData(withRootObject: foo)
print("encodedData: \(encodedData))")
if let foo1 = NSKeyedUnarchiver.unarchiveObject(with: encodedData) as? Foo {
print("cx = ", foo1.cx)
} else{
print("There is an issue")
}
}
}
Xcode throws an error: *** Application terminated due to unmapped "NSInvalidArgumentException", reason: '- [_ SwiftValue encodeWithCoder:]: unrecognized selector posted to instance
source to share
Cause
This is because the keys Character
in cx
will be marked as _SwiftValue
objects to be sent encodeWithCoder:
, resulting in an unrecognized exception being thrown.
See the comment at the top of SwiftValue.h :
This implements the Objective-C class, which is used to wrap Swift values that have been bound to Objective-C objects without special handling. The class is opaque to user code, but
NSObject
- andNSCopying
- is matched and understood at runtime by Swift to dynamically cast back to the contained type.
Decision
If you can change the type cx
to [String : Int]
, everything will work out of the box (no pun intended).
Otherwise, you will need to convert cx
to Foo.encode(with:)
to something that can be encoded (for example [String : Int]
, for example) and vice versa in the decode initializer.
See How to encode a character using NSCoder in swift? and How to encode an enum using NSCoder in swift? for some code.
source to share