[Accepted] SE-0112: Improved NSError Bridging


(Chris Lattner) #1

Proposal Link: https://github.com/apple/swift-evolution/blob/master/proposals/0112-nserror-bridging.md

The review of "SE-0112: Improved NSError Bridging" ran from June 30 ... July 4, 2016. The proposal has been *accepted*:

The community and core team agree that this proposal is a huge step forward that enriches the experience working with and extending the Cocoa NSError model in Swift. The core team requests one minor renaming of "attemptRecovery(optionIndex:andThen:)" to "attemptRecovery(optionIndex:resultHandler:)”. It also discussed renaming CustomNSError and RecoverableError, but decided to stay with those names.

Thank you to Doug Gregor and Charles Srstka for driving this discussion forward, and for Doug Gregor taking the charge on the implementation effort to make this happen for Swift 3!

-Chris Lattner
Review Manager


(Charles Srstka) #2

Wow, thanks! I’m delighted that Apple found this improvement to be worth inclusion in Swift 3. This will truly make the language much nicer to use with the Cocoa frameworks.

Thanks!

Charles

···

On Jul 11, 2016, at 11:19 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Proposal Link: https://github.com/apple/swift-evolution/blob/master/proposals/0112-nserror-bridging.md

The review of "SE-0112: Improved NSError Bridging" ran from June 30 ... July 4, 2016. The proposal has been *accepted*:

The community and core team agree that this proposal is a huge step forward that enriches the experience working with and extending the Cocoa NSError model in Swift. The core team requests one minor renaming of "attemptRecovery(optionIndex:andThen:)" to "attemptRecovery(optionIndex:resultHandler:)”. It also discussed renaming CustomNSError and RecoverableError, but decided to stay with those names.

Thank you to Doug Gregor and Charles Srstka for driving this discussion forward, and for Doug Gregor taking the charge on the implementation effort to make this happen for Swift 3!

-Chris Lattner
Review Manager

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Shawn Erickson) #3

Thanks for the effort on the proposal and discussion and thanks to those
working in the implementation.

-Shawn

···

On Tue, Jul 12, 2016 at 12:25 AM Charles Srstka via swift-evolution < swift-evolution@swift.org> wrote:

Wow, thanks! I’m delighted that Apple found this improvement to be worth
inclusion in Swift 3. This will truly make the language much nicer to use
with the Cocoa frameworks.

Thanks!

Charles

> On Jul 11, 2016, at 11:19 PM, Chris Lattner via swift-evolution < > swift-evolution@swift.org> wrote:
>
> Proposal Link:
https://github.com/apple/swift-evolution/blob/master/proposals/0112-nserror-bridging.md
>
> The review of "SE-0112: Improved NSError Bridging" ran from June 30 ...
July 4, 2016. The proposal has been *accepted*:
>
> The community and core team agree that this proposal is a huge step
forward that enriches the experience working with and extending the Cocoa
NSError model in Swift. The core team requests one minor renaming of
"attemptRecovery(optionIndex:andThen:)" to
"attemptRecovery(optionIndex:resultHandler:)”. It also discussed renaming
CustomNSError and RecoverableError, but decided to stay with those names.
>
> Thank you to Doug Gregor and Charles Srstka for driving this discussion
forward, and for Doug Gregor taking the charge on the implementation effort
to make this happen for Swift 3!
>
> -Chris Lattner
> Review Manager
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jon Shier) #4

I’m not sure where to put such feedback, but the ErrorProtocol to Error rename that accompanied the implementation of this proposal is very, very painful. It completely eliminates the very useful ability to embed an associated Error type inside other types, as those types now conflict with the protocol. Also, was this rename accompanied by an evolution proposal? It seems like the change was just made when this proposal was implemented.
  Also, the adoption of this proposal by the Cocoa(Touch) frameworks as seen in Xcode 8 beta 4 has made asynchronous error handling quite a bit more arduous. For example, the CKDatabase method fetch(withRecordID recordID: CKRecordID, completionHandler: (CKRecord?, Error?) -> Void) returns an `Error` now, meaning I have to cast to the specific `CKError` type to get useful information out of it. Is this just an unfortunate first effort that will be fixed, or is this the expected form of these sorts of APIs after this proposal?

Jon Shier

···

On Jul 12, 2016, at 8:44 AM, Shawn Erickson via swift-evolution <swift-evolution@swift.org> wrote:

Thanks for the effort on the proposal and discussion and thanks to those working in the implementation.

-Shawn
On Tue, Jul 12, 2016 at 12:25 AM Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Wow, thanks! I’m delighted that Apple found this improvement to be worth inclusion in Swift 3. This will truly make the language much nicer to use with the Cocoa frameworks.

Thanks!

Charles

> On Jul 11, 2016, at 11:19 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>
> Proposal Link: https://github.com/apple/swift-evolution/blob/master/proposals/0112-nserror-bridging.md
>
> The review of "SE-0112: Improved NSError Bridging" ran from June 30 ... July 4, 2016. The proposal has been *accepted*:
>
> The community and core team agree that this proposal is a huge step forward that enriches the experience working with and extending the Cocoa NSError model in Swift. The core team requests one minor renaming of "attemptRecovery(optionIndex:andThen:)" to "attemptRecovery(optionIndex:resultHandler:)”. It also discussed renaming CustomNSError and RecoverableError, but decided to stay with those names.
>
> Thank you to Doug Gregor and Charles Srstka for driving this discussion forward, and for Doug Gregor taking the charge on the implementation effort to make this happen for Swift 3!
>
> -Chris Lattner
> Review Manager
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Lily Ballard) #5

It did not eliminate that ability at all. You just need to say
`Swift.Error` instead of `Error` when referring to the protocol if a
nested `Error` type is in scope. For example:

class MyClass {
    enum Error: Swift.Error {
        case somethingWentWrong
    }
}

-Kevin Ballard

···

On Tue, Aug 2, 2016, at 10:30 AM, Jon Shier via swift-evolution wrote:

I’m not sure where to put such feedback, but the ErrorProtocol to
Error rename that accompanied the implementation of this proposal is
very, very painful. It completely eliminates the very useful ability
to embed an associated Error type inside other types, as those types
now conflict with the protocol. Also, was this rename accompanied by
an evolution proposal? It seems like the change was just made when
this proposal was implemented.
Also, the adoption of this proposal by the Cocoa(Touch) frameworks as
seen in Xcode 8 beta 4 has made asynchronous error handling quite a
bit more arduous. For example, the CKDatabase method
fetch(withRecordID recordID: CKRecordID, completionHandler:
(CKRecord?, Error?) -> Void) returns an `Error` now, meaning I have to
cast to the specific `CKError` type to get useful information out of
it. Is this just an unfortunate first effort that will be fixed, or is
this the expected form of these sorts of APIs after this proposal?

Jon Shier

On Jul 12, 2016, at 8:44 AM, Shawn Erickson via swift-evolution <swift- >> evolution@swift.org> wrote:

Thanks for the effort on the proposal and discussion and thanks to
those working in the implementation.

-Shawn
On Tue, Jul 12, 2016 at 12:25 AM Charles Srstka via swift-evolution >> <swift-evolution@swift.org> wrote:

Wow, thanks! I’m delighted that Apple found this improvement to be
worth inclusion in Swift 3. This will truly make the language much
nicer to use with the Cocoa frameworks.

Thanks!

Charles

> On Jul 11, 2016, at 11:19 PM, Chris Lattner via swift-evolution >>> > <swift-evolution@swift.org> wrote:
>
> Proposal Link:
> https://github.com/apple/swift-evolution/blob/master/proposals/0112-nserror-bridging.md
>
> The review of "SE-0112: Improved NSError Bridging" ran from June
> 30 ... July 4, 2016. The proposal has been *accepted*:
>
> The community and core team agree that this proposal is a huge
> step forward that enriches the experience working with and
> extending the Cocoa NSError model in Swift. The core team
> requests one minor renaming of
> "attemptRecovery(optionIndex:andThen:)" to
> "attemptRecovery(optionIndex:resultHandler:)”. It also discussed
> renaming CustomNSError and RecoverableError, but decided to stay
> with those names.
>
> Thank you to Doug Gregor and Charles Srstka for driving this
> discussion forward, and for Doug Gregor taking the charge on the
> implementation effort to make this happen for Swift 3!
>
> -Chris Lattner
> Review Manager
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_________________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Douglas Gregor) #6

  I’m not sure where to put such feedback, but the ErrorProtocol to Error rename that accompanied the implementation of this proposal is very, very painful. It completely eliminates the very useful ability to embed an associated Error type inside other types, as those types now conflict with the protocol. Also, was this rename accompanied by an evolution proposal? It seems like the change was just made when this proposal was implemented.

The rename was part of the proposal, in bullet #5 of the proposed solution (which, amusing, pastes as bullet #1 below):

Rename ErrorProtocol to Error: once we've completed the bridging story, Error becomes the primary way to work with error types in Swift, and the value type to which NSError is bridged:

func handleError(_ error: Error, userInteractionPermitted: Bool)

  Also, the adoption of this proposal by the Cocoa(Touch) frameworks as seen in Xcode 8 beta 4 has made asynchronous error handling quite a bit more arduous. For example, the CKDatabase method fetch(withRecordID recordID: CKRecordID, completionHandler: (CKRecord?, Error?) -> Void) returns an `Error` now, meaning I have to cast to the specific `CKError` type to get useful information out of it. Is this just an unfortunate first effort that will be fixed, or is this the expected form of these sorts of APIs after this proposal?

Prior to this proposal, you would have had to check the domain against CKErrorDomain anyway to determine whether you’re looking at a CloudKit error (vs. some other error that is passing through CloudKit), so error bridging shouldn’t actually be adding any work here—although it might be making explicit work that was already done or should have been done. Once you have casted to CKError, you now have typed accessors for information in the error:

