[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