[swift-evolution] [Proposal draft] NSError bridging

Charles Srstka cocoadev at charlessoft.com
Wed Jun 29 14:20:41 CDT 2016


> On Jun 29, 2016, at 12:05 PM, Dmitri Gribenko <gribozavr at gmail.com> wrote:
> 
> On Wed, Jun 29, 2016 at 4:13 AM, Charles Srstka
> <cocoadev at charlessoft.com> wrote:
>> On Jun 29, 2016, at 2:50 AM, Dmitri Gribenko via swift-evolution
>> <swift-evolution at swift.org> wrote:
>> 
>> 
>> I'm not sure I really want '.url' and '.stringEncoding' on every
>> Error.  'var underlying' is universally useful, but providing it
>> requires a implementing conformance to CustomNSError, which has to
>> vend a weakly-typed dictionary.  Is this really the API we want to
>> expose?
>> 
>> 
>> We need to expose the dictionary in order to provide full compatibility with
>> NSError.
> 
> The full compatibility argument is universal, and it can be applied to
> anything.  Not always the answer is "dump the compatibility APIs onto
> the Swift type”.

In this case, the type in question is the error type returned by all of the error-returning APIs in the frameworks, as well as the vast library of Objective-C code out there in the community. I think that being able to access all the information provided by said error type is a fairly important goal.

Also, the APIs are not “dumped onto the Swift type.” They are added in an extension in Foundation, and if you don’t import Foundation, you won’t see them. This is consistent with the way other bridged types are treated; for example, Foundation has an extension on String that provides most of the goodies provided by NSString. This allows us to write code using Strings exclusively, with NSString rarely, if ever, actually needing to appear in Swift code, which is what we want to achieve with NSError->Error.

>> Also, the underlying error has to be stored somewhere, which
>> effectively prevents implementers of CustomNSError from being enums.
>> 
>> 
>> Not at all. Your enum can implement a dynamic errorUserInfo property that
>> will populate the dictionary with the appropriate values. If you need to
>> actually store something, that can be done with enum cases as well.
> 
> You would need to store the underlying error in every enum case, which
> creates boilerplate, and you'd lose the raw representable conformance.

Only on the cases for which an underlying error is relevant, which may only be a few cases. Alternatively, you could use a struct if that fits your design better, or you could go with a hybrid approach such as an enum inside a struct, or if RawRepresentable conformance is important, you could forego providing an underlying error. Simply use the type of error that most appropriately fits your project and the conceptual type of error you wish to report.

If you do decide to provide an underlying error, you will need to create a little boilerplate, yes, whether it’s in an enum case, a struct’s initializer, or somewhere else. I don’t think that is avoidable in any case. I will still contend that it is much less boilerplate than having to fill your code with this:

let userInfo = [NSLocalizedFailureReasonErrorKey: NSLocalizedString(“A horse, a horse, my kingdom for a horse!”, comment: “Tudors are in, Yorks are out"), NSRecoverySuggestionErrorKey: NSLocalizedString(“Time for a new king.”, comment: “The Henry that doesn’t have a Shakespeare play”), NSFilePathErrorKey: “/Europe/England/Leicestershire/Bosworth”, NSURLErrorKey: NSURL(string: "https://en.wikipedia.org/wiki/Battle_of_Bosworth_Field”), NSUnderlyingErrorKey: wantedAHorseshoeNail]
throw NSError(domain: “OhMyGodWhyDoINeedToTypeAllThis”, code: WarOfRosesError.HorseDied.rawValue, userInfo: userInfo)

and then, when you need to check for the thing later on, you have to:

do {
    try something()
} catch let error as NSError {
    if error.domain == “OhMyGodWhyDoINeedToTypeAllThis” && error.code == WarOfRosesError.HorseDied.rawValue {
        self.setUpHenryVII()
    }
} catch {
    // handle normal errors
}

Yeah, I think we win overall in the boilerplate department by just turning that into a enum case or a struct with an “underlying” argument on it. :-P

Charles



More information about the swift-evolution mailing list