Fernando Castor
Fernando Castor

Reputation: 51

NSError and Error in Swift

I've seen that there is already a question on the difference between NSError and Error in Swift and I am aware of the differences. However, I do not understand the behavior of the code snippet below, since it compiles correctly. Swift's error handling mechanism requires that every do-catch block not appearing in a function with a throws clause to include one generic handler (either catch with nothing else or a catch for Error). However, it appears that using NSError has the same effect. Does anybody here know the reason why? I've read in another question that "any ErrorType-conformant class can be casted to NSError. These features are described here in the docs.". However, the documentation pointed to by that answer did not clarify this point to me, since it focuses on Objective-C, which I am not using.

import Foundation
extension Int : Error {}
extension String : Error {}

func fErr() throws {
  let a = Int(readLine()!)!
  if a > 20 {
    throw 42
  } else {
    throw "An error occurred"
  }
}

func gErr() {
  do {
    try fErr()
  } catch let e as NSError {
    print(e)
  }
}

Upvotes: 5

Views: 5232

Answers (2)

Itai Ferber
Itai Ferber

Reputation: 29928

On Darwin platforms (macOS, iOS, tvOS, watchOS), Swift has a concept called bridging which allows Swift types offered by the standard library to bridge to Objective-C types (usually vended by the Cocoa frameworks). Types which do this include

  • Array ↔︎ NSArray
  • Dictionary ↔︎ NSDictionary
  • Set ↔︎ NSSet
  • Error ↔︎ NSError
  • etc. (see the above link for a more comprehensive list)

Bridging types requires compiler support, and the Error ↔︎ NSError bridging has quite a few components (among others). This bridging, though, allows the compiler to generally treat NSErrors as Errors and vice versa; in fact, as you can see in the snippet above, any Error can be cast to an NSError and back.

You can see some of the runtime components needed to make this happen in another answer of mine on custom NSErrors, but the gist is that any Error can provide enough information to be wrapped up in an NSError instance (by implementing var _domain, var _code, and var _userInfo), and vice versa.

When you catch an error as NSError, you implicitly bridge the Error (no matter what it is) into an NSError, in the same way that you can bridge [1, 2, 3] as NSArray or ["foo" : "bar"] as NSDictionary.

[Note that bridging is currently only available where the Objective-C runtime is available, i.e. only on Darwin platforms. Such bridging calls do not work on Linux.]

See also SE-0112 Improved NSError Bridging (the original Swift Evolution proposal) for more background on this and further detail.

Upvotes: 7

Vyacheslav
Vyacheslav

Reputation: 27221

As I can see there is an implicit conversion somewhere of the Swift source code.

One of the possible proofs is this part of C++ code Optional<SILDeclRef> ErrorToNSErrorFn;

Upvotes: 1

Related Questions