NSKeyedUnarchiver unarchiveObjectWithData returning nil
Asked Answered
S

1

7

I have a class which serializes and deserializes with NSCoder/NSKeyedArchiver and NSKeyedUnarchiver. I have unit tests that are designed to check for my handling of various errors inside my serialization format (which is not a simple archive but contains archives).

However, since I upgraded to Swift 2/iOS 9, the tests are displaying some unusual behaviour. Previously, the test for invalid archives failed as deserializing an invalid archive threw an Objective-C exception, as the docs state, that crashed the program as Swift cannot catch them. This is fine, I intended to fix the test at some point in the future.

Now, the test passes. When fed my random or deterministic garbage, I instead receive nil back from unarchiveObjectWithData rather than an exception. I've checked the docs for this method and there's no behaviour change listed.

Frankly, I find this behaviour change to be extremely suspicious as there's no mention anywhere of how or why this change occurred. My previously failing unit tests are just now passing with no apparent reason why.

So is this the new expected behaviour (that it returns nil)? If not, how can I get the actual expected behaviour (the Obj-C exception) rather than the nil for an invalid archive?

Steep answered 5/10, 2015 at 9:21 Comment(0)
I
4

Swift2/iOS9 introduces undocumented throws class method in NSKeyedUnarchiver:

extension NSKeyedUnarchiver {
    @warn_unused_result
    public class func unarchiveTopLevelObjectWithData(data: NSData) throws -> AnyObject?
}

But it doesn't seems to work if the data is completely wrong format: It returns nil without error.

let dat = "test".dataUsingEncoding(NSUTF8StringEncoding)!
try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(dat) // -> `nil`

Errors are thrown only when the unarchiver found some invalid entities (e.g. unknown class name) while decoding. I think it's a kind of bug or it's accidentally released with incomplete implementations.

Anyway, if you want Objective-C exception, you can just construct NSKeyedUnarchiver:

let dat = "test".dataUsingEncoding(NSUTF8StringEncoding)!
let unarchiver = NSKeyedUnarchiver(forReadingWithData: dat) // -> throws Objective-C exception

But, there is no throws initializer... yet?

Insinuate answered 5/10, 2015 at 11:43 Comment(1)
Thanks for this. I think I will construct the NSKeyedArchiver and manually handle that ObjC exception for now, so that I am not in this weird un-documented half-implemented step.Steep

© 2022 - 2024 — McMap. All rights reserved.