extension CKError {
  /// Retrieve partial error results associated by item ID.
  public var partialErrorsByItemID: [NSObject : Error]? {
    return userInfo[CKPartialErrorsByItemIDKey] as? [NSObject : Error]
  }

  /// The original CKRecord object that you used as the basis for
  /// making your changes.
  public var ancestorRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorAncestorRecordKey] as? CKRecord
  }

  /// The CKRecord object that was found on the server. Use this
  /// record as the basis for merging your changes.
  public var serverRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorServerRecordKey] as? CKRecord
  }

  /// The CKRecord object that you tried to save. This record is based
  /// on the record in the CKRecordChangedErrorAncestorRecordKey key
  /// but contains the additional changes you made.
  public var clientRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorClientRecordKey] as? CKRecord
  }

  /// The number of seconds after which you may retry a request. This
  /// key may be included in an error of type
  /// `CKErrorServiceUnavailable` or `CKErrorRequestRateLimited`.
  public var retryAfterSeconds: Double? {
    return userInfo[CKErrorRetryAfterKey] as? Double
  }
}
  - Doug

···

On Aug 2, 2016, at 10:30 AM, Jon Shier via swift-evolution <swift-evolution@swift.org> wrote:

Jon Shier
  

On Jul 12, 2016, at 8:44 AM, Shawn Erickson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Thanks for the effort on the proposal and discussion and thanks to those working in the implementation.

-Shawn
On Tue, Jul 12, 2016 at 12:25 AM Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Wow, thanks! I’m delighted that Apple found this improvement to be worth inclusion in Swift 3. This will truly make the language much nicer to use with the Cocoa frameworks.

Thanks!

Charles

> On Jul 11, 2016, at 11:19 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>
> Proposal Link: https://github.com/apple/swift-evolution/blob/master/proposals/0112-nserror-bridging.md
>
> The review of "SE-0112: Improved NSError Bridging" ran from June 30 ... July 4, 2016. The proposal has been *accepted*:
>
> The community and core team agree that this proposal is a huge step forward that enriches the experience working with and extending the Cocoa NSError model in Swift. The core team requests one minor renaming of "attemptRecovery(optionIndex:andThen:)" to "attemptRecovery(optionIndex:resultHandler:)”. It also discussed renaming CustomNSError and RecoverableError, but decided to stay with those names.
>
> Thank you to Doug Gregor and Charles Srstka for driving this discussion forward, and for Doug Gregor taking the charge on the implementation effort to make this happen for Swift 3!
>
> -Chris Lattner
> Review Manager
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jon Shier) #7

Thanks Doug. I missed the rename, as earlier points still referred to ErrorProtocol. In regards to the CloudKit errors, I appreciate the strongly typed CKError, but why not have the methods return that type directly? Every usage of these methods is going to require such a cast, so why require it in the first place? I don’t understand what advantage erasing the strongly type error that was just created has when the developer will just have to bring it right back. Or is this just a first implementation?

Jon

···

On Aug 2, 2016, at 4:20 PM, Douglas Gregor <dgregor@apple.com> wrote:

On Aug 2, 2016, at 10:30 AM, Jon Shier via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

  I’m not sure where to put such feedback, but the ErrorProtocol to Error rename that accompanied the implementation of this proposal is very, very painful. It completely eliminates the very useful ability to embed an associated Error type inside other types, as those types now conflict with the protocol. Also, was this rename accompanied by an evolution proposal? It seems like the change was just made when this proposal was implemented.

The rename was part of the proposal, in bullet #5 of the proposed solution (which, amusing, pastes as bullet #1 below):

Rename ErrorProtocol to Error: once we've completed the bridging story, Error becomes the primary way to work with error types in Swift, and the value type to which NSError is bridged:

func handleError(_ error: Error, userInteractionPermitted: Bool)

  Also, the adoption of this proposal by the Cocoa(Touch) frameworks as seen in Xcode 8 beta 4 has made asynchronous error handling quite a bit more arduous. For example, the CKDatabase method fetch(withRecordID recordID: CKRecordID, completionHandler: (CKRecord?, Error?) -> Void) returns an `Error` now, meaning I have to cast to the specific `CKError` type to get useful information out of it. Is this just an unfortunate first effort that will be fixed, or is this the expected form of these sorts of APIs after this proposal?

Prior to this proposal, you would have had to check the domain against CKErrorDomain anyway to determine whether you’re looking at a CloudKit error (vs. some other error that is passing through CloudKit), so error bridging shouldn’t actually be adding any work here—although it might be making explicit work that was already done or should have been done. Once you have casted to CKError, you now have typed accessors for information in the error:

extension CKError {
  /// Retrieve partial error results associated by item ID.
  public var partialErrorsByItemID: [NSObject : Error]? {
    return userInfo[CKPartialErrorsByItemIDKey] as? [NSObject : Error]
  }

  /// The original CKRecord object that you used as the basis for
  /// making your changes.
  public var ancestorRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorAncestorRecordKey] as? CKRecord
  }

  /// The CKRecord object that was found on the server. Use this
  /// record as the basis for merging your changes.
  public var serverRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorServerRecordKey] as? CKRecord
  }

  /// The CKRecord object that you tried to save. This record is based
  /// on the record in the CKRecordChangedErrorAncestorRecordKey key
  /// but contains the additional changes you made.
  public var clientRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorClientRecordKey] as? CKRecord
  }

  /// The number of seconds after which you may retry a request. This
  /// key may be included in an error of type
  /// `CKErrorServiceUnavailable` or `CKErrorRequestRateLimited`.
  public var retryAfterSeconds: Double? {
    return userInfo[CKErrorRetryAfterKey] as? Double
  }
}
  - Doug

Jon Shier
  

On Jul 12, 2016, at 8:44 AM, Shawn Erickson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Thanks for the effort on the proposal and discussion and thanks to those working in the implementation.

-Shawn
On Tue, Jul 12, 2016 at 12:25 AM Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Wow, thanks! I’m delighted that Apple found this improvement to be worth inclusion in Swift 3. This will truly make the language much nicer to use with the Cocoa frameworks.

Thanks!

Charles

> On Jul 11, 2016, at 11:19 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>
> Proposal Link: https://github.com/apple/swift-evolution/blob/master/proposals/0112-nserror-bridging.md
>
> The review of "SE-0112: Improved NSError Bridging" ran from June 30 ... July 4, 2016. The proposal has been *accepted*:
>
> The community and core team agree that this proposal is a huge step forward that enriches the experience working with and extending the Cocoa NSError model in Swift. The core team requests one minor renaming of "attemptRecovery(optionIndex:andThen:)" to "attemptRecovery(optionIndex:resultHandler:)”. It also discussed renaming CustomNSError and RecoverableError, but decided to stay with those names.
>
> Thank you to Doug Gregor and Charles Srstka for driving this discussion forward, and for Doug Gregor taking the charge on the implementation effort to make this happen for Swift 3!
>
> -Chris Lattner
> Review Manager
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Lily Ballard) #8

You're assuming that every error passed to that method is a CKError. The
documentation does not claim that to be true, so it's quite plausible
that you might get other errors that are simply passed through.

-Kevin

···

On Tue, Aug 2, 2016, at 02:19 PM, Jon Shier via swift-evolution wrote:

Thanks Doug. I missed the rename, as earlier points still referred to
ErrorProtocol. In regards to the CloudKit errors, I appreciate the
strongly typed CKError, but why not have the methods return that type
directly? Every usage of these methods is going to require such a
cast, so why require it in the first place? I don’t understand what
advantage erasing the strongly type error that was just created has
when the developer will just have to bring it right back. Or is this
just a first implementation?

Jon

On Aug 2, 2016, at 4:20 PM, Douglas Gregor <dgregor@apple.com> wrote:

On Aug 2, 2016, at 10:30 AM, Jon Shier via swift-evolution <swift- >>> evolution@swift.org> wrote:

I’m not sure where to put such feedback, but the ErrorProtocol to
Error rename that accompanied the implementation of this proposal is
very, very painful. It completely eliminates the very useful ability
to embed an associated Error type inside other types, as those types
now conflict with the protocol. Also, was this rename accompanied by
an evolution proposal? It seems like the change was just made when
this proposal was implemented.

The rename was part of the proposal, in bullet #5 of the proposed
solution (which, amusing, pastes as bullet #1 below):

1. Rename ErrorProtocol to Error: once we've completed the bridging
    story, Error becomes the primary way to work with error types in
    Swift, and the value type to which NSError is bridged:

func handleError(_ error: Error, userInteractionPermitted: Bool)

Also, the adoption of this proposal by the Cocoa(Touch) frameworks
as seen in Xcode 8 beta 4 has made asynchronous error handling quite
a bit more arduous. For example, the CKDatabase method
fetch(withRecordID recordID: CKRecordID, completionHandler:
(CKRecord?, Error?) -> Void) returns an `Error` now, meaning I have
to cast to the specific `CKError` type to get useful information out
of it. Is this just an unfortunate first effort that will be fixed,
or is this the expected form of these sorts of APIs after this
proposal?

Prior to this proposal, you would have had to check the domain
against CKErrorDomain anyway to determine whether you’re looking at a
CloudKit error (vs. some other error that is passing through
CloudKit), so error bridging shouldn’t actually be adding any work
here—although it might be making explicit work that was already done
or should have been done. Once you have casted to CKError, you now
have typed accessors for information in the error:

extension CKError {
  /// Retrieve partial error results associated by item ID.
  public var partialErrorsByItemID: [NSObject : Error]? {
    return userInfo[CKPartialErrorsByItemIDKey] as? [NSObject :
    Error]
  }

  /// The original CKRecord object that you used as the basis for
  /// making your changes.
  public var ancestorRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorAncestorRecordKey] as?
    CKRecord
  }

  /// The CKRecord object that was found on the server. Use this
  /// record as the basis for merging your changes.
  public var serverRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorServerRecordKey] as? CKRecord
  }

  /// The CKRecord object that you tried to save. This record is
  based
  /// on the record in the CKRecordChangedErrorAncestorRecordKey key
  /// but contains the additional changes you made.
  public var clientRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorClientRecordKey] as? CKRecord
  }

  /// The number of seconds after which you may retry a request. This
  /// key may be included in an error of type
  /// `CKErrorServiceUnavailable` or `CKErrorRequestRateLimited`.
  public var retryAfterSeconds: Double? {
    return userInfo[CKErrorRetryAfterKey] as? Double
  }
}
- Doug

