I'm using ARKit and GameKitMatches so I can't use Codable (afaik) because MCPeerID
as well as ARWorldMap
aren't codable, to get that out of the way first.
So I'm using NSCoding and NSSecureCoding but for some reason I always catch the error:
The data couldn’t be read because it isn’t in the correct format.
...even tho I literally just created it.
I also tried to use NSKeyedUnarchiver.unarchivedObject(ofClasses: classes
but that threw an unexpected-nil in my init.
Here's a playground I made showing the problem:
class CodingData: NSObject, NSCoding, NSSecureCoding {
static var supportsSecureCoding = true
var dic: [String: Int]!
var i: Int!
func encode(with coder: NSCoder) {
coder.encode(i, forKey: "i")
coder.encode(dic, forKey: "dic")
}
required convenience init?(coder: NSCoder) {
let anInt = coder.decodeObject(forKey: "i") as! Int
let anDic = coder.decodeObject(forKey: "dic") as! [String: Int]
self.init(dic: anDic, i: anInt)
}
init(dic: [String: Int], i: Int){
self.dic = dic
self.i = i
}
}
do{
let test = CodingData(dic: [:], i: 0)
//let classes = [NSDictionary.self, NSNumber.self]
let testData = try NSKeyedArchiver.archivedData(withRootObject: test, requiringSecureCoding: true)
let emptyDic = try NSKeyedUnarchiver.unarchivedObject(ofClass: CodingData.self, from: testData)
// Error here ^^^^^^
}catch{
error.localizedDescription
}
BTW, not sure if it matters but while trying to debug the coder
in the init it always said (prob. just a bug):
error: <EXPR>:1:1: error: non-nominal type '$__lldb_context' (aka 'Self') cannot be extended
extension $__lldb_context {
^ ~~~~~~~~~~~~~~~
error: <EXPR>:19:27: error: value of type 'Self' has no member '$__lldb_wrapped_expr_28'
$__lldb_injected_self.$__lldb_wrapped_expr_28(
~~~~~~~~~~~~~~~~~~~~~ ^~~~~~~~~~~~~~~~~~~~~~~
You've made a great effort, but there is so much wrong with your code that I think the easiest way to answer is just to correct it:
class CodingData: NSObject, NSSecureCoding {
static var supportsSecureCoding = true
var dic: [String: Int]
var i: Int
func encode(with coder: NSCoder) {
coder.encode(i as NSNumber, forKey: "i")
coder.encode(dic as NSDictionary, forKey: "dic")
}
required init?(coder: NSCoder) {
let anInt = coder.decodeObject(of: NSNumber.self, forKey: "i")
let anDic = coder.decodeObject(of: NSDictionary.self, forKey: "dic")
self.dic = anDic as! [String:Int]
self.i = anInt as! Int
}
init(dic: [String: Int], i: Int){
self.dic = dic
self.i = i
}
}
It will work now:
let test = CodingData(dic: [:], i: 0)
let testData = try NSKeyedArchiver.archivedData(withRootObject: test, requiringSecureCoding: true)
let emptyDic = try NSKeyedUnarchiver.unarchivedObject(ofClass: CodingData.self, from: testData)
print(emptyDic?.dic as Any, emptyDic?.i as Any) // Optional([:]) Optional(0)
So your four chief mistakes were:
Do not declare an instance property as an implicitly unwrapped Optional (!
)
init?(coder:)
is not a convenience
initializer
To do secure coding, you must decode each encoded property using secure coding (decodeObject(of:forKey:)
)
Only NSSecureCoding types can be encoded using secure coding, so Swift types must be cast to their Objective-C equivalents