michaelkirk
(Michael Kirk)
1
Using the NSUnderlyingError pattern caused an unexpected crash for me.
In Swift I had defined an Error like this:
@objc
public class WrappingError: NSObject, CustomNSError {
public let someDetails: String
public let someMoreDetails: UInt32
@objc
public static let kSomeDetailsKey = "kSomeDetailsKey"
@objc
public static let kSomeMoreDetailsKey = "kSomeMoreDetailsKey"
public let underlyingError: Error
init(someDetails: String, someMoreDetails: UInt32, underlyingError: Error) {
self.someDetails = someDetails
self.someMoreDetails = someMoreDetails
self.underlyingError = underlyingError
}
public var errorUserInfo: [String: Any] {
return [
type(of: self).someDetailsKey: someDetails,
type(of: self).someMoreDetailsKey: someMoreDetails,
NSUnderlyingErrorKey: underlyingError
]
}
}
The crash occurred in objc in some code like this:
NSError *error;
[SomeSwiftClass methodThatThrowsAWrappingError:&error]
if ([error.domain isEqualTo:@"MyModule.WrappingError"]) {
NSError *underlyingError = error.userInfo[NSUnderlyingErrorKey];
// !!! "unknown selector" crash on next line, because `underlyingError`
// is a Swift Error value, not an NSError
if ([underlyingError.domain isEqualTo:@"FooError"]) {
[self handleFooError];
}
}
Am I correct in concluding that the conversion from a Swift Error to an NSError occurs as part of the throwing process, and thus if I want to do something like the above, the right thing would be to make a change to WrappingError like:
public var errorUserInfo: [String: Any] {
return [
type(of: self).someDetailsKey: someDetails,
type(of: self).someMoreDetailsKey: someMoreDetails,
NSUnderlyingErrorKey: (underlyingError as NSError) // <- explicit cast
]
}
Joe_Groff
(Joe Groff)
2
This is a bug. What version of Swift are you using?
michaelkirk
(Michael Kirk)
3
Swift 4.1
I can put together a demo app for you.
Joe_Groff
(Joe Groff)
4
I believe this is already fixed in Swift 4.2. The same runtime behavior should be exercised by this test case:
import Foundation
enum Foo: Error { case x, y, z }
let x: Any = Foo.x
print((x as AnyObject) as! NSError)
IIRC this crashed in Swift 4.1, but in Swift 4.2, it correctly prints Error Domain=foo.Foo Code=0 "(null)" like an NSError would.
1 Like
michaelkirk
(Michael Kirk)
5
You're right, I've verified that this works with Swift 4.2. Thanks!
Joe_Groff
(Joe Groff)
6
No problem, thank you for confirming!