Jon Shier

On Jul 12, 2016, at 8:44 AM, Shawn Erickson via swift-evolution <swift- >>>> evolution@swift.org> wrote:

Thanks for the effort on the proposal and discussion and thanks to
those working in the implementation.

-Shawn
On Tue, Jul 12, 2016 at 12:25 AM Charles Srstka via swift-evolution >>>> <swift-evolution@swift.org> wrote:

Wow, thanks! I’m delighted that Apple found this improvement to be
worth inclusion in Swift 3. This will truly make the language much
nicer to use with the Cocoa frameworks.

Thanks!

Charles

> On Jul 11, 2016, at 11:19 PM, Chris Lattner via swift-evolution >>>>> > <swift-evolution@swift.org> wrote:
>
> Proposal Link:
> https://github.com/apple/swift-evolution/blob/master/proposals/0112-nserror-bridging.md
>
> The review of "SE-0112: Improved NSError Bridging" ran from June
> 30 ... July 4, 2016. The proposal has been *accepted*:
>
> The community and core team agree that this proposal is a huge
> step forward that enriches the experience working with and
> extending the Cocoa NSError model in Swift. The core team
> requests one minor renaming of
> "attemptRecovery(optionIndex:andThen:)" to
> "attemptRecovery(optionIndex:resultHandler:)”. It also
> discussed renaming CustomNSError and RecoverableError, but
> decided to stay with those names.
>
> Thank you to Doug Gregor and Charles Srstka for driving this
> discussion forward, and for Doug Gregor taking the charge on the
> implementation effort to make this happen for Swift 3!
>
> -Chris Lattner
> Review Manager
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_________________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Douglas Gregor) #9

  Thanks Doug. I missed the rename, as earlier points still referred to ErrorProtocol. In regards to the CloudKit errors, I appreciate the strongly typed CKError, but why not have the methods return that type directly?

Generally speaking, Cocoa only uses NSError—not specific subclasses or NSError or other error types—because errors can occur at many different places in the stack and be propagated up. A CloudKit operation could fail because of some problem detected in a different error domain—say, the general Cocoa error domain or URLError domain—and that non-CloudKit error would get passed through immediately. So, if you were assuming that every error you get here had to be in the CloudKit error domain, I believe your code was already incorrect. It is *possible* that CloudKit translates/wraps all other errors, but that would be odd for a Cocoa framework.

Every usage of these methods is going to require such a cast, so why require it in the first place? I don’t understand what advantage erasing the strongly type error that was just created has when the developer will just have to bring it right back. Or is this just a first implementation?

There was never a strongly-typed error, and in most Cocoa cases there shouldn’t be one because NSError covers all error domains, by design.

  - Doug

···

On Aug 2, 2016, at 2:19 PM, Jon Shier <jon@jonshier.com> wrote:

Jon

On Aug 2, 2016, at 4:20 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

On Aug 2, 2016, at 10:30 AM, Jon Shier via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

  I’m not sure where to put such feedback, but the ErrorProtocol to Error rename that accompanied the implementation of this proposal is very, very painful. It completely eliminates the very useful ability to embed an associated Error type inside other types, as those types now conflict with the protocol. Also, was this rename accompanied by an evolution proposal? It seems like the change was just made when this proposal was implemented.

The rename was part of the proposal, in bullet #5 of the proposed solution (which, amusing, pastes as bullet #1 below):

Rename ErrorProtocol to Error: once we've completed the bridging story, Error becomes the primary way to work with error types in Swift, and the value type to which NSError is bridged:

func handleError(_ error: Error, userInteractionPermitted: Bool)

  Also, the adoption of this proposal by the Cocoa(Touch) frameworks as seen in Xcode 8 beta 4 has made asynchronous error handling quite a bit more arduous. For example, the CKDatabase method fetch(withRecordID recordID: CKRecordID, completionHandler: (CKRecord?, Error?) -> Void) returns an `Error` now, meaning I have to cast to the specific `CKError` type to get useful information out of it. Is this just an unfortunate first effort that will be fixed, or is this the expected form of these sorts of APIs after this proposal?

Prior to this proposal, you would have had to check the domain against CKErrorDomain anyway to determine whether you’re looking at a CloudKit error (vs. some other error that is passing through CloudKit), so error bridging shouldn’t actually be adding any work here—although it might be making explicit work that was already done or should have been done. Once you have casted to CKError, you now have typed accessors for information in the error:

extension CKError {
  /// Retrieve partial error results associated by item ID.
  public var partialErrorsByItemID: [NSObject : Error]? {
    return userInfo[CKPartialErrorsByItemIDKey] as? [NSObject : Error]
  }

  /// The original CKRecord object that you used as the basis for
  /// making your changes.
  public var ancestorRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorAncestorRecordKey] as? CKRecord
  }

  /// The CKRecord object that was found on the server. Use this
  /// record as the basis for merging your changes.
  public var serverRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorServerRecordKey] as? CKRecord
  }

  /// The CKRecord object that you tried to save. This record is based
  /// on the record in the CKRecordChangedErrorAncestorRecordKey key
  /// but contains the additional changes you made.
  public var clientRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorClientRecordKey] as? CKRecord
  }

  /// The number of seconds after which you may retry a request. This
  /// key may be included in an error of type
  /// `CKErrorServiceUnavailable` or `CKErrorRequestRateLimited`.
  public var retryAfterSeconds: Double? {
    return userInfo[CKErrorRetryAfterKey] as? Double
  }
}
  - Doug

Jon Shier
  

On Jul 12, 2016, at 8:44 AM, Shawn Erickson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Thanks for the effort on the proposal and discussion and thanks to those working in the implementation.

-Shawn
On Tue, Jul 12, 2016 at 12:25 AM Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Wow, thanks! I’m delighted that Apple found this improvement to be worth inclusion in Swift 3. This will truly make the language much nicer to use with the Cocoa frameworks.

Thanks!

Charles

> On Jul 11, 2016, at 11:19 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>
> Proposal Link: https://github.com/apple/swift-evolution/blob/master/proposals/0112-nserror-bridging.md
>
> The review of "SE-0112: Improved NSError Bridging" ran from June 30 ... July 4, 2016. The proposal has been *accepted*:
>
> The community and core team agree that this proposal is a huge step forward that enriches the experience working with and extending the Cocoa NSError model in Swift. The core team requests one minor renaming of "attemptRecovery(optionIndex:andThen:)" to "attemptRecovery(optionIndex:resultHandler:)”. It also discussed renaming CustomNSError and RecoverableError, but decided to stay with those names.
>
> Thank you to Doug Gregor and Charles Srstka for driving this discussion forward, and for Doug Gregor taking the charge on the implementation effort to make this happen for Swift 3!
>
> -Chris Lattner
> Review Manager
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jon Shier) #10

Doug:
  Thanks for indulging me so far, I think I’ve almost got it. Prior to this, using NSError, I could just look at the relevant properties of the error if I needed to see what type it was. Network errors had different codes from CloudKit errors, POSIX errors were underlying FileManager errors. A bit complex due to the undocumented nature of so many of these errors, but I could ignore any aspect of the error I didn’t care about. Now, however, it seems I must always care about what types of errors come out of various methods, as I’ll need to cast to the appropriate types to get useful information. For example, how would you handle the CloudKit errors I mentioned before? It seems to me like I would need to, at the point where I need to extract useful information, do a switch on various casts. First, try casting to CKError, then to CocoaError (?), and then likely produce a fatalError if there’s an unexpected type. Or is Error guaranteed to always cast to something useful? I’ve read the proposal a few times now and it looks like a lot of casting is going to be required, I’m mostly curious about the recommended patterns, especially for asynchronous calls that don’t go through throw/catch.

Jon

···

On Aug 2, 2016, at 5:36 PM, Douglas Gregor <dgregor@apple.com> wrote:

On Aug 2, 2016, at 2:19 PM, Jon Shier <jon@jonshier.com <mailto:jon@jonshier.com>> wrote:

  Thanks Doug. I missed the rename, as earlier points still referred to ErrorProtocol. In regards to the CloudKit errors, I appreciate the strongly typed CKError, but why not have the methods return that type directly?

Generally speaking, Cocoa only uses NSError—not specific subclasses or NSError or other error types—because errors can occur at many different places in the stack and be propagated up. A CloudKit operation could fail because of some problem detected in a different error domain—say, the general Cocoa error domain or URLError domain—and that non-CloudKit error would get passed through immediately. So, if you were assuming that every error you get here had to be in the CloudKit error domain, I believe your code was already incorrect. It is *possible* that CloudKit translates/wraps all other errors, but that would be odd for a Cocoa framework.

Every usage of these methods is going to require such a cast, so why require it in the first place? I don’t understand what advantage erasing the strongly type error that was just created has when the developer will just have to bring it right back. Or is this just a first implementation?

There was never a strongly-typed error, and in most Cocoa cases there shouldn’t be one because NSError covers all error domains, by design.

  - Doug

Jon

On Aug 2, 2016, at 4:20 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

