Reputation: 646
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")
}
}
calling:
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: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance
Upvotes: 3
Views: 382
Reputation: 1227
Reason
That is because the Character
-typed keys in cx
will be boxed as _SwiftValue
objects which will be sent encodeWithCoder:
which leads to the unrecognized selector exception.
See the comment at the top of SwiftValue.h:
This implements the Objective-C class that is used to carry Swift values that have been bridged to Objective-C objects without special handling. The class is opaque to user code, but is
NSObject
- andNSCopying
- conforming and is understood by the Swift runtime for dynamic casting back to the contained type.
Solution
If you can change the type of cx
to [String : Int]
, everything will work out of the box (no pun intended).
Otherwise you will have to convert cx
in Foo.encode(with:)
to something that can be encoded (like [String : Int]
, for instance) and vice versa in the decoding initializer.
See How do I encode Character using NSCoder in swift? and How do I encode enum using NSCoder in swift? for some code.
Upvotes: 2