2

I am trying to archive a dictionary using the following code and getting error. Obviously there is something is wrong in the response dictionary I am passing but the trace doesn't tell anything. How do I nail down the root cause?

do {
    let data = try NSKeyedArchiver.archivedData(withRootObject: response, requiringSecureCoding: false)
} catch {
    NSLog("Unable to archive  \(error)")
}   

Error:

2019-07-17 19:08:38.978954+0530 MyApp-Swift[372:16845] Unable to archive  Error Domain=NSCocoaErrorDomain Code=4866 "Caught exception during archival: -[__SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x282995e00
    (
    0   CoreFoundation                      0x00000001ca149ebc  + 252
    1   libobjc.A.dylib                     0x00000001c9319a50 objc_exception_throw + 56
    2   CoreFoundation                      0x00000001ca062b14  + 0
    3   CoreFoundation                      0x00000001ca14f7bc  + 1412
    4   CoreFoundation                      0x00000001ca15146c _CF_forwarding_prep_0 + 92
    5   Foundation                          0x00000001cabc4aa8  + 1276
    6   Foundation                          0x00000001caadc3b4  + 444
    7   Foundation                          0x00000001cab08ed8  + 964
    8   Foundation                          0x00000001cabc4aa8  + 1276
    9   Foundation                          0x00000001caadc3b4  + 444
    10  Foundation                          0x0000000
rmaddy
  • 314,917
  • 42
  • 532
  • 579
Deepak Sharma
  • 5,577
  • 7
  • 55
  • 131
  • 2
    Why are you even using `NSKeyedArchiver` in Swift? For most scenarios, using `Codable` and `PropertyListEncoder`/`JSONEncoder` is a much better alternative. Is `response` a `Dictionary` or `NSDictionary`? What is its exact type? – Dávid Pásztor Jul 17 '19 at 14:27
  • I am porting code from Objective C, so I didn't know about those options. Will look into alternatives. Btw, what's exactly wrong with NSKeyedArchiver in Swift? What advantages do alternatives offer? – Deepak Sharma Jul 17 '19 at 15:58
  • 1
    What's wrong with NSKeyedArchiver in Swift is what I just said in my answer. None of the Swift types are NSCoding adopters. Some Swift types can be bridged to Objective-C types that are NSCoding adopters, but many cannot be. – matt Jul 17 '19 at 15:58
  • Ok, so if I already have a file saved with data generated from NSKeyedArchiver.archivedData(withRootObject:), how do I make it backward compatible with codable property list? – Deepak Sharma Jul 17 '19 at 16:00
  • You can't. You would need to include in your app, if you wanted to do that, some form of _migration_. – matt Jul 17 '19 at 16:03
  • Lets say the previous version of the app had many files written from data generated using NSKeyedArchiver (for simplicity, they only contain Dates), is there any need to rewrite those files for migration? – Deepak Sharma Jul 17 '19 at 16:06
  • I see Codable & PropertyListEncoder still doesn't supports enums. Is JSONEncoder the choice? – Deepak Sharma Jul 17 '19 at 16:10
  • That's a matter of opinion, but I would say no there isn't. My Zotz app, for example, still uses NSKeyedArchiver in some places. But in general I have switched to Codable throughout my apps. – matt Jul 17 '19 at 16:12
  • I feel this has gotten off the track. The crash is explained. If you have a new question, ask a new question. Thanks. – matt Jul 17 '19 at 16:15

2 Answers2

4

NSKeyedArchiver is for Cocoa Objective C objects only. Only NSCoding adopters can be archived with an NSKeyedArchiver. You would need an NSDictionary containing only NSCoding types. Well, you don’t have one.

matt
  • 515,959
  • 87
  • 875
  • 1,141
1

Just complementing matt's answer, which is absolutely right.

In my case, I was using a Struct that conformed to Codable and, to make it work, I just encoded it to a Dictionary [String: Any].

Applying it to your code, it would be something like this:

do {
    let encodedResponse = (try? JSONSerialization.jsonObject(with: JSONEncoder().encode(response))) as? [String: Any] ?? [:]
    let data = try NSKeyedArchiver.archivedData(withRootObject: encodedResponse, requiringSecureCoding: false)
} catch {
    NSLog("Unable to archive  \(error)")
}
Luca Schifino
  • 61
  • 1
  • 1