On Aug 2, 2016, at 10:30 AM, Jon Shier via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

  I’m not sure where to put such feedback, but the ErrorProtocol to Error rename that accompanied the implementation of this proposal is very, very painful. It completely eliminates the very useful ability to embed an associated Error type inside other types, as those types now conflict with the protocol. Also, was this rename accompanied by an evolution proposal? It seems like the change was just made when this proposal was implemented.

The rename was part of the proposal, in bullet #5 of the proposed solution (which, amusing, pastes as bullet #1 below):

Rename ErrorProtocol to Error: once we've completed the bridging story, Error becomes the primary way to work with error types in Swift, and the value type to which NSError is bridged:

func handleError(_ error: Error, userInteractionPermitted: Bool)

  Also, the adoption of this proposal by the Cocoa(Touch) frameworks as seen in Xcode 8 beta 4 has made asynchronous error handling quite a bit more arduous. For example, the CKDatabase method fetch(withRecordID recordID: CKRecordID, completionHandler: (CKRecord?, Error?) -> Void) returns an `Error` now, meaning I have to cast to the specific `CKError` type to get useful information out of it. Is this just an unfortunate first effort that will be fixed, or is this the expected form of these sorts of APIs after this proposal?

Prior to this proposal, you would have had to check the domain against CKErrorDomain anyway to determine whether you’re looking at a CloudKit error (vs. some other error that is passing through CloudKit), so error bridging shouldn’t actually be adding any work here—although it might be making explicit work that was already done or should have been done. Once you have casted to CKError, you now have typed accessors for information in the error:

extension CKError {
  /// Retrieve partial error results associated by item ID.
  public var partialErrorsByItemID: [NSObject : Error]? {
    return userInfo[CKPartialErrorsByItemIDKey] as? [NSObject : Error]
  }

  /// The original CKRecord object that you used as the basis for
  /// making your changes.
  public var ancestorRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorAncestorRecordKey] as? CKRecord
  }

  /// The CKRecord object that was found on the server. Use this
  /// record as the basis for merging your changes.
  public var serverRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorServerRecordKey] as? CKRecord
  }

  /// The CKRecord object that you tried to save. This record is based
  /// on the record in the CKRecordChangedErrorAncestorRecordKey key
  /// but contains the additional changes you made.
  public var clientRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorClientRecordKey] as? CKRecord
  }

  /// The number of seconds after which you may retry a request. This
  /// key may be included in an error of type
  /// `CKErrorServiceUnavailable` or `CKErrorRequestRateLimited`.
  public var retryAfterSeconds: Double? {
    return userInfo[CKErrorRetryAfterKey] as? Double
  }
}
  - Doug

Jon Shier
  

On Jul 12, 2016, at 8:44 AM, Shawn Erickson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Thanks for the effort on the proposal and discussion and thanks to those working in the implementation.

-Shawn
On Tue, Jul 12, 2016 at 12:25 AM Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Wow, thanks! I’m delighted that Apple found this improvement to be worth inclusion in Swift 3. This will truly make the language much nicer to use with the Cocoa frameworks.

Thanks!

Charles

> On Jul 11, 2016, at 11:19 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>
> Proposal Link: https://github.com/apple/swift-evolution/blob/master/proposals/0112-nserror-bridging.md
>
> The review of "SE-0112: Improved NSError Bridging" ran from June 30 ... July 4, 2016. The proposal has been *accepted*:
>
> The community and core team agree that this proposal is a huge step forward that enriches the experience working with and extending the Cocoa NSError model in Swift. The core team requests one minor renaming of "attemptRecovery(optionIndex:andThen:)" to "attemptRecovery(optionIndex:resultHandler:)”. It also discussed renaming CustomNSError and RecoverableError, but decided to stay with those names.
>
> Thank you to Doug Gregor and Charles Srstka for driving this discussion forward, and for Doug Gregor taking the charge on the implementation effort to make this happen for Swift 3!
>
> -Chris Lattner
> Review Manager
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Lily Ballard) #11

With NSError, you *must* check the domain before trying to interpret the
code, or else your code is buggy and will behave incorrectly when
receiving an unexpected error. With SE-0112, instead of checking the
domain, you check if the Error can be casted to the particular error
type that represents the domain. There is a one-to-one correspondence
between domains and the new error types. For example, NSCocoaErrorDomain
is represented by CocoaError, NSURLErrorDomain is URLError, etc.

So previously you might have code that looks like

func handleError(error: NSError) {
    switch error.domain {
    case NSCocoaErrorDomain where error.code == NSFileNoSuchFileError:
        let path = error.userInfo[NSFilePathErrorKey] as? String
        // handle error for path
    case NSURLErrorDomain where error.code == NSURLErrorTimedOut:
        let url = error.userInfo[NSURLErrorKey] as? NSURL
        // handle error for url
    default:
        // generic handling of other errors
    }
}

And now you'd write that like

func handleError(error: Error) {
    switch error {
    case let error as CocoaError where error.code ==
    .fileNoSuchFileError:
        let path = error.filePath
        // handle error for path
    case let error as URLError where error.code == .timedOut:
        let url = error.failingURL
        // handle error for url
    default:
        // generic handling of other errors
    }
}

It's the same basic structure, except now you get strong typing, you
can't possibly forget to check the domain (which is a surprisingly
common bug I see in a lot of code), and you get convenient accessors for
the values stored in the user info.

And if you don't actually care about any of the user info properties,
then the new version is much simpler than the old:

func handleError(error: Error) {
    switch error {
    case CocoaError.fileNoSuchFileError:
        // handle error
    case URLError.timedOut:
        // handle error
    default:
        // generic handling of other errors
    }
}

It's similar to checking the code without the domain in the old style,
except now it checks the domain automatically, so you *still* can't
accidentally interpret an error's code in the wrong domain.

-Kevin Ballard

···

On Thu, Aug 4, 2016, at 11:00 AM, Jon Shier via swift-evolution wrote:

Doug:
Thanks for indulging me so far, I think I’ve almost got it. Prior to
this, using NSError, I could just look at the relevant properties of
the error if I needed to see what type it was. Network errors had
different codes from CloudKit errors, POSIX errors were underlying
FileManager errors. A bit complex due to the undocumented nature of so
many of these errors, but I could ignore any aspect of the error I
didn’t care about. Now, however, it seems I must always care about
what types of errors come out of various methods, as I’ll need to cast
to the appropriate types to get useful information. For example, how
would you handle the CloudKit errors I mentioned before? It seems to
me like I would need to, at the point where I need to extract useful
information, do a switch on various casts. First, try casting to
CKError, then to CocoaError (?), and then likely produce a fatalError
if there’s an unexpected type. Or is Error guaranteed to always cast
to something useful? I’ve read the proposal a few times now and it
looks like a lot of casting is going to be required, I’m mostly
curious about the recommended patterns, especially for asynchronous
calls that don’t go through throw/catch.

Jon

On Aug 2, 2016, at 5:36 PM, Douglas Gregor <dgregor@apple.com> wrote:

On Aug 2, 2016, at 2:19 PM, Jon Shier <jon@jonshier.com> wrote:

Thanks Doug. I missed the rename, as earlier points still referred
to ErrorProtocol. In regards to the CloudKit errors, I appreciate
the strongly typed CKError, but why not have the methods return that
type directly?

Generally speaking, Cocoa only uses NSError—not specific subclasses
or NSError or other error types—because errors can occur at many
different places in the stack and be propagated up. A CloudKit
operation could fail because of some problem detected in a different
error domain—say, the general Cocoa error domain or URLError
domain—and that non-CloudKit error would get passed through
immediately. So, if you were assuming that every error you get here
had to be in the CloudKit error domain, I believe your code was
already incorrect. It is *possible* that CloudKit translates/wraps
all other errors, but that would be odd for a Cocoa framework.

Every usage of these methods is going to require such a cast, so why
require it in the first place? I don’t understand what advantage
erasing the strongly type error that was just created has when the
developer will just have to bring it right back. Or is this just a
first implementation?

There was never a strongly-typed error, and in most Cocoa cases there
shouldn’t be one because NSError covers all error domains, by design.

- Doug

Jon

On Aug 2, 2016, at 4:20 PM, Douglas Gregor <dgregor@apple.com> >>>> wrote:

On Aug 2, 2016, at 10:30 AM, Jon Shier via swift-evolution <swift- >>>>> evolution@swift.org> wrote:

I’m not sure where to put such feedback, but the ErrorProtocol to
Error rename that accompanied the implementation of this proposal
is very, very painful. It completely eliminates the very useful
ability to embed an associated Error type inside other types, as
those types now conflict with the protocol. Also, was this rename
accompanied by an evolution proposal? It seems like the change was
just made when this proposal was implemented.

The rename was part of the proposal, in bullet #5 of the proposed
solution (which, amusing, pastes as bullet #1 below):

1. Rename ErrorProtocol to Error: once we've completed the
    bridging story, Error becomes the primary way to work with
    error types in Swift, and the value type to which NSError is
    bridged:

func handleError(_ error: Error, userInteractionPermitted: Bool)

Also, the adoption of this proposal by the Cocoa(Touch) frameworks
as seen in Xcode 8 beta 4 has made asynchronous error handling
quite a bit more arduous. For example, the CKDatabase method
fetch(withRecordID recordID: CKRecordID, completionHandler:
(CKRecord?, Error?) -> Void) returns an `Error` now, meaning I
have to cast to the specific `CKError` type to get useful
information out of it. Is this just an unfortunate first effort
that will be fixed, or is this the expected form of these sorts of
APIs after this proposal?

Prior to this proposal, you would have had to check the domain
against CKErrorDomain anyway to determine whether you’re looking at
a CloudKit error (vs. some other error that is passing through
CloudKit), so error bridging shouldn’t actually be adding any work
here—although it might be making explicit work that was already
done or should have been done. Once you have casted to CKError, you
now have typed accessors for information in the error:

extension CKError {
  /// Retrieve partial error results associated by item ID.
  public var partialErrorsByItemID: [NSObject : Error]? {
    return userInfo[CKPartialErrorsByItemIDKey] as? [NSObject :
    Error]
  }

  /// The original CKRecord object that you used as the basis for
  /// making your changes.
  public var ancestorRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorAncestorRecordKey] as?
    CKRecord
  }

  /// The CKRecord object that was found on the server. Use this
  /// record as the basis for merging your changes.
  public var serverRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorServerRecordKey] as?
    CKRecord
  }

  /// The CKRecord object that you tried to save. This record is
  based
  /// on the record in the CKRecordChangedErrorAncestorRecordKey
  key
  /// but contains the additional changes you made.
  public var clientRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorClientRecordKey] as?
    CKRecord
  }

  /// The number of seconds after which you may retry a request.
  This
  /// key may be included in an error of type
  /// `CKErrorServiceUnavailable` or `CKErrorRequestRateLimited`.
  public var retryAfterSeconds: Double? {
    return userInfo[CKErrorRetryAfterKey] as? Double
  }
}
- Doug

