Help with NSKeyedUnarchiver NSKeyedArciver

I finnished a course that used

let unarchivedData = UserDefaults.standard.object(forKey: key) as ? NSData
return NSKeyedUnarchiver.unarchivedObject(with: unarchivedData) as? [[Task]]

And

return NSKeyedUnarchiver.archivedData(withRootObject: tasks) as NSData

THey don't seem to work anymore, how should they be written instead

1 Like

Consider:

  • Please post the actual error your got, because it’s hard to diagnose an error when without actually seeing it.

  • Please format your code as code blocks (use the </> icon in the editor), because that’ll make your posts easier to read.

  • Do these code snippets represent your actual code? My specific concern is your second code snippet, which shows NSKeyedUnarchiver but which is obviously must be NSKeyedArchiver because that’s the only thing that makes any sense in this context. Is that a problem with your post? Or with your code? Or with the course you’re following?

  • Whatever course you’re following seems to be somewhat out of date. Specifically:

    • Most Swift projects these days use Swift’s built in Codable rather than the older NSCoding. There are circumstances where NSCoding is the right option, but if you’re just getting started then Codable is much easier.

    • The references to NSData seem weird. You should not need to bounce through NSData in this situation. For example, unarchiveObject(with:) takes a Data parameter, not an NSData.

    • It doesn’t use the latest APIs. For example, in this situation I’d use UserDefaults.data(forKey:) rather than UserDefaults.object(forKey:).

    • It doesn’t use NSSecureCoding, which is strongly recommended in modern code. For more background on this, see WWDC 2018 Session 222 Data You Can Trust.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

Thank you. I "found" in xcode, that I can use an older version of swift. That solved the problem of making the app run. Other than that, you gave me some helpful pointers on how to proceed when continuing the app.

What you say About Swift versions is both encouraging and discouraging

  1. There is a way to read existing NSCoding files (written by apps over which one has no control)
  2. Maybe the evolving implementation in Xcode 11.2's SDKs is broken (and will get fixed in due time)
    I myself have tried everything (rewriting some legacy and working Obj-C code in Swift), but no cigar. Crashes or nils, and no propagation to init?(coder:NSCoder)'s at deeper levels. Not even decoding of the root object.
    I've read all kinds of comments on various developer discussions, where people have trouble unarchiving NSCoding data, especially with further depreciations in Catalina.

I’m not sure who your post was addressed to. Regardless, it’s clear that you do have a significant problem. Unfortunately there’s not enough details here to help with that problem. Can you boil this down into a small, concrete example?

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

@eskimo: Sorry for my intervention being slightly off-topic, but I had been reading around on different forums about NSKeyedArchiving with NSCoding, and miwashi’s remark that compiling with a previous version of Swift would help, gave me some hope. However, the Swift version doesn’t seem to make a difference (apart from depreciated methods and the introduction of NSSecureCoding).

My particular problem is that my clients have a bunch of legacy files written by an Obj-C document-based app of mine, that makes use (in MyDocument)

of -dataOfType:error: ( [NSKeyedArchiver archivedDataWIthRootObject:docModel] ),

and of -readFromData:ofType:error: ( docModel = [NSKeyedUnarchiver unarchiveObjectWithData:data] ).

BTW, in it I was also using custom encoding/decoding -encodeWithCoder: / -initWithCoder

The Swift(UI) app I am developing now (and which will eventually replace the legacy Obj-C app) should continue to support the old archiving format (a binary plist) till I am ready to publish the new app. (Therefore switching from NSCoding to Codable if no option for me at this time).

Reimplementing archiving/unarchiving in Swift returned many errors, some caused by illegal format of the archived file, whatever I tried (further complicated by some depreciations since macOS 10.14). When archiving/unarchiving started to work in Swift on minimal test cases, the legacy Obj-C program also couldn’t read the files the Swift version wrote, and vice versa.

Some errors also were plainly my fault, because I didn’t use the correct type in the custom encode/coding operations.

After studying the data format for the bplist (plutil is my friend!) I found out that the class names that the Swift version uses are prefixed by the Module name (in this case the app name).

Eventually the trick seems to be to declare all the classes that need archiving as
@objc(className) class className: NSObject, NSSecureCoding,
which effectively removes the module prefix from the className appearing in the bplist data.

Some error message hat I got in my quest, referred to the DocModel class not being found (because they were prefixed by Swift by the annoying module name). In the end, this error message (cryptic as it was), referred to the solution.

It would save a lot of developers much time if the documentation would emphasize this difference in class naming/visibility between Obj-C and Swift.

I appreciate that recent Swift-based documentation, which is being entirely overhauled, doesn’t refer to tutorials of programming guides, like in the past. I hope Apple fixes that.

I am also still looking for a tutorial explaining at least the concepts of the inner workings of keyed(un)archiving.

Thanks, eskimo, for responding to my post. I hope it is of help to some readers. I may repost the above on forums where it may be more on-topic.