[swift-users] Proper Way to make Errors in Swift 3

Ole Begemann ole at oleb.net
Sun Mar 5 13:20:54 CST 2017


Have you tried explicitly casting the value to NSError when you pass it 
to Objective-C? I think it should work then.

     let myError: MyError = ...
     myObjCFunc(myError as NSError)

On 02/03/2017 17:29, Ronak via swift-users wrote:
> Hi everyone,
>
> It looks like I’m still having issues exposing a CustomNSError to
> Objective-C. I am generating errors of this type in Swift and then
> trying to bridge them in one direction over to Objective-C.
> From Objective-C, this Error type is being exposed as a _SwiftValue.
>
> Do I have to mark this error as @objc and switch to using a raw enum? If
> so, I fail to see the benefit of using CustomNSError or any of the new
> error related protocols from Swift -> Objective-C.
>
> Here’s my implementation:
>
> publicenumMyError: CustomNSError, Equatable{
>
>   caseone([String: Any])
>
>   casetwo([String: Any])
>
>   casethree([String: Any])
>
>   /// The domain of the error.
>   publicstaticvarerrorDomain: String{
>     return“MyError"
>   }
>
>   /// The error code within the given domain.
>   publicvarerrorCode: Int{
>     switchself{
>     case.one:
>       return50000
>     case.two:
>       return50001
>     case.three:
>       return50002
>     }
>   }
>
>   /// The user-info dictionary.
>   publicvarerrorUserInfo: [String: Any] {
>     varuserInfo = [String: Any]()
>     ifcaselet.one(info) = self{
>       userInfo = info
>     } elseifcaselet.two(info) = self{
>       userInfo = info
>     } elseifcaselet.three(info) = self{
>       userInfo = info
>     }
>
>     returnuserInfo
>   }
> }
>
> Thanks
>
> Ronak
>
>> On Sep 29, 2016, at 5:46 PM, Ronak via swift-users
>> <swift-users at swift.org <mailto:swift-users at swift.org>> wrote:
>>
>> Ahh..thanks for the reply Zach. I didn’t actually see your reply until
>> now.
>>
>> I’ll see how I can adjust my code.
>>
>> Thanks for this!
>>
>>
>>> On Sep 29, 2016, at 4:38 PM, Zach Waldowski <zach at waldowski.me
>>> <mailto:zach at waldowski.me>> wrote:
>>>
>>> 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:
>>>
>>> externNSString*constMyErrorDomain NS_REFINED_FOR_SWIFT;
>>> externNSString*constMyErrorUserInfoStringKey NS_REFINED_FOR_SWIFT;
>>>
>>> typedefNS_ENUM(NSInteger, MyErrorCode) {
>>>     MyErrorCodeOne,
>>>     MyErrorCodeTwo,
>>>     MyErrorCodeThree,
>>> } NS_REFINED_FOR_SWIFT;
>>>
>>> enumMyError: CustomNSError{
>>>
>>>     caseone(String)
>>>     casetwo
>>>     casethree
>>>
>>>     staticvarerrorDomain: String{
>>>         return__MyErrorDomain
>>>     }
>>>
>>>     varerrorCode: Int{
>>>         switchself{
>>>         case.one:
>>>             return__MyErrorCode.one.rawValue
>>>         case.two:
>>>             return__MyErrorCode.two.rawValue
>>>         case.three:
>>>             return__MyErrorCode.three.rawValue
>>>         }
>>>     }
>>>
>>>     varerrorUserInfo: [String: Any] {
>>>         varuserInfo = [String: Any]()
>>>         ifcaselet.one(string) = self{
>>>             userInfo[__MyErrorUserInfoStringKey] = string
>>>         }
>>>         returnuserInfo
>>>     }
>>>
>>> }
>>>
>>>> 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 completely
>>>> and came up with this implementation:
>>>>
>>>>
>>>> /// The enumeration of the possible error codes in the Foundation
>>>> error domain
>>>> @objcpublicclassFoundationError: NSObject, CustomNSError{
>>>>
>>>>     /// The underlying error code
>>>>     privateletcode: FoundationError.Code
>>>>
>>>>     /// The type of an error code.
>>>>     @objcpublicenumCode: Int{
>>>>
>>>>         /// An ARCOperationCondition failed during evaluation
>>>>         caseoperationConditionFailed = 10000
>>>>
>>>>         /// An ARCOperation failed during execution
>>>>         caseoperationExecutionFailed = 10001
>>>>     }
>>>>
>>>>     /// The domain of the error.
>>>>     publicstaticvarerrorDomain: String{
>>>>         return"FoundationError"
>>>>     }
>>>>
>>>>     /// The error code within the given domain.
>>>>     publicvarerrorCode: Int{
>>>>         returncode.rawValue
>>>>     }
>>>>
>>>>     /// The user-info dictionary.
>>>>     publicleterrorUserInfo: [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
>>>>     publicconvenienceinit(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
>>>>     publicinit(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
>>>>     publicoverridefuncisEqual(_object: Any?) -> Bool{
>>>>         guardletobject = object as? FoundationErrorelse{ returnfalse}
>>>>
>>>>         returnerrorCode == 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



More information about the swift-users mailing list