Jon Shier

On Jul 12, 2016, at 8:44 AM, Shawn Erickson via swift-evolution >>>>>> <swift-evolution@swift.org> wrote:

Thanks for the effort on the proposal and discussion and thanks
to those working in the implementation.

-Shawn
On Tue, Jul 12, 2016 at 12:25 AM Charles Srstka via swift- >>>>>> evolution <swift-evolution@swift.org> wrote:

Wow, thanks! I’m delighted that Apple found this improvement to
be worth inclusion in Swift 3. This will truly make the language
much nicer to use with the Cocoa frameworks.

Thanks!

Charles

> On Jul 11, 2016, at 11:19 PM, Chris Lattner via swift- >>>>>>> > evolution <swift-evolution@swift.org> wrote:
>
> Proposal Link:
> https://github.com/apple/swift-evolution/blob/master/proposals/0112-nserror-bridging.md
>
> The review of "SE-0112: Improved NSError Bridging" ran from
> June 30 ... July 4, 2016. The proposal has been *accepted*:
>
> The community and core team agree that this proposal is a huge
> step forward that enriches the experience working with and
> extending the Cocoa NSError model in Swift. The core team
> requests one minor renaming of
> "attemptRecovery(optionIndex:andThen:)" to
> "attemptRecovery(optionIndex:resultHandler:)”. It also
> discussed renaming CustomNSError and RecoverableError, but
> decided to stay with those names.
>
> Thank you to Doug Gregor and Charles Srstka for driving this
> discussion forward, and for Doug Gregor taking the charge on
> the implementation effort to make this happen for Swift 3!
>
> -Chris Lattner
> Review Manager
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_________________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jean-Daniel) #12

With NSError, you must check the domain before trying to interpret the code, or else your code is buggy and will behave incorrectly when receiving an unexpected error.

You must check before interpreting the code, but you don’t have to interpret the code to do something useful with an NSError.

I think what Jon is looking for is ‘LocalizedError’ and ‘CustomNSError’.
Is there any guarantee that casting an NSError into a CustomNSError or LocalizedError will always succeed ?

···

Le 5 août 2016 à 05:12, Kevin Ballard via swift-evolution <swift-evolution@swift.org> a écrit :

With SE-0112, instead of checking the domain, you check if the Error can be casted to the particular error type that represents the domain. There is a one-to-one correspondence between domains and the new error types. For example, NSCocoaErrorDomain is represented by CocoaError, NSURLErrorDomain is URLError, etc.

So previously you might have code that looks like

func handleError(error: NSError) {
    switch error.domain {
    case NSCocoaErrorDomain where error.code == NSFileNoSuchFileError:
        let path = error.userInfo[NSFilePathErrorKey] as? String
        // handle error for path
    case NSURLErrorDomain where error.code == NSURLErrorTimedOut:
        let url = error.userInfo[NSURLErrorKey] as? NSURL
        // handle error for url
    default:
        // generic handling of other errors
    }
}

And now you'd write that like

func handleError(error: Error) {
    switch error {
    case let error as CocoaError where error.code == .fileNoSuchFileError:
        let path = error.filePath
        // handle error for path
    case let error as URLError where error.code == .timedOut:
        let url = error.failingURL
        // handle error for url
    default:
        // generic handling of other errors
    }
}

It's the same basic structure, except now you get strong typing, you can't possibly forget to check the domain (which is a surprisingly common bug I see in a lot of code), and you get convenient accessors for the values stored in the user info.

And if you don't actually care about any of the user info properties, then the new version is much simpler than the old:

func handleError(error: Error) {
    switch error {
    case CocoaError.fileNoSuchFileError:
        // handle error
    case URLError.timedOut:
        // handle error
    default:
        // generic handling of other errors
    }
}

It's similar to checking the code without the domain in the old style, except now it checks the domain automatically, so you still can't accidentally interpret an error's code in the wrong domain.

-Kevin Ballard

On Thu, Aug 4, 2016, at 11:00 AM, Jon Shier via swift-evolution wrote:

Doug:
Thanks for indulging me so far, I think I’ve almost got it. Prior to this, using NSError, I could just look at the relevant properties of the error if I needed to see what type it was. Network errors had different codes from CloudKit errors, POSIX errors were underlying FileManager errors. A bit complex due to the undocumented nature of so many of these errors, but I could ignore any aspect of the error I didn’t care about. Now, however, it seems I must always care about what types of errors come out of various methods, as I’ll need to cast to the appropriate types to get useful information. For example, how would you handle the CloudKit errors I mentioned before? It seems to me like I would need to, at the point where I need to extract useful information, do a switch on various casts. First, try casting to CKError, then to CocoaError (?), and then likely produce a fatalError if there’s an unexpected type. Or is Error guaranteed to always cast to something useful? I’ve read the proposal a few times now and it looks like a lot of casting is going to be required, I’m mostly curious about the recommended patterns, especially for asynchronous calls that don’t go through throw/catch.

Jon

On Aug 2, 2016, at 5:36 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

On Aug 2, 2016, at 2:19 PM, Jon Shier <jon@jonshier.com <mailto:jon@jonshier.com>> wrote:

Thanks Doug. I missed the rename, as earlier points still referred to ErrorProtocol. In regards to the CloudKit errors, I appreciate the strongly typed CKError, but why not have the methods return that type directly?

Generally speaking, Cocoa only uses NSError—not specific subclasses or NSError or other error types—because errors can occur at many different places in the stack and be propagated up. A CloudKit operation could fail because of some problem detected in a different error domain—say, the general Cocoa error domain or URLError domain—and that non-CloudKit error would get passed through immediately. So, if you were assuming that every error you get here had to be in the CloudKit error domain, I believe your code was already incorrect. It is *possible* that CloudKit translates/wraps all other errors, but that would be odd for a Cocoa framework.

Every usage of these methods is going to require such a cast, so why require it in the first place? I don’t understand what advantage erasing the strongly type error that was just created has when the developer will just have to bring it right back. Or is this just a first implementation?

There was never a strongly-typed error, and in most Cocoa cases there shouldn’t be one because NSError covers all error domains, by design.

- Doug

Jon

On Aug 2, 2016, at 4:20 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

On Aug 2, 2016, at 10:30 AM, Jon Shier via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I’m not sure where to put such feedback, but the ErrorProtocol to Error rename that accompanied the implementation of this proposal is very, very painful. It completely eliminates the very useful ability to embed an associated Error type inside other types, as those types now conflict with the protocol. Also, was this rename accompanied by an evolution proposal? It seems like the change was just made when this proposal was implemented.

The rename was part of the proposal, in bullet #5 of the proposed solution (which, amusing, pastes as bullet #1 below):

Rename ErrorProtocol to Error: once we've completed the bridging story, Error becomes the primary way to work with error types in Swift, and the value type to which NSError is bridged:

func handleError(_ error: Error, userInteractionPermitted: Bool)

Also, the adoption of this proposal by the Cocoa(Touch) frameworks as seen in Xcode 8 beta 4 has made asynchronous error handling quite a bit more arduous. For example, the CKDatabase method fetch(withRecordID recordID: CKRecordID, completionHandler: (CKRecord?, Error?) -> Void) returns an `Error` now, meaning I have to cast to the specific `CKError` type to get useful information out of it. Is this just an unfortunate first effort that will be fixed, or is this the expected form of these sorts of APIs after this proposal?

Prior to this proposal, you would have had to check the domain against CKErrorDomain anyway to determine whether you’re looking at a CloudKit error (vs. some other error that is passing through CloudKit), so error bridging shouldn’t actually be adding any work here—although it might be making explicit work that was already done or should have been done. Once you have casted to CKError, you now have typed accessors for information in the error:

extension CKError {
  /// Retrieve partial error results associated by item ID.
  public var partialErrorsByItemID: [NSObject : Error]? {
    return userInfo[CKPartialErrorsByItemIDKey] as? [NSObject : Error]
  }

  /// The original CKRecord object that you used as the basis for
  /// making your changes.
  public var ancestorRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorAncestorRecordKey] as? CKRecord
  }

  /// The CKRecord object that was found on the server. Use this
  /// record as the basis for merging your changes.
  public var serverRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorServerRecordKey] as? CKRecord
  }

  /// The CKRecord object that you tried to save. This record is based
  /// on the record in the CKRecordChangedErrorAncestorRecordKey key
  /// but contains the additional changes you made.
  public var clientRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorClientRecordKey] as? CKRecord
  }

  /// The number of seconds after which you may retry a request. This
  /// key may be included in an error of type
  /// `CKErrorServiceUnavailable` or `CKErrorRequestRateLimited`.
  public var retryAfterSeconds: Double? {
    return userInfo[CKErrorRetryAfterKey] as? Double
  }
}
- Doug

Jon Shier

