Reputation: 4836
On iOS version lower than 11 the throwing archivedData(withRootObject:requiringSecureCoding:)
is unavailable, so I have tried to do the equivalent on versions less than iOS 11:
let archiveData: NSData
if #available(iOS 11.0, *) {
archiveData = try NSKeyedArchiver.archivedData(
withRootObject: rootObject,
requiringSecureCoding: true
) as NSData
} else {
NSKeyedArchiver.archivedData(withRootObject: userActivity)
let mutableData = NSMutableData()
let archiver = NSKeyedArchiver(forWritingWith: mutableData)
archiver.requiresSecureCoding = true
archiver.encode(rootObject, forKey: NSKeyedArchiveRootObjectKey)
if let error = archiver.error {
throw error
}
archiver.finishEncoding()
archiveData = mutableData
}
However, when the rootObject
calls NSCoder.failWithError(_:)
in the encode(with:)
function an NSInvalidUnarchiveOperationException
exception is raised.
If I subclass NSKeyedArchiver
as such:
final class KeyedArchiver: NSKeyedArchiver {
override var decodingFailurePolicy: NSCoder.DecodingFailurePolicy {
return .setErrorAndReturn
}
}
It instead raises an NSInternalInconsistencyException
exception with the message Attempting to set decode error on throwing NSCoder
.
Is there a way to do this kind of archiving without throwing an exception, short of writing an Objective-C function to catch the exception and throwing it as an error?
Upvotes: 0
Views: 591
Reputation: 29938
The reason you're still getting the exception at encode time is that the .decodingFailurePolicy
is effective only when decoding (i.e., unarchiving via NSKeyedUnarchiver
), not encoding. Any object that calls .failWithError(_:)
on encode will still produce the exception.
Calling .failWithError(_:)
at encode-time is relatively rare: usually, once you have a fully constructed object at runtime, it's not terribly likely that it should be in a state that's not encodable. There are of course cases where this is possible, so you really have two options:
NSKeyedArchiver
via an Objective-C function which can catch the exception (and ideally throw
an Error
containing that exception, like the newer NSKeyedArchiver
API does on your behalf)Based on your comment above, option 2 is your best bet.
As an aside, you can shorten up your fallback code to avoid having to construct an intermediate NSMutableData
instance:
let archiver = NSKeyedArchiver()
archiver.encode(rootObject, forKey: NSKeyedArchiveRootObjectKey)
archiveData = archiver.encodedData
The default initializer on NSKeyedArchiver
constructs a new archiver with an internal mutable data instance to use, and NSKeyedArchiver.encodedData
property automatically calls -finishEncoding
on your behalf.
Upvotes: 1