[swift-users] Proper Way to make Errors in Swift 3
Zach Waldowski
zach at waldowski.me
Thu Sep 29 15:38:24 CDT 2016
Error types themselves shouldn’t generally cross into Objective-C, because you don’t get interop; for that, we have Error, which crosses the bridge as NSError.
If it’s instructive to think of it this way, both Objective-C and Swift should define errors in their best native way, and use NSError. That’s, at least, the use case for CustomNSError and LocalizedError.
If you’re primarily exporting errors from Objective-C to be “seen” in Swift, you want to look into the ns_error_domain attribute on the C side. This generates a good deal of the enum Code: Int boilerplate coming in to Swift, but it’s obnoxious to create those errors from Swift.
If you’re primarily exporting errors from Swift to Objective-C, you can make any Swift type implement Error and CustomNSError, which can then cross the bridge.
The happy path of full error interop in both directions is a little more complicated. Generally you have to start with one of the above approach and “mirror” some values in the other language. Consider the following as a slightly over-wrought example of having your cake and eating it too:
extern NSString *const MyErrorDomain NS_REFINED_FOR_SWIFT;
extern NSString *const MyErrorUserInfoStringKey NS_REFINED_FOR_SWIFT;
typedef NS_ENUM(NSInteger, MyErrorCode) {
MyErrorCodeOne,
MyErrorCodeTwo,
MyErrorCodeThree,
} NS_REFINED_FOR_SWIFT;
enum MyError: CustomNSError {
case one(String)
case two
case three
static var errorDomain: String {
return __MyErrorDomain
}
var errorCode: Int {
switch self {
case .one:
return __MyErrorCode.one.rawValue
case .two:
return __MyErrorCode.two.rawValue
case .three:
return __MyErrorCode.three.rawValue
}
}
var errorUserInfo: [String: Any] {
var userInfo = [String: Any]()
if case let .one(string) = self {
userInfo[__MyErrorUserInfoStringKey] = string
}
return userInfo
}
}
> On Sep 29, 2016, at 1:17 PM, Ronak via swift-users <swift-users at swift.org <mailto:swift-users at swift.org>> wrote:
>
> Hello all,
>
> We are proceeding to update all of our Swift code to Swift 3 now and had a few questions about the proper way to implement Errors. We need these entities to be available in Objective-C and they are actively being used in Swift classes marked as @objc.
>
> I read: https://github.com/apple/swift-evolution/blob/master/proposals/0112-nserror-bridging.md <https://github.com/apple/swift-evolution/blob/master/proposals/0112-nserror-bridging.md> completely and came up with this implementation:
>
>
> /// The enumeration of the possible error codes in the Foundation error domain
> @objc public class FoundationError: NSObject, CustomNSError {
>
> /// The underlying error code
> private let code: FoundationError.Code
>
> /// The type of an error code.
> @objc public enum Code: Int {
>
> /// An ARCOperationCondition failed during evaluation
> case operationConditionFailed = 10000
>
> /// An ARCOperation failed during execution
> case operationExecutionFailed = 10001
> }
>
> /// The domain of the error.
> public static var errorDomain: String {
> return "FoundationError"
> }
>
> /// The error code within the given domain.
> public var errorCode: Int {
> return code.rawValue
> }
>
> /// The user-info dictionary.
> public let errorUserInfo: [String : Any]
>
> /// Initializes a new FoundationError with an empty userInfo dictionary
> ///
> /// - parameter code: one of the available error codes
> ///
> /// - returns: a new instance of FoundationError
> public convenience init(code: FoundationError.Code) {
> self.init(code: code, userInfo: [:])
> }
>
> /// Initializes a new FoundationError with an userInfo dictionary
> ///
> /// - parameter code: one of the available error codes
> /// - parameter userInfo: the user-info dictionary
> ///
> /// - returns: a new instance of FoundationError
> public init(code: FoundationError.Code, userInfo: [String : Any]) {
> self.code = code
> errorUserInfo = userInfo
> }
>
> /// Computes whether two FoundationErrors are equal
> ///
> /// - parameter object: a FoundationError
> ///
> /// - returns: true, if the two errors are equal
> public override func isEqual(_ object: Any?) -> Bool {
> guard let object = object as? FoundationError else { return false }
>
> return errorCode == object.errorCode && errorUserInfo.keys.elementsEqual(object.errorUserInfo.keys)
> }
> }
>
> My question is whether this is the correct way to do this now; or is there another solution we should be doing? We would like to follow Swift Best Practices here, but unfortunately, the documentation is quite vague on this subject.
>
>
> Thanks for your help!
>
> Ronak Patel
> _______________________________________________
> swift-users mailing list
> swift-users at swift.org <mailto:swift-users at swift.org>
> https://lists.swift.org/mailman/listinfo/swift-users
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20160929/dda2557b/attachment.html>
More information about the swift-users
mailing list