On Jul 12, 2016, at 8:44 AM, Shawn Erickson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Thanks for the effort on the proposal and discussion and thanks to those working in the implementation.

-Shawn
On Tue, Jul 12, 2016 at 12:25 AM Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Wow, thanks! I’m delighted that Apple found this improvement to be worth inclusion in Swift 3. This will truly make the language much nicer to use with the Cocoa frameworks.

Thanks!

Charles

> On Jul 11, 2016, at 11:19 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>
> Proposal Link: https://github.com/apple/swift-evolution/blob/master/proposals/0112-nserror-bridging.md
>
> The review of "SE-0112: Improved NSError Bridging" ran from June 30 ... July 4, 2016. The proposal has been *accepted*:
>
> The community and core team agree that this proposal is a huge step forward that enriches the experience working with and extending the Cocoa NSError model in Swift. The core team requests one minor renaming of "attemptRecovery(optionIndex:andThen:)" to "attemptRecovery(optionIndex:resultHandler:)”. It also discussed renaming CustomNSError and RecoverableError, but decided to stay with those names.
>
> Thank you to Doug Gregor and Charles Srstka for driving this discussion forward, and for Doug Gregor taking the charge on the implementation effort to make this happen for Swift 3!
>
> -Chris Lattner
> Review Manager
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Lily Ballard) #13

If all you want to do is get the localized description, then you can
just say `(error as NSError).localizedDescription`.

-Kevin

···

On Fri, Aug 5, 2016, at 02:59 AM, Jean-Daniel Dupas wrote:

Le 5 août 2016 à 05:12, Kevin Ballard via swift-evolution <swift- >> evolution@swift.org> a écrit :

With NSError, you *must* check the domain before trying to interpret
the code, or else your code is buggy and will behave incorrectly when
receiving an unexpected error.

You must check before interpreting the code, but you don’t have to
interpret the code to do something useful with an NSError.

I think what Jon is looking for is ‘LocalizedError’ and
‘CustomNSError’.
Is there any guarantee that casting an NSError into a CustomNSError or
LocalizedError will always succeed ?

With SE-0112, instead of checking the domain, you check if the Error
can be casted to the particular error type that represents the
domain. There is a one-to-one correspondence between domains and the
new error types. For example, NSCocoaErrorDomain is represented by
CocoaError, NSURLErrorDomain is URLError, etc.

So previously you might have code that looks like

func handleError(error: NSError) {
    switch error.domain {
    case NSCocoaErrorDomain where error.code ==
    NSFileNoSuchFileError:
        let path = error.userInfo[NSFilePathErrorKey] as? String
        // handle error for path
    case NSURLErrorDomain where error.code == NSURLErrorTimedOut:
        let url = error.userInfo[NSURLErrorKey] as? NSURL
        // handle error for url
    default:
        // generic handling of other errors
    }
}

And now you'd write that like

func handleError(error: Error) {
    switch error {
    case let error as CocoaError where error.code ==
    .fileNoSuchFileError:
        let path = error.filePath
        // handle error for path
    case let error as URLError where error.code == .timedOut:
        let url = error.failingURL
        // handle error for url
    default:
        // generic handling of other errors
    }
}

It's the same basic structure, except now you get strong typing, you
can't possibly forget to check the domain (which is a surprisingly
common bug I see in a lot of code), and you get convenient accessors
for the values stored in the user info.

And if you don't actually care about any of the user info properties,
then the new version is much simpler than the old:

func handleError(error: Error) {
    switch error {
    case CocoaError.fileNoSuchFileError:
        // handle error
    case URLError.timedOut:
        // handle error
    default:
        // generic handling of other errors
    }
}

It's similar to checking the code without the domain in the old
style, except now it checks the domain automatically, so you *still*
can't accidentally interpret an error's code in the wrong domain.

-Kevin Ballard

On Thu, Aug 4, 2016, at 11:00 AM, Jon Shier via swift- >> evolution wrote:

Doug:
Thanks for indulging me so far, I think I’ve almost got it. Prior to
this, using NSError, I could just look at the relevant properties of
the error if I needed to see what type it was. Network errors had
different codes from CloudKit errors, POSIX errors were underlying
FileManager errors. A bit complex due to the undocumented nature of
so many of these errors, but I could ignore any aspect of the error
I didn’t care about. Now, however, it seems I must always care about
what types of errors come out of various methods, as I’ll need to
cast to the appropriate types to get useful information. For
example, how would you handle the CloudKit errors I mentioned
before? It seems to me like I would need to, at the point where I
need to extract useful information, do a switch on various casts.
First, try casting to CKError, then to CocoaError (?), and then
likely produce a fatalError if there’s an unexpected type. Or is
Error guaranteed to always cast to something useful? I’ve read the
proposal a few times now and it looks like a lot of casting is going
to be required, I’m mostly curious about the recommended patterns,
especially for asynchronous calls that don’t go through throw/catch.

Jon

On Aug 2, 2016, at 5:36 PM, Douglas Gregor <dgregor@apple.com> >>>> wrote:

On Aug 2, 2016, at 2:19 PM, Jon Shier <jon@jonshier.com> wrote:

Thanks Doug. I missed the rename, as earlier points still referred
to ErrorProtocol. In regards to the CloudKit errors, I appreciate
the strongly typed CKError, but why not have the methods return
that type directly?

Generally speaking, Cocoa only uses NSError—not specific subclasses
or NSError or other error types—because errors can occur at many
different places in the stack and be propagated up. A CloudKit
operation could fail because of some problem detected in a
different error domain—say, the general Cocoa error domain or
URLError domain—and that non-CloudKit error would get passed
through immediately. So, if you were assuming that every error you
get here had to be in the CloudKit error domain, I believe your
code was already incorrect. It is *possible* that CloudKit
translates/wraps all other errors, but that would be odd for a
Cocoa framework.

Every usage of these methods is going to require such a cast, so
why require it in the first place? I don’t understand what
advantage erasing the strongly type error that was just created
has when the developer will just have to bring it right back. Or
is this just a first implementation?

There was never a strongly-typed error, and in most Cocoa cases
there shouldn’t be one because NSError covers all error domains, by
design.

- Doug

Jon

On Aug 2, 2016, at 4:20 PM, Douglas Gregor <dgregor@apple.com> >>>>>> wrote:

On Aug 2, 2016, at 10:30 AM, Jon Shier via swift-evolution <swift- >>>>>>> evolution@swift.org> wrote:

I’m not sure where to put such feedback, but the ErrorProtocol
to Error rename that accompanied the implementation of this
proposal is very, very painful. It completely eliminates the
very useful ability to embed an associated Error type inside
other types, as those types now conflict with the protocol.
Also, was this rename accompanied by an evolution proposal? It
seems like the change was just made when this proposal was
implemented.

The rename was part of the proposal, in bullet #5 of the proposed
solution (which, amusing, pastes as bullet #1 below):

1. Rename ErrorProtocol to Error: once we've completed the
    bridging story, Error becomes the primary way to work with
    error types in Swift, and the value type to which NSError is
    bridged:

func handleError(_ error: Error, userInteractionPermitted: Bool)

Also, the adoption of this proposal by the Cocoa(Touch)
frameworks as seen in Xcode 8 beta 4 has made asynchronous error
handling quite a bit more arduous. For example, the CKDatabase
method fetch(withRecordID recordID: CKRecordID,
completionHandler: (CKRecord?, Error?) -> Void) returns an
`Error` now, meaning I have to cast to the specific `CKError`
type to get useful information out of it. Is this just an
unfortunate first effort that will be fixed, or is this the
expected form of these sorts of APIs after this proposal?

Prior to this proposal, you would have had to check the domain
against CKErrorDomain anyway to determine whether you’re looking
at a CloudKit error (vs. some other error that is passing through
CloudKit), so error bridging shouldn’t actually be adding any
work here—although it might be making explicit work that was
already done or should have been done. Once you have casted to
CKError, you now have typed accessors for information in the
error:

extension CKError {
  /// Retrieve partial error results associated by item ID.
  public var partialErrorsByItemID: [NSObject : Error]? {
    return userInfo[CKPartialErrorsByItemIDKey] as? [NSObject :
    Error]
  }

  /// The original CKRecord object that you used as the basis for
  /// making your changes.
  public var ancestorRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorAncestorRecordKey] as?
    CKRecord
  }

  /// The CKRecord object that was found on the server. Use this
  /// record as the basis for merging your changes.
  public var serverRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorServerRecordKey] as?
    CKRecord
  }

  /// The CKRecord object that you tried to save. This record is
  based
  /// on the record in the CKRecordChangedErrorAncestorRecordKey
  key
  /// but contains the additional changes you made.
  public var clientRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorClientRecordKey] as?
    CKRecord
  }

  /// The number of seconds after which you may retry a request.
  This
  /// key may be included in an error of type
  /// `CKErrorServiceUnavailable` or `CKErrorRequestRateLimited`.
  public var retryAfterSeconds: Double? {
    return userInfo[CKErrorRetryAfterKey] as? Double
  }
}
- Doug

Jon Shier

On Jul 12, 2016, at 8:44 AM, Shawn Erickson via swift-evolution >>>>>>>> <swift-evolution@swift.org> wrote:

Thanks for the effort on the proposal and discussion and thanks
to those working in the implementation.

-Shawn
On Tue, Jul 12, 2016 at 12:25 AM Charles Srstka via swift- >>>>>>>> evolution <swift-evolution@swift.org> wrote:

Wow, thanks! I’m delighted that Apple found this improvement
to be worth inclusion in Swift 3. This will truly make the
language much nicer to use with the Cocoa frameworks.

Thanks!

Charles

