Since
NSKeyedUnarchiver.unarchiveTopLevelObjectWithData
has been deprecated as of macOS 14.0 I am trying to load legacy NSCoder archives now using NSSecureCoding set in the class and using:
coder.unarchivedObject(ofClasses classes: [AnyClass], from data: Data)
to load the archive. The archive loads and most of the class members can be extracted. However, Swift Arrays always come out nil even though
coder.containsValue(forKey:)
returns true. The arrays were encoded with:
var pageUsesClearBackground: Array<Bool> = [true, true, true, true]
coder.encode(pageUsesClearBackground, forKey: "CB_KEY")
Using:
pageUsesClearBackground = coder.decodeObject(forKey: "CB_KEY") as? Array<Bool>
Brings back an array with 0 values, even though if I use the non NSSecureCode version, the Array is populated.
pageUsesClearBackground [Bool] 0 values
I suspect this has something to do with Arrays in Swift are a struct and do not conform to NSCoding. Odd that it worked encoding and decoding them for years. Any idea how to retrieve class members that are Arrays from an archive made before NSSecureCoding?
Apparently, NSSecureCoding is so secure you can't even get the data back.
Although the function you're actually using (unarchivedObject(ofClasses:from:)
) has a nice Swifty name, it's still an Obj-C class method, and Obj-C doesn't understand Swift arrays directly.
When you archive a Swift array in the NSCoder
world, the object is converted automatically to a true NSArray
.
Putting all of that together, even though you're invoking a Swift version of the function, you still need to specify the true Obj-C class. The correct form of this call would be:
NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self], from data: data)
(Note that the .self
is required to get the class object. Also, this is a class function, so you apply it to the NSKeyedUnarchiver
class itself, not to an instance of the class.)
Or, if that's the only class you need to specify, you can use the slightly simpler:
NSKeyedUnarchiver.unarchivedObject(ofClass: NSArray.self, from data: data)
I realize this isn't ideal (or obvious), because the automatic bridging and conversions of Obj-C arrays to/from Swift arrays is mostly invisible, but this is one sharp corner that you need to deal with yourself.