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@swift.org 
> <mailto:swift-users@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@swift.org <mailto:swift-users@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Reply via email to