> On Jul 11, 2016, at 11:19 PM, Chris Lattner via swift- >>>>>>>>> > evolution <swift-evolution@swift.org> wrote:
>
> Proposal Link:
> https://github.com/apple/swift-evolution/blob/master/proposals/0112-nserror-bridging.md
>
> The review of "SE-0112: Improved NSError Bridging" ran from
> June 30 ... July 4, 2016. The proposal has been *accepted*:
>
> The community and core team agree that this proposal is a
> huge step forward that enriches the experience working with
> and extending the Cocoa NSError model in Swift. The core
> team requests one minor renaming of
> "attemptRecovery(optionIndex:andThen:)" to
> "attemptRecovery(optionIndex:resultHandler:)”. It also
> discussed renaming CustomNSError and RecoverableError, but
> decided to stay with those names.
>
> Thank you to Doug Gregor and Charles Srstka for driving this
> discussion forward, and for Doug Gregor taking the charge on
> the implementation effort to make this happen for Swift 3!
>
> -Chris Lattner
> Review Manager
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_________________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Douglas Gregor) #14

If all you want to do is get the localized description, then you can just say `(error as NSError).localizedDescription`.

Just ‘error.localizedDescription’ works now. That was part of SE-0112.

  - Doug

···

On Aug 5, 2016, at 12:59 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

-Kevin

On Fri, Aug 5, 2016, at 02:59 AM, Jean-Daniel Dupas wrote:

Le 5 août 2016 à 05:12, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

With NSError, you must check the domain before trying to interpret the code, or else your code is buggy and will behave incorrectly when receiving an unexpected error.

You must check before interpreting the code, but you don’t have to interpret the code to do something useful with an NSError.

I think what Jon is looking for is ‘LocalizedError’ and ‘CustomNSError’.
Is there any guarantee that casting an NSError into a CustomNSError or LocalizedError will always succeed ?

With SE-0112, instead of checking the domain, you check if the Error can be casted to the particular error type that represents the domain. There is a one-to-one correspondence between domains and the new error types. For example, NSCocoaErrorDomain is represented by CocoaError, NSURLErrorDomain is URLError, etc.

So previously you might have code that looks like

func handleError(error: NSError) {
    switch error.domain {
    case NSCocoaErrorDomain where error.code == NSFileNoSuchFileError:
        let path = error.userInfo[NSFilePathErrorKey] as? String
        // handle error for path
    case NSURLErrorDomain where error.code == NSURLErrorTimedOut:
        let url = error.userInfo[NSURLErrorKey] as? NSURL
        // handle error for url
    default:
        // generic handling of other errors
    }
}

And now you'd write that like

func handleError(error: Error) {
    switch error {
    case let error as CocoaError where error.code == .fileNoSuchFileError:
        let path = error.filePath
        // handle error for path
    case let error as URLError where error.code == .timedOut:
        let url = error.failingURL
        // handle error for url
    default:
        // generic handling of other errors
    }
}

It's the same basic structure, except now you get strong typing, you can't possibly forget to check the domain (which is a surprisingly common bug I see in a lot of code), and you get convenient accessors for the values stored in the user info.

And if you don't actually care about any of the user info properties, then the new version is much simpler than the old:

func handleError(error: Error) {
    switch error {
    case CocoaError.fileNoSuchFileError:
        // handle error
    case URLError.timedOut:
        // handle error
    default:
        // generic handling of other errors
    }
}

It's similar to checking the code without the domain in the old style, except now it checks the domain automatically, so you still can't accidentally interpret an error's code in the wrong domain.

-Kevin Ballard

On Thu, Aug 4, 2016, at 11:00 AM, Jon Shier via swift-evolution wrote:

Doug:
Thanks for indulging me so far, I think I’ve almost got it. Prior to this, using NSError, I could just look at the relevant properties of the error if I needed to see what type it was. Network errors had different codes from CloudKit errors, POSIX errors were underlying FileManager errors. A bit complex due to the undocumented nature of so many of these errors, but I could ignore any aspect of the error I didn’t care about. Now, however, it seems I must always care about what types of errors come out of various methods, as I’ll need to cast to the appropriate types to get useful information. For example, how would you handle the CloudKit errors I mentioned before? It seems to me like I would need to, at the point where I need to extract useful information, do a switch on various casts. First, try casting to CKError, then to CocoaError (?), and then likely produce a fatalError if there’s an unexpected type. Or is Error guaranteed to always cast to something useful? I’ve read the proposal a few times now and it looks like a lot of casting is going to be required, I’m mostly curious about the recommended patterns, especially for asynchronous calls that don’t go through throw/catch.

Jon

On Aug 2, 2016, at 5:36 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

On Aug 2, 2016, at 2:19 PM, Jon Shier <jon@jonshier.com <mailto:jon@jonshier.com>> wrote:

Thanks Doug. I missed the rename, as earlier points still referred to ErrorProtocol. In regards to the CloudKit errors, I appreciate the strongly typed CKError, but why not have the methods return that type directly?

Generally speaking, Cocoa only uses NSError—not specific subclasses or NSError or other error types—because errors can occur at many different places in the stack and be propagated up. A CloudKit operation could fail because of some problem detected in a different error domain—say, the general Cocoa error domain or URLError domain—and that non-CloudKit error would get passed through immediately. So, if you were assuming that every error you get here had to be in the CloudKit error domain, I believe your code was already incorrect. It is *possible* that CloudKit translates/wraps all other errors, but that would be odd for a Cocoa framework.

Every usage of these methods is going to require such a cast, so why require it in the first place? I don’t understand what advantage erasing the strongly type error that was just created has when the developer will just have to bring it right back. Or is this just a first implementation?

There was never a strongly-typed error, and in most Cocoa cases there shouldn’t be one because NSError covers all error domains, by design.

- Doug

Jon

On Aug 2, 2016, at 4:20 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

On Aug 2, 2016, at 10:30 AM, Jon Shier via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I’m not sure where to put such feedback, but the ErrorProtocol to Error rename that accompanied the implementation of this proposal is very, very painful. It completely eliminates the very useful ability to embed an associated Error type inside other types, as those types now conflict with the protocol. Also, was this rename accompanied by an evolution proposal? It seems like the change was just made when this proposal was implemented.

The rename was part of the proposal, in bullet #5 of the proposed solution (which, amusing, pastes as bullet #1 below):

Rename ErrorProtocol to Error: once we've completed the bridging story, Error becomes the primary way to work with error types in Swift, and the value type to which NSError is bridged:

func handleError(_ error: Error, userInteractionPermitted: Bool)

Also, the adoption of this proposal by the Cocoa(Touch) frameworks as seen in Xcode 8 beta 4 has made asynchronous error handling quite a bit more arduous. For example, the CKDatabase method fetch(withRecordID recordID: CKRecordID, completionHandler: (CKRecord?, Error?) -> Void) returns an `Error` now, meaning I have to cast to the specific `CKError` type to get useful information out of it. Is this just an unfortunate first effort that will be fixed, or is this the expected form of these sorts of APIs after this proposal?

Prior to this proposal, you would have had to check the domain against CKErrorDomain anyway to determine whether you’re looking at a CloudKit error (vs. some other error that is passing through CloudKit), so error bridging shouldn’t actually be adding any work here—although it might be making explicit work that was already done or should have been done. Once you have casted to CKError, you now have typed accessors for information in the error:

extension CKError {
  /// Retrieve partial error results associated by item ID.
  public var partialErrorsByItemID: [NSObject : Error]? {
    return userInfo[CKPartialErrorsByItemIDKey] as? [NSObject : Error]
  }

  /// The original CKRecord object that you used as the basis for
  /// making your changes.
  public var ancestorRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorAncestorRecordKey] as? CKRecord
  }

  /// The CKRecord object that was found on the server. Use this
  /// record as the basis for merging your changes.
  public var serverRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorServerRecordKey] as? CKRecord
  }

  /// The CKRecord object that you tried to save. This record is based
  /// on the record in the CKRecordChangedErrorAncestorRecordKey key
  /// but contains the additional changes you made.
  public var clientRecord: CKRecord? {
    return userInfo[CKRecordChangedErrorClientRecordKey] as? CKRecord
  }

  /// The number of seconds after which you may retry a request. This
  /// key may be included in an error of type
  /// `CKErrorServiceUnavailable` or `CKErrorRequestRateLimited`.
  public var retryAfterSeconds: Double? {
    return userInfo[CKErrorRetryAfterKey] as? Double
  }
}
- Doug

Jon Shier

On Jul 12, 2016, at 8:44 AM, Shawn Erickson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Thanks for the effort on the proposal and discussion and thanks to those working in the implementation.

-Shawn
On Tue, Jul 12, 2016 at 12:25 AM Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Wow, thanks! I’m delighted that Apple found this improvement to be worth inclusion in Swift 3. This will truly make the language much nicer to use with the Cocoa frameworks.

Thanks!

Charles

> On Jul 11, 2016, at 11:19 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>
> Proposal Link: https://github.com/apple/swift-evolution/blob/master/proposals/0112-nserror-bridging.md
>
> The review of "SE-0112: Improved NSError Bridging" ran from June 30 ... July 4, 2016. The proposal has been *accepted*:
>
> The community and core team agree that this proposal is a huge step forward that enriches the experience working with and extending the Cocoa NSError model in Swift. The core team requests one minor renaming of "attemptRecovery(optionIndex:andThen:)" to "attemptRecovery(optionIndex:resultHandler:)”. It also discussed renaming CustomNSError and RecoverableError, but decided to stay with those names.
>
> Thank you to Doug Gregor and Charles Srstka for driving this discussion forward, and for Doug Gregor taking the charge on the implementation effort to make this happen for Swift 3!
>
> -Chris Lattner
> Review Manager
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Erica Sadun) #15

Would it kill to allow:

let err = NSError()
err.localizedDescription = "bad things happen"
throw err

or even

throw NSError("Bad things happen")

for lightweight use? I ended up refactoring entirely to enum : Error because Swift yelled at me for using NSError(): "this results in an invalid NSError instance. It will raise an exception in a future release. Please call errorWithDomain:code:userInfo: or initWithDomain:code:userInfo:. This message shown only once."

enum Errors: Error {case bad}
Errors.bad._code // 0
Errors.bad._domain // "Errors"
Errors.bad._userInfo // Optional({})
Errors.bad.localizedDescription // "The operation couldn’t be completed. (Errors error 0.)"

Bleh.

-- E

···

On Aug 5, 2016, at 4:19 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On Aug 5, 2016, at 12:59 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

If all you want to do is get the localized description, then you can just say `(error as NSError).localizedDescription`.

Just ‘error.localizedDescription’ works now. That was part of SE-0112.

  - Doug


(Lily Ballard) #16

NSErrors need a domain/code. It doesn’t make much sense to throw one without it. And besides, there’s a fairly trivial solution for doing what you want to do:

struct GenericError: LocalizedError {
    let message: String
    init(_ message: String) {
        self.message = message
    }
    var errorDescription: String? {
        return message
    }
}

Now you can just say `throw GenericError(“Bad things happen”)`.

-Kevin Ballard

···

On Aug 5, 2016, at 5:16 PM, Erica Sadun <erica@ericasadun.com> wrote:

On Aug 5, 2016, at 4:19 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Aug 5, 2016, at 12:59 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

If all you want to do is get the localized description, then you can just say `(error as NSError).localizedDescription`.

Just ‘error.localizedDescription’ works now. That was part of SE-0112.

  - Doug

Would it kill to allow:

let err = NSError()
err.localizedDescription = "bad things happen"
throw err

or even

throw NSError("Bad things happen")

for lightweight use? I ended up refactoring entirely to enum : Error because Swift yelled at me for using NSError(): "this results in an invalid NSError instance. It will raise an exception in a future release. Please call errorWithDomain:code:userInfo: or initWithDomain:code:userInfo:. This message shown only once."

enum Errors: Error {case bad}
Errors.bad._code // 0
Errors.bad._domain // "Errors"
Errors.bad._userInfo // Optional({})
Errors.bad.localizedDescription // "The operation couldn’t be completed. (Errors error 0.)"

Bleh.


(Charles Srstka) #17

You can make something that can do that fairly easily:

struct TextualError: LocalizedError {
    var errorString: String
    
    init() {
        self.init("")
    }
    
    init(_ string: String) {
        self.errorString = string
    }
    
    var failureReason: String? {
        return self.errorString
    }
}

Then, you can just:

throw TextualError("Must Sterilize!”)

Or:

var error = TextualError()
error.failureReason = "Must Sterilize!”
throw error

Charles

···

On Aug 5, 2016, at 7:16 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

Would it kill to allow:

let err = NSError()
err.localizedDescription = "bad things happen"
throw err

or even

throw NSError("Bad things happen")


(Erica Sadun) #18

I know I can build workarounds but if we're going to have the error.localizedDescription, making it an initializable/assignable property just seems like a nice thing™. Why can't we have nice things™?

-- E

···

On Aug 5, 2016, at 8:10 PM, Kevin Ballard <kevin@sb.org> wrote:

On Aug 5, 2016, at 5:16 PM, Erica Sadun <erica@ericasadun.com <mailto:erica@ericasadun.com>> wrote:

On Aug 5, 2016, at 4:19 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Aug 5, 2016, at 12:59 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

If all you want to do is get the localized description, then you can just say `(error as NSError).localizedDescription`.

Just ‘error.localizedDescription’ works now. That was part of SE-0112.

  - Doug

Would it kill to allow:

let err = NSError()
err.localizedDescription = "bad things happen"
throw err

or even

throw NSError("Bad things happen")

for lightweight use? I ended up refactoring entirely to enum : Error because Swift yelled at me for using NSError(): "this results in an invalid NSError instance. It will raise an exception in a future release. Please call errorWithDomain:code:userInfo: or initWithDomain:code:userInfo:. This message shown only once."

enum Errors: Error {case bad}
Errors.bad._code // 0
Errors.bad._domain // "Errors"
Errors.bad._userInfo // Optional({})
Errors.bad.localizedDescription // "The operation couldn’t be completed. (Errors error 0.)"

Bleh.

NSErrors need a domain/code. It doesn’t make much sense to throw one without it. And besides, there’s a fairly trivial solution for doing what you want to do:

struct GenericError: LocalizedError {
    let message: String
    init(_ message: String) {
        self.message = message
    }
    var errorDescription: String? {
        return message
    }
}

Now you can just say `throw GenericError(“Bad things happen”)`.

-Kevin Ballard


(Lily Ballard) #19

I don’t actually think it’s a nice thing™ to have it be assignable like you ask, because we should be encouraging people to use typed errors. You may as well just ask for String to conform to Error (in fact, you could just add that conformance yourself and skip the GenericError wrapper entirely).

-Kevin

···

On Aug 5, 2016, at 7:36 PM, Erica Sadun <erica@ericasadun.com> wrote:

On Aug 5, 2016, at 8:10 PM, Kevin Ballard <kevin@sb.org <mailto:kevin@sb.org>> wrote:

On Aug 5, 2016, at 5:16 PM, Erica Sadun <erica@ericasadun.com <mailto:erica@ericasadun.com>> wrote:

On Aug 5, 2016, at 4:19 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Aug 5, 2016, at 12:59 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

If all you want to do is get the localized description, then you can just say `(error as NSError).localizedDescription`.

Just ‘error.localizedDescription’ works now. That was part of SE-0112.

  - Doug

Would it kill to allow:

let err = NSError()
err.localizedDescription = "bad things happen"
throw err

or even

throw NSError("Bad things happen")

for lightweight use? I ended up refactoring entirely to enum : Error because Swift yelled at me for using NSError(): "this results in an invalid NSError instance. It will raise an exception in a future release. Please call errorWithDomain:code:userInfo: or initWithDomain:code:userInfo:. This message shown only once."

enum Errors: Error {case bad}
Errors.bad._code // 0
Errors.bad._domain // "Errors"
Errors.bad._userInfo // Optional({})
Errors.bad.localizedDescription // "The operation couldn’t be completed. (Errors error 0.)"

Bleh.

NSErrors need a domain/code. It doesn’t make much sense to throw one without it. And besides, there’s a fairly trivial solution for doing what you want to do:

struct GenericError: LocalizedError {
    let message: String
    init(_ message: String) {
        self.message = message
    }
    var errorDescription: String? {
        return message
    }
}

Now you can just say `throw GenericError(“Bad things happen”)`.

-Kevin Ballard

I know I can build workarounds but if we're going to have the error.localizedDescription, making it an initializable/assignable property just seems like a nice thing™. Why can't we have nice things™?


(Jon Shier) #20

Doug, et. al.:
  Thanks for the discussion so far; I think I understand the new error model from a user’s perspective. However, now I’m looking for some guidance for framework developers. While the proposal laid out the system types would wrap imported Objective-C errors, I don’t see anything for the desired patters for Swift native errors. For instance, in Alamofire we currently either pass through the NSErrors we receive from underlying frameworks or generate our own, usually in our own domain, with unique error codes and such. I can see my way to translating most of these directly to an AFError type that conforms to Error, but some of them require associated values (e.g. the content type validation error needs to know the expected and actual content types). In these cases I’m having a hard time see how these things should be stored, especially when only some cases need this data. Of course, I’m away of the LocalizedError protocol, but that doesn’t seem applicable here, as I’m mostly wondering about storage. Rather than creating types similar to the system error types, perhaps a basic enum based error would work, where only the cases that need it capture values? I’m mostly curious what the anticipated pattern was here.

Jon

···

On Aug 6, 2016, at 1:15 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

On Aug 5, 2016, at 7:36 PM, Erica Sadun <erica@ericasadun.com <mailto:erica@ericasadun.com>> wrote:

On Aug 5, 2016, at 8:10 PM, Kevin Ballard <kevin@sb.org <mailto:kevin@sb.org>> wrote:

On Aug 5, 2016, at 5:16 PM, Erica Sadun <erica@ericasadun.com <mailto:erica@ericasadun.com>> wrote:

On Aug 5, 2016, at 4:19 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Aug 5, 2016, at 12:59 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

If all you want to do is get the localized description, then you can just say `(error as NSError).localizedDescription`.

Just ‘error.localizedDescription’ works now. That was part of SE-0112.

  - Doug

Would it kill to allow:

let err = NSError()
err.localizedDescription = "bad things happen"
throw err

or even

throw NSError("Bad things happen")

for lightweight use? I ended up refactoring entirely to enum : Error because Swift yelled at me for using NSError(): "this results in an invalid NSError instance. It will raise an exception in a future release. Please call errorWithDomain:code:userInfo: or initWithDomain:code:userInfo:. This message shown only once."

enum Errors: Error {case bad}
Errors.bad._code // 0
Errors.bad._domain // "Errors"
Errors.bad._userInfo // Optional({})
Errors.bad.localizedDescription // "The operation couldn’t be completed. (Errors error 0.)"

Bleh.

NSErrors need a domain/code. It doesn’t make much sense to throw one without it. And besides, there’s a fairly trivial solution for doing what you want to do:

struct GenericError: LocalizedError {
    let message: String
    init(_ message: String) {
        self.message = message
    }
    var errorDescription: String? {
        return message
    }
}

Now you can just say `throw GenericError(“Bad things happen”)`.

-Kevin Ballard

I know I can build workarounds but if we're going to have the error.localizedDescription, making it an initializable/assignable property just seems like a nice thing™. Why can't we have nice things™?

I don’t actually think it’s a nice thing™ to have it be assignable like you ask, because we should be encouraging people to use typed errors. You may as well just ask for String to conform to Error (in fact, you could just add that conformance yourself and skip the GenericError wrapper entirely).

-Kevin
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution