[swift-evolution] [Proposal draft] NSError bridging

Charles Srstka cocoadev at charlessoft.com
Wed Jun 29 18:31:08 CDT 2016


> On Jun 29, 2016, at 5:30 PM, Paul Cantrell via swift-evolution <swift-evolution at swift.org> wrote:
> 
>> On Jun 27, 2016, at 1:17 PM, Douglas Gregor via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>> The LocalizedError protocol describes an error that provides localized messages for display to the end user, all of which provide default implementations. The conforming type can provide implementations for any subset of these requirements:
>> 
>> protocol LocalizedError : Error {
>>   /// A localized message describing what error occurred.
>>   var errorDescription: String? { get }
>>>> }
> 
> 
> Given that LocalizedError would now be its own protocol that not all errors would conform to, could errorDescription be non-optional?
> 
>>   var errorDescription: String { get }
> 
> It would be nice if conformance to LocalizedError guaranteed the presence of a user-readable message. Such a guarantee is useful when building a UI.
> 
> I realize the bridging to NSError may make this impossible, but in principle it seems like the right design.

The trouble is that NSError allows NSLocalizedDescriptionKey to be nil, and leaving it nil is how you get Cocoa's default behavior in a lot of situations. In fact, this is usually what you want—leaving NSLocalizedDescriptionKey nil and populating NSLocalizedFailureReasonErrorKey instead is often the better way to go. For example, in NSDocument’s error reporting, if you throw an error that sets a failure reason, like this:

override func read(from data: Data, ofType typeName: String) throws {
    let userInfo = [NSLocalizedFailureReasonErrorKey: "Something went wrong."]
    throw NSError(domain: "Foo", code: 1, userInfo: userInfo)
}

The error is presented to the user as “The operation could not be completed. Something went wrong.”

However, if you provide the description instead:

override func read(from data: Data, ofType typeName: String) throws {
    let userInfo = [NSLocalizedDescriptionKey: "Something went wrong."]
    throw NSError(domain: "Foo", code: 1, userInfo: userInfo)
}

You just get “The operation could not be completed.” with no further information.

Providing the failure reason while leaving the description nil also changes the presentation when you’re reporting errors directly, as below:

let userInfo = [NSLocalizedFailureReasonErrorKey: "Something went wrong."]
NSApp.presentError(NSError(domain: "Foo", code: 1, userInfo: userInfo))

This gives you “The operation could not be completed. Something went wrong.” By comparison:

let userInfo = [NSLocalizedDescriptionKey: "Something went wrong."]
NSApp.presentError(NSError(domain: "Foo", code: 1, userInfo: userInfo))

This just gives you “Something went wrong.” without the polite “The operation could not be completed.” prefix, which causes the error description to come across as rather blunt. A default implementation for the property could be provided, of course, but since the primary purpose of the methods in this protocol are for the implementer of the error type to provide information to the frameworks to assist in creating the NSError, I can’t think of any way to have it simultaneously return a meaningful value to a client and still communicate to the frameworks that this value should be nil.

Perhaps we should add an additional property for a user-facing string generated from the rest of the strings?

Charles

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160629/3f82089a/attachment.html>


More information about the swift-evolution mailing list