Skip to content

Commit 38f68fe

Browse files
committed
Parity: NSCoder miscellanea
- Added missing API: `DecodingFailurePolicy.raiseException`. It is now the default like on Darwin. - Match Darwin behavior while decoding if you decode without raising exceptions. - `decodeTopLevel…` variants are noted unavailable on swift-corelibs-foundation, with rationale and replacement directions. Swift doesn’t have exceptions, and it used to define `.setErrorAndReturn` as the defaults. However, until very recently `failWithError(_:)` was unimplemented and trapped with a fatal error, effectively meaning that all coding failures were behaving as if `.raiseException` was set in a Darwin Swift-only app: by crashing. Since code written to 4.x-5.0 may be relying on this behavior, we make it explicit and consistent with Darwin: there is now a `.raiseException` constant, it’s the default, and it fails with a fatal error in `failWithError(_:)`. The new secure initializers (at swiftlang#2102) set `.setErrorAndReturn` just like on Darwin, and thus won’t raise and instead just thrown an error. Tests will be added.
1 parent bd23c5a commit 38f68fe

File tree

4 files changed

+149
-68
lines changed

4 files changed

+149
-68
lines changed

Foundation/NSCoder.swift

+54-15
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ extension NSCoder {
1414
/// supports exceptions here, and there may be other approaches supported
1515
/// in the future, so its included for completeness.
1616
public enum DecodingFailurePolicy : Int {
17+
case raiseException
1718
case setErrorAndReturn
1819
}
1920
}
@@ -185,7 +186,7 @@ open class NSCoder : NSObject {
185186
}
186187

187188
open func decodeObject<DecodedObjectType: NSCoding>(of cls: DecodedObjectType.Type, forKey key: String) -> DecodedObjectType? where DecodedObjectType: NSObject {
188-
NSUnimplemented()
189+
return decodeObject(of: [cls], forKey: key) as? DecodedObjectType
189190
}
190191

191192
/// Decodes an object for the key, restricted to the specified `classes`.
@@ -199,19 +200,42 @@ open class NSCoder : NSObject {
199200
/// - key: The code key.
200201
/// - Returns: The decoded object.
201202
open func decodeObject(of classes: [AnyClass]?, forKey key: String) -> Any? {
202-
NSUnimplemented()
203+
guard allowsKeyedCoding else {
204+
fatalError("This method is only implemented for coders which allowKeyedCoding.")
205+
}
206+
207+
if requiresSecureCoding {
208+
NSRequiresConcreteImplementation()
209+
} else {
210+
return decodeObject(forKey: key)
211+
}
203212
}
204213

214+
// ----- Top-level decoding -----
215+
216+
/*
217+
On Darwin, if a coder's .decodingFailurePolicy is .raiseException, ObjC exceptions are used to interrupt execution of your init(coder:) initializer when a failure occurs during decoding.
218+
219+
The …TopLevel… methods below are documented to:
220+
- interrupt your init(coder:) execution and immediately unwind the stack if a decoding error occurs with your .decodingFailurePolicy set to .raiseException, and
221+
- still return with a thrown error, and continue program execution.
222+
223+
This isn't possible in swift-corelibs-foundation, where ObjC exceptions are unavailable; either program execution is immediately interrupted, or an error is thrown, but not both. To port your code to swift-corelibs-foundations, use the replacements noted below.
224+
*/
225+
226+
@available(*, unavailable, message: "The behavior of this method isn't supported in swift-corelibs-foundation. You can use decodeObject() instead on all platforms. If you would like an error to be thrown, use .decodingFailurePolicy = .setErrorAndReturn, and check the .error property.", renamed: "decodeObject()")
205227
open func decodeTopLevelObject() throws -> Any? {
206-
NSUnimplemented()
228+
NSUnsupported()
207229
}
208230

231+
@available(*, unavailable, message: "The behavior of this method isn't supported in swift-corelibs-foundation. You can use decodeObject(forKey:) instead on all platforms. If you would like an error to be thrown, use .decodingFailurePolicy = .setErrorAndReturn, and check the .error property.", renamed: "decodeObject(forKey:)")
209232
open func decodeTopLevelObject(forKey key: String) throws -> Any? {
210-
NSUnimplemented()
233+
NSUnsupported()
211234
}
212235

236+
@available(*, unavailable, message: "The behavior of this method isn't supported in swift-corelibs-foundation. You can use decodeObject(of:forKey:) instead on all platforms. If you would like an error to be thrown, use .decodingFailurePolicy = .setErrorAndReturn, and check the .error property.", renamed: "decodeObject(of:forKey:)")
213237
open func decodeTopLevelObject<DecodedObjectType: NSCoding>(of cls: DecodedObjectType.Type, forKey key: String) throws -> DecodedObjectType? where DecodedObjectType: NSObject {
214-
NSUnimplemented()
238+
NSUnsupported()
215239
}
216240

217241
/// Decodes an top-level object for the key, restricted to the specified
@@ -225,10 +249,13 @@ open class NSCoder : NSObject {
225249
/// - classes: An array of the expected classes.
226250
/// - key: The code key.
227251
/// - Returns: The decoded object.
252+
@available(*, unavailable, message: "The behavior of this method isn't supported in swift-corelibs-foundation. You can use decodeObject(of:forKey:) instead on all platforms. If you would like an error to be thrown, use .decodingFailurePolicy = .setErrorAndReturn, and check the .error property.", renamed: "decodeObject(of:forKey:)")
228253
open func decodeTopLevelObject(of classes: [AnyClass], forKey key: String) throws -> Any? {
229-
NSUnimplemented()
254+
NSUnsupported()
230255
}
231256

257+
// -----
258+
232259
/// Encodes `object`.
233260
///
234261
/// `NSCoder`’s implementation simply invokes `encodeValue(ofObjCType:at:)`
@@ -400,15 +427,15 @@ open class NSCoder : NSObject {
400427
///
401428
/// - Parameter aPropertyList: The property list to encode.
402429
open func encodePropertyList(_ aPropertyList: Any) {
403-
NSUnimplemented()
430+
NSRequiresConcreteImplementation()
404431
}
405432

406433
/// Decodes a property list that was previously encoded with
407434
/// `encodePropertyList(_:)`.
408435
///
409436
/// - Returns: The decoded property list.
410437
open func decodePropertyList() -> Any? {
411-
NSUnimplemented()
438+
NSRequiresConcreteImplementation()
412439
}
413440

414441
/// The system version in effect for the archive.
@@ -640,11 +667,17 @@ open class NSCoder : NSObject {
640667
}
641668
*/
642669

643-
/// - Experiment: This method does not exist in the Darwin Foundation.
670+
/// - Experiment: This method does not exist in Darwin Foundation. Replaces decodeBytes(forKey:).
671+
@available(swift, deprecated: 9999, renamed: "withDecodedUnsafeBytes(forKey:body:)")
644672
open func withDecodedUnsafeBufferPointer<ResultType>(forKey key: String, body: (UnsafeBufferPointer<UInt8>?) throws -> ResultType) rethrows -> ResultType {
645673
NSRequiresConcreteImplementation()
646674
}
647-
675+
676+
/// - Experiment: This method does not exist in Darwin Foundation. Replaces decodeBytes(forKey:).
677+
open func withDecodedUnsafeBytes<ResultType>(forKey key: String, body: (UnsafeRawBufferPointer?) throws -> ResultType) rethrows -> ResultType {
678+
NSRequiresConcreteImplementation()
679+
}
680+
648681
/// Encodes a given integer number and associates it with a given key.
649682
///
650683
/// Subclasses must override this method if they perform keyed coding.
@@ -683,7 +716,7 @@ open class NSCoder : NSObject {
683716
/// - Parameter key: The coder key.
684717
/// - Returns: A decoded object containing a property list.
685718
open func decodePropertyList(forKey key: String) -> Any? {
686-
NSUnimplemented()
719+
NSRequiresConcreteImplementation()
687720
}
688721

689722
/// The array of coded classes allowed for secure coding.
@@ -695,7 +728,7 @@ open class NSCoder : NSObject {
695728
/// - Experiment: This is a draft API currently under consideration for
696729
/// official import into Foundation.
697730
open var allowedClasses: [AnyClass]? {
698-
NSUnimplemented()
731+
NSRequiresConcreteImplementation()
699732
}
700733

701734
open func failWithError(_ error: Error) {
@@ -704,11 +737,17 @@ open class NSCoder : NSObject {
704737
} else {
705738
NSLog("*** NSKeyedUnarchiver.init: decoding error")
706739
}
740+
741+
guard decodingFailurePolicy != .raiseException else {
742+
fatalError("There was a decoding error and the decoding failure policy is .raiseException. Aborting.")
743+
}
744+
745+
_hasFailed = true
707746
}
708747

709-
open var decodingFailurePolicy: NSCoder.DecodingFailurePolicy {
710-
return .setErrorAndReturn
711-
}
748+
internal private(set) var _hasFailed = false
749+
750+
open var decodingFailurePolicy: NSCoder.DecodingFailurePolicy = .raiseException
712751

713752
open var error: Error? {
714753
NSRequiresConcreteImplementation()

0 commit comments

Comments
 (0)