<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Jul 5, 2016, at 5:54 PM, Ben Rimmington <<a href="mailto:me@benrimmington.com" class="">me@benrimmington.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><blockquote type="cite" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><br class="Apple-interchange-newline">On 6 Jul 2016, at 01:02, Douglas Gregor <<a href="mailto:dgregor@apple.com" class="">dgregor@apple.com</a>> wrote:<br class=""><br class=""><blockquote type="cite" class="">On Jul 5, 2016, at 5:00 PM, Ben Rimmington <<a href="mailto:me@benrimmington.com" class="">me@benrimmington.com</a>> wrote:<br class=""><br class=""><<a href="https://github.com/apple/swift-evolution/blob/master/proposals/0112-nserror-bridging.md" class="">https://github.com/apple/swift-evolution/blob/master/proposals/0112-nserror-bridging.md</a>><br class=""><br class="">The new protocols could be combined into a single CustomNSError protocol.<br class="">This would mirror the NSError class APIs, which are being customized.<br class=""></blockquote><br class="">Why is that good? The two primary protocols—LocalizedError and RecoverableError—provide a more focused, easy-to-understand experience for opting in to specific behavior. CustomNSError is a fallback for “I want to do something special with the generated NSError”.<br class=""></blockquote><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">You wrote previously:</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">"To a close approximation, there are no use sites of these protocols."</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class=""><</span><a href="http://thread.gmane.org/gmane.comp.lang.swift.evolution/22485/focus=22919" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class="">http://thread.gmane.org/gmane.comp.lang.swift.evolution/22485/focus=22919</a><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">></span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">If the Printable protocol was renamed to CustomStringConvertible to discourage use sites, then having a single CustomNSError protocol should have a similar effect. </span></div></blockquote><div><br class=""></div><div>We don’t need to discourage use sites; I just don’t expect them to be common because it’s the library that will be querying these conformances. Regardless, we shouldn’t contort a protocol design to discourage usage; we should make it clear what conforming to the protocol implies.</div><br class=""><blockquote type="cite" class=""><div class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">The protocol will be as easy-to-understand as the NSError class itself. If there are default implementations for all requirements, a conforming type can still opt into specific behavior.</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""></div></blockquote><div><br class=""></div>“As easy-to-understand as the NSError class” is not the goal here. We want to do better, and that means detangling the various different things that NSError puts together.</div><div><br class=""></div><div>With this proposal, you define an error type like this:</div><div><br class=""></div><div><pre style="box-sizing: border-box; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 14px; margin-top: 0px; margin-bottom: 0px; line-height: 1.45; word-wrap: normal; padding: 16px; overflow: auto; background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; word-break: normal; color: rgb(51, 51, 51);" class=""><span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">enum</span> HomeworkError <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">:</span> ErrorProtocol {
<span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">case</span> forgotten
<span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">case</span> lost
<span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">case</span> dogAteIt
}</pre><div class=""><br class=""></div><div>To give it a nice, localized error message (or other localized information), you conform to LocalizedError:</div><div><br class=""></div><div><pre style="box-sizing: border-box; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 14px; margin-top: 0px; margin-bottom: 0px; line-height: 1.45; word-wrap: normal; padding: 16px; overflow: auto; background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; word-break: normal; color: rgb(51, 51, 51);" class=""><span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">extension</span> HomeworkError <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">:</span> LocalizedError {
<span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">var</span> errorDescription: <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 134, 179);">String</span>? {
<span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">switch</span> <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">self</span> {
<span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">case</span> <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">.</span>forgotten: <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">return</span> NSLocalizedString(<span class="pl-s" style="box-sizing: border-box; color: rgb(24, 54, 145);"><span class="pl-pds" style="box-sizing: border-box;">"</span>I forgot it<span class="pl-pds" style="box-sizing: border-box;">"</span></span>)
<span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">case</span> <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">.</span>lost: <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">return</span> NSLocalizedString(<span class="pl-s" style="box-sizing: border-box; color: rgb(24, 54, 145);"><span class="pl-pds" style="box-sizing: border-box;">"</span>I lost it<span class="pl-pds" style="box-sizing: border-box;">"</span></span>)
<span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">case</span> <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">.</span>dogAteIt: <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">return</span> NSLocalizedString(<span class="pl-s" style="box-sizing: border-box; color: rgb(24, 54, 145);"><span class="pl-pds" style="box-sizing: border-box;">"</span>The dog ate it<span class="pl-pds" style="box-sizing: border-box;">"</span></span>)
}
}
}</pre><div class=""><br class=""></div></div><div>and if you want to implement recovery attempts for your error, you conform to RecoverableError:</div><div><br class=""></div><div><pre style="box-sizing: border-box; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 14px; margin-top: 0px; margin-bottom: 0px; line-height: 1.45; word-wrap: normal; padding: 16px; overflow: auto; background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; word-break: normal; color: rgb(51, 51, 51);" class=""><span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">extension</span> HomeworkError <span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">:</span> RecoverableError {
// implementation here
}</pre><div class=""><br class=""></div></div><div>You can catch these errors with “catch let error as HomeworkError”, or “catch HomeworkError.forgotten”, or whatever.</div><div><br class=""></div><div>Nowhere in any of that did I mention NSError, or error domains, or codes, or user-info dictionaries. You shouldn’t need them with this model, at all. NSError is bridged away completely, and is an implementation detail of Objective-C interoperability. CustomNSError, and references to the NSError type, is an escape hatch so that one can get precise control over the interoperability with NSError for those (hopefully rare) cases where the Swift error-handling model can’t express something that NSError can.</div><div><br class=""></div><br class=""><blockquote type="cite" class=""><div class=""><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><blockquote type="cite" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><blockquote type="cite" class="">Instead of using NSError.setUserInfoValueProvider(forDomain:provider:)<span class="Apple-converted-space"> </span><br class="">could you wrap the CustomNSError value inside an NSError subclass?<br class=""><br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>class _CustomNSError: NSError {<br class=""><br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> private let _error: CustomNSError<br class=""><br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> init(_error: CustomNSError) {<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> self._error = _error<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> super.init(<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> domain: _error.dynamicType.errorDomain,<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> code: _error.errorCode,<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> userInfo: _error.errorUserInfo)<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> }<br class=""><br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> override var localizedDescription: String {<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> return _error.errorDescription ?? super.localizedDescription<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> }<br class=""><br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> override var localizedFailureReason: String? {<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> return _error.failureReason ?? super.localizedFailureReason<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> }<br class=""><br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> override var localizedRecoverySuggestion: String? {<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> return _error.recoverySuggestion ?? super.localizedRecoverySuggestion<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> }<br class=""><br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> override var localizedRecoveryOptions: [String]? {<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> return _error.recoveryOptions ?? super.localizedRecoveryOptions<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> }<br class=""><br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> override var recoveryAttempter: AnyObject? {<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> if _error.recoveryOptions != nil {<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> return _NSErrorRecoveryAttempter(error: _error)<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> } else {<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> return super.recoveryAttempter<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> }<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> }<br class=""><br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> override var helpAnchor: String? {<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> return _error.helpAnchor ?? super.helpAnchor<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span><span class="Apple-converted-space"> </span> }<br class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>}<br class=""></blockquote><br class="">We could, but why? This is precisely what user-info value providers were designed for.<br class=""></blockquote><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">If the NSError.setUserInfoValueProvider(forDomain:provider:) method isn't available in all supported target platforms, an NSError subclass seems to be the simpler option.</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""></div></blockquote></div><br class=""><div class="">When/where it is available, NSError.setUserInfoValueProvider(forDomain:provider:) is the solution recommended by Cocoa as the best way to lazily populate the userInfo dictionary, so it makes sense for us to use that mechanism. Sure, we need to implement a fallback, but the use of that fallback will go away at some point and we’ll be back to one implementation.</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>- Doug</div><div class=""><br class=""></div><div class=""><br class=""></div></body></html>