Obviously, I’m in favor of this one. +1! I think I did prefer the older name of CustomUserInfoError for the domain/code/userInfo protocol, rather than CustomNSError. This is just because I’d like to be able to do a global search through my project for “NSError” and have it turn up empty. Maybe a silly reason, I know. ;-)
I sent a few more comments off-list before I realized you’d posted the proposal already, so you can peruse, consider, and/or discard those at your leisure. It’s nothing terribly important, though; this proposal is pretty great as is. Thanks so much for doing this! I’m really excited for better error handling in Swift 3. Charles > On Jun 27, 2016, at 1:17 PM, Douglas Gregor <[email protected]> wrote: > > Hi all, > > Proposal link: > https://github.com/DougGregor/swift-evolution/blob/nserror-bridging/proposals/0000-nserror-bridging.md > > Here is a detailed proposal draft for bridging NSError to ErrorProtocol. > Getting this right is surprisingly involved, so the detailed design on this > proposal is fairly large. Comments welcome! > > - Doug > > NSError Bridging > > • Proposal: SE-NNNN > • Author: Doug Gregor, Charles Srstka > • Status: Awaiting review > • Review manager: TBD > Introduction > > Swift's error handling model interoperates directly with Cocoa's NSError > conventions. For example, an Objective-C method with an NSError** parameter, > e.g., > > - (nullable NSURL *)replaceItemAtURL:(NSURL *)url > options:(NSFileVersionReplacingOptions)options error:(NSError **)error; > will be imported as a throwing method: > > func replaceItem(at url: URL, options: ReplacingOptions = []) throws -> URL > Swift bridges between ErrorProtocol-conforming types and NSError so, for > example, a Swift enum that conforms toErrorProtocol can be thrown and will be > reflected as an NSError with a suitable domain and code. Moreover, an > NSErrorproduced with that domain and code can be caught as the Swift enum > type, providing round-tripping so that Swift can deal in ErrorProtocol values > while Objective-C deals in NSError objects. > > However, the interoperability is incomplete in a number of ways, which > results in Swift programs having to walk a careful line between the > ErrorProtocol-based Swift way and the NSError-based way. This proposal > attempts to bridge those gaps. > > Swift-evolution thread: Charles Srstka's pitch for Consistent bridging for > NSErrors at the language boundary, which discussed Charles' original proposal > that addressed these issues by providing NSError to ErrorProtocol bridging > and exposing the domain, code, and user-info dictionary for all errors. This > proposal expands upon that work, but without directly exposing the domain, > code, and user-info. > > Motivation > > There are a number of weaknesses in Swift's interoperability with Cocoa's > error model, including: > > • There is no good way to provide important error information when > throwing an error from Swift. For example, let's consider a simple > application-defined error in Swift: > > enum HomeworkError : Int > , ErrorProtocol { > > case > forgotten > > case > lost > > case > dogAteIt > } > > One can throw HomeworkError.dogAteIt and it can be interpreted as an NSError > by Objective-C with an appropriate error domain (effectively, the mangled > name of the HomeworkError type) and code (effectively, the case > discriminator). However, one cannot provide a localized description, help > anchor, recovery attempter, or any other information commonly placed into the > userInfo dictionary of an NSError. To provide these values, one must > specifically construct an NSError in Swift, e.g., > > throw NSError(code: HomeworkError.dogAteIt.rawValue > , > domain: HomeworkError > . > _domain, > userInfo: [ NSLocalizedDescriptionKey > : "the dog ate it" ]) > • There is no good way to get information typically associated with > NSError's userInfo in Swift. For example, the Swift-natural way to catch a > specific error in the AVError error domain doesn't give one access to the > userInfo dictionary, e.g.,: > > catch let error as AVError where error == . > diskFull { > > // AVError is an enum, so one only gets the equivalent of the code. > > > // There is no way to access the localized description (for example) or > > > // any other information typically stored in the ``userInfo`` dictionary. > > } > > The workaround is to catch as an NSError, which is quite a bit more ugly: > > catch let error as NSError where error._domain == AVFoundationErrorDomain && > error._code == AVFoundationErrorDomain.diskFull.rawValue > { > > // okay: userInfo is finally accessible, but still weakly typed > > } > > This makes it extremely hard to access common information, such as the > localized description. Moreover, the userInfo dictionary is effectively > untyped so, for example, one has to know a priori that the value associated > with the known AVErrorDeviceKey will be typed as CMTime: > > catch let error as NSError where error._domain = > AVFoundationErrorDomain { > > if let time = error.userInfo[AVErrorDeviceKey] as? > CMTime { > > // ... > > } > } > > It would be far better if one could catch an AVError directly and query the > time in a type-safe manner: > > catch let error as > AVError { > > if let time = error. > time { > > // ... > > } > } > > • NSError is inconsistently bridged with ErrorProtocol. Swift > interoperates by translating between NSError and ErrorProtocol when mapping > between a throwing Swift method/initializer and an Objective-C method with an > NSError** parameter. However, an Objective-C method that takes an NSError* > parameter (e.g., to render it) does not bridge to ErrorProtocol, meaning that > NSError is part of the API in Swift in some places (but not others). For > example, NSError leaks through when the following UIDocument API in > Objective-C: > > - (void)handleError:(NSError *)error > userInteractionPermitted:(BOOL)userInteractionPermitted; > is imported into Swift as follows: > > func handleError(_ error: NSError, userInteractionPermitted: Bool) > One would expect the first parameter to be imported as ErrorProtocol. > > Proposed solution > > This proposal involves directly addressing (1)-(3) with new protocols and a > different way of bridging Objective-C error code types into Swift, along with > some conveniences for working with Cocoa errors: > > • Introduce three new protocols for describing more information about > errors: LocalizedError, RecoverableError, andCustomNSError. For example, an > error type can provide a localized description by conforming to > LocalizedError: > > extension HomeworkError : > LocalizedError { > > var errorDescription: String > ? { > > switch self > { > > case .forgotten: return NSLocalizedString("I forgot it" > ) > > case .lost: return NSLocalizedString("I lost it" > ) > > case .dogAteIt: return NSLocalizedString("The dog ate it" > ) > } > } > } > > • Imported Objective-C error types should be mapped to struct types > that store an NSError so that no information is lost when bridging from an > NSError to the Swift error types. We propose to introduce a new macro, > NS_ERROR_ENUM, that one can use to both declare an enumeration type used to > describe the error codes as well as tying that type to a specific domain > constant, e.g., > > typedef NS_ERROR_ENUM(NSInteger > , AVError, AVFoundationErrorDomain) { > AVErrorUnknown = - > 11800 > , > AVErrorOutOfMemory = - > 11801 > , > AVErrorSessionNotRunning = - > 11803 > , > AVErrorDeviceAlreadyUsedByAnotherSession = - > 11804 > , > > // ... > > } > > The imported AVError will have a struct that allows one to access the > userInfo dictionary directly. This retains the ability to catch via a > specific code, e.g., > > catch AVError. > outOfMemory { > > // ... > > } > > However, catching a specific error as a value doesn't lose information: > > catch let error as AVError where error.code == . > sessionNotRunning { > > // able to access userInfo here! > > } > > This also gives the ability for one to add typed accessors for known keys > within the userInfo dictionary: > > extension > AVError { > > var > time: CMTime? { > > get > { > > return userInfo[AVErrorTimeKey] as? > CMTime? > } > > > set > { > userInfo[AVErrorTimeKey] > = newValue.map { $0 as > CMTime } > } > } > } > > • Bridge NSError to ErrorProtocol, so that all NSError uses are bridged > consistently. For example, this means that the Objective-C API: > > - (void)handleError:(NSError *)error > userInteractionPermitted:(BOOL)userInteractionPermitted; > is imported into Swift as: > > func handleError(_ error: ErrorProtocol, userInteractionPermitted: Bool) > This will use the same bridging logic in the Clang importer that we use for > other value types (Array, String, URL, etc.), but with the runtime > translation we've already been doing for catching/throwing errors. > > When we introduce this bridging, we will need to remove NSError's conformance > to ErrorProtocol to avoid creating cyclic implicit conversions. However, we > still need an easy way to create an ErrorProtocol instance from an arbitrary > NSError, e.g., > > extension > NSError { > > var asError: ErrorProtocol { ... > } > } > > • In Foundation, add an extension to ErrorProtocol that provides typed > access to the common user-info keys. Note that we focus only on those > user-info keys that are read by user code (vs. only accessed by frameworks): > > extension > ErrorProtocol { > > // Note: for exposition only. Not actual API. > > > private var userInfo: [NSObject : AnyObject > ] { > > return (self as! NSError). > userInfo > } > > > var localizedDescription: String > { > > return (self as! NSError). > localizedDescription > } > > > var filePath: String > ? { > > return userInfo[NSFilePathErrorKey] as? String > > } > > > var stringEncoding: String. > Encoding? { > > return (userInfo[NSStringEncodingErrorKey] as? > NSNumber) > > .map { String.Encoding(rawValue: $0.uintValue > ) } > } > > > var > underlying: ErrorProtocol? { > > return (userInfo[NSUnderlyingErrorKey] as? NSError)?. > asError > } > > > var > url: URL? { > > return userInfo[NSURLErrorKey] as? > URL > } > } > > • 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) > Detailed design > > This section details both the design (including the various new protocols, > mapping from Objective-C error code enumeration types into Swift types, etc.) > and the efficient implementation of this design to interoperate with NSError. > Throughout the detailed design, we already assume the name change from > ErrorProtocol to Error. > > New protocols > > This proposal introduces several new protocols that allow error types to > expose more information about error types. > > The LocalizedError protocol describes an error that provides localized > messages for display to the end user, all of which provide default > implementations. The conforming type can provide implementations for any > subset of these requirements: > > protocol LocalizedError : > Error { > > /// A localized message describing what error occurred. > > > var errorDescription: String? { get > } > > > /// A localized message describing the reason for the failure. > > > var failureReason: String? { get > } > > > /// A localized message describing how one might recover from the failure. > > > var recoverySuggestion: String? { get > } > > > /// A localized message providing "help" text if the user requests help. > > > var helpAnchor: String? { get > } > } > > > extension > LocalizedError { > > var errorDescription: String? { return nil > } > > var failureReason: String? { return nil > } > > var recoverySuggestion: String? { return nil > } > > var helpAnchor: String? { return nil > } > } > > The RecoverableError protocol describes an error that might be recoverable: > > protocol RecoverableError : > Error { > > /// Provides a set of possible recovery options to present to the user. > > > var recoveryOptions: [String] { get > } > > > /// Attempt to recover from this error when the user selected the > > > /// option at the given index. This routine must call resultHandler and > > > /// indicate whether recovery was successful (or not). > > > /// > > > /// This entry point is used for recovery of errors handled at a > > > /// "document" granularity, that do not affect the entire > > > /// application. > > > func attemptRecovery(optionIndex recoveryOptionIndex: Int > , > > andThen resultHandler: (recovered: Bool) -> Void > ) > > > /// Attempt to recover from this error when the user selected the > > > /// option at the given index. Returns true to indicate > > > /// successful recovery, and false otherwise. > > > /// > > > /// This entry point is used for recovery of errors handled at > > > /// the "application" granularity, where nothing else in the > > > /// application can proceed until the attmpted error recovery > > > /// completes. > > > func attemptRecovery(optionIndex recoveryOptionIndex: Int) -> Bool > > } > > > extension > RecoverableError { > > /// By default, implements document-modal recovery via application-model > > > /// recovery. > > > func attemptRecovery(optionIndex recoveryOptionIndex: Int > , > > andThen resultHandler: (Bool) -> Void > ) { > resultHandler(recovered: attemptRecovery(optionIndex: > recoveryOptionIndex)) > } > } > > Error types that conform to RecoverableError may be given an opportunity to > recover from the error. The user can be presented with some number of > (localized) recovery options, described by recoveryOptions, and the selected > option will be passed to the appropriate attemptRecovery method. > > The CustomNSError protocol describes an error that wants to provide custom > NSError information. This can be used, e.g., to provide a specific > domain/code or to populate NSError's userInfo dictionary with values for > custom keys that can be accessed from Objective-C code but are not covered by > the other protocols. > > /// Describes an error type that fills in the userInfo directly. > protocol CustomNSError : > Error { > > var errorDomain: String { get > } > > var errorCode: Int { get > } > > var errorUserInfo: [String : AnyObject] { get > } > } > > Note that, unlike with NSError, the provided errorUserInfo requires String > keys. This is in line with common practice for NSError and is important for > the implementation (see below). All of these properties are defaulted, so one > can provide any subset: > > extension > CustomNSError { > > var errorDomain: String { ... > } > > var errorCode: Int { ... > } > > var errorUserInfo: [String : AnyObject] { ... > } > } > > Mapping error types to NSError > > Every type that conforms to the Error protocol is implicitly bridged to > NSError. This has been the case since Swift 2, where the compiler provides a > domain (i.e., the mangled name of the type) and code (based on the > discriminator of the enumeration type). This proposal also allows for the > userInfo dictionary to be populated by the runtime, which will check for > conformance to the various protocols (LocalizedError, RecoverableError, or > CustomNSError) to retrieve information. > > Conceptually, this could be implemented by eagerly creating a userInfo > dictionary for a given instance of Error: > > func createUserInfo(error: Error) -> [NSObject : AnyObject > ] { > > var userInfo: [NSObject : AnyObject] = [: > ] > > > // Retrieve custom userInfo information. > > > if let customUserInfoError = error as? > CustomNSError { > userInfo > = customUserInfoError. > userInfo > } > > > if let localizedError = error as? > LocalizedError { > > if let description = localizedError. > errorDescription { > userInfo[NSLocalizedDescriptionKey] > = > description > } > > > if let reason = localizedError. > failureReason { > userInfo[NSLocalizedFailureReasonErrorKey] > = > reason > } > > > if let suggestion = localizedError. > recoverySuggestion { > userInfo[NSLocalizedRecoverySuggestionErrorKey] > = > suggestion > } > > > if let helpAnchor = localizedError. > helpAnchor { > userInfo[NSHelpAnchorErrorKey] > = > helpAnchor > } > } > > > if let recoverableError = error as? > RecoverableError { > userInfo[NSLocalizedRecoveryOptionsErrorKey] > = recoverableError. > recoveryOptions > userInfo[NSRecoveryAttempterErrorKey] > = > RecoveryAttempter() > } > } > > The RecoveryAttempter class is an implementation detail. It will implement > the informal protocol NSErrorRecoveryAttempting for the given error: > > class RecoveryAttempter : > NSObject { > > @objc > (attemptRecoveryFromError:optionIndex:delegate:didRecoverSelector:contextInfo:) > > func attemptRecovery(fromError nsError > : NSError, > > optionIndex recoveryOptionIndex: Int > , > delegate: > AnyObject > ?, > didRecoverSelector: Selector, > contextInfo: > UnsafeMutablePointer<Void> > ) { > > let error = nsError as! > RecoverableError > error > .attemptRecovery(optionIndex: recoveryOptionIndex) { success in > > > // Exposition only: this part will actually have to be > > > // implemented in Objective-C to pass the BOOL and void* through. > > delegate? > . > perform(didRecoverSelector, with: success, with: contextInfo) > } > } > > > @objc > (attemptRecoveryFromError:optionIndex:) > > func attemptRecovery(fromError nsError > : NSError, > > optionIndex recoveryOptionIndex: Int) -> Bool > { > > let error = nsError as! > RecoverableError > > return error. > attemptRecovery(optionIndex: recoveryOptionIndex) > } > } > > The actual the population of the userInfo dictionary should not be eager. > NSError provides the notion of global "user info value providers" that it > uses to lazily request the values for certain keys, via > setUserInfoValueProvider(forDomain:provider:), which is declared as: > > extension > NSError { > > @available(OSX 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, * > ) > > class func setUserInfoValueProvider(forDomain errorDomain: String > , > provider: ((NSError, > String) -> AnyObject > ?)? = nil) > } > > The runtime would need to register a user info value provider for each error > type the first time it is bridged into NSError, supplying the domain and the > following user info value provider function: > > func userInfoValueProvider(nsError: NSError, key: String) -> AnyObject > ? { > > let error = nsError as! > Error > > switch > key { > > case > NSLocalizedDescriptionKey: > > return (error as? LocalizedError)?. > errorDescription > > > case > NSLocalizedFailureReasonErrorKey: > > return (error as? LocalizedError)?. > failureReason > > > case > NSLocalizedRecoverySuggestionErrorKey: > > return (error as? LocalizedError)?. > recoverySuggestion > > > case > NSHelpAnchorErrorKey: > > return (error as? LocalizedError)?. > helpAnchor > > > case > NSLocalizedRecoveryOptionsErrorKey: > > return (error as? RecoverableError)?. > recoveryOptions > > > case > NSRecoveryAttempterErrorKey: > > return error is RecoverableError ? RecoveryAttempter() : nil > > > > default > : > > guard let customUserInfoError = error as? CustomNSError else { return nil > } > > return customUserInfoError. > userInfo[key] > } > } > > On platforms that predate the introduction of user info value providers, > there are alternate implementation strategies, including introducing a custom > NSDictionary subclass to use as the userInfo in the NSError that lazily > populates the dictionary by, effectively, calling the userInfoValueProvider > function above for each requested key. Or, one could eagerly populate > userInfo on older platforms. > > Importing error types from Objective-C > > In Objective-C, error domains are typically constructed using an enumeration > describing the error codes and a constant describing the error domain, e.g, > > extern NSString *const > AVFoundationErrorDomain; > > > typedef NS_ENUM(NSInteger > , AVError) { > AVErrorUnknown = - > 11800 > , > AVErrorOutOfMemory = - > 11801 > , > AVErrorSessionNotRunning = - > 11803 > , > AVErrorDeviceAlreadyUsedByAnotherSession = - > 11804 > , > > // ... > > } > > This is currently imported as an enum that conforms to Error: > > enum AVError : Int > { > > case unknown = -11800 > > > case outOfMemory = -11801 > > > case sessionNotRunning = -11803 > > > case deviceAlreadyUsedByAnotherSession = -11804 > > > > static var _domain: String { return > AVFoundationErrorDomain } > } > > and Swift code introduces an extension that makes it an Error, along with > some implementation magic to allow bridging from an NSError (losing userInfo > in the process): > > extension AVError : > Error { > > static var _domain: String { return > AVFoundationErrorDomain } > } > > Instead, error enums should be expressed with a new macro, NS_ERROR_ENUM, > that ties together the code and domain in the Objective-C header: > > extern NSString *const > AVFoundationErrorDomain; > > > typedef NS_ERROR_ENUM(NSInteger > , AVError, AVFoundationErrorDomain) { > AVErrorUnknown = - > 11800 > , > AVErrorOutOfMemory = - > 11801 > , > AVErrorSessionNotRunning = - > 11803 > , > AVErrorDeviceAlreadyUsedByAnotherSession = - > 11804 > , > > // ... > > } > > This will import as a new struct AVError that contains an NSError, so there > is no information loss. The actual enum will become a nested type Code, so > that it is still accessible. The resulting struct will be as follows: > > struct > AVError { > > /// Stored NSError. Note that error.domain == AVFoundationErrorDomain is an > invariant. > > > private var > error: NSError > > > /// Describes the error codes; directly imported from AVError > > > enum Code : Int > , ErrorCodeProtocol { > > typealias ErrorType = > AVError > > > case unknown = -11800 > > > case outOfMemory = -11801 > > > case sessionNotRunning = -11803 > > > case deviceAlreadyUsedByAnotherSession = -11804 > > > > func errorMatchesCode(_ error: AVError) -> Bool > { > > return error.code == self > > } > } > > > /// Allow one to create an error (optionally) with a userInfo dictionary. > > > init(_ code: Code, userInfo: [NSObject: AnyObject] = [: > ]) { > error > = NSError(code: code.rawValue > , domain: _domain, userInfo: userInfo) > } > > > /// Retrieve the code. > > > var code: Code { return Code(rawValue: error.code)! > } > > > /// Allow direct access to the userInfo dictionary. > > > var userInfo: [NSObject: AnyObject] { return error. > userInfo } > > > /// Make it easy to refer to constants without context. > > > static let unknown: Code = . > unknown > > static let outOfMemory: Code = . > outOfMemory > > static let sessionNotRunning: Code = . > sessionNotRunning > > static let deviceAlreadyUsedByAnotherSession: Code = . > deviceAlreadyUsedByAnotherSession > } > > > // Implementation detail: makes AVError conform to Error > extension AVError : > Error { > > static var _domain: String { return > AVFoundationErrorDomain } > > > var _code: Int { return error. > code } > } > > This syntax allows one to throw specific errors fairly easily, with or > without userInfo dictionaries: > > throw AVError(. > sessionNotRunning) > > throw AVError(.sessionNotRunning, userInfo: [ ... ]) > The ImportedErrorCode protocol is a helper so that we can define a general ~= > operator, which is used by both switchcase matching and catch blocks: > > protocol > ErrorCodeProtocol { > > typealias ErrorType : > Error > > > func errorMatchesCode(_ error: ErrorType) -> Bool > > } > > > func ~= <EC: ErrorCodeProtocol> (error: Error, code: EC) -> Bool > { > > guard let myError = error as? EC.ErrorType else { return false > } > > return code. > errorMatchesCode(myError) > } > > Mapping NSError types back into Swift > > When an NSError object bridged to an Error instance, it may be immediately > mapped back to a Swift error type (e.g., if the error was created as a > HomeworkError instance in Swift and then passed through NSError unmodified) > or it might be leave as an instance of NSError. The error might then be catch > as a particular Swift error type, e.g., > > catch let error as AVError where error.code == . > sessionNotRunning { > > // able to access userInfo here! > > } > > In this case, the mapping from an NSError instance to AVError goes through an > implementation-detail protocol_ObjectiveCBridgeableError: > > protocol _ObjectiveCBridgeableError : > Error { > > /// Produce a value of the error type corresponding to the given NSError, > > > /// or return nil if it cannot be bridged. > > > init > ?(_bridgedNSError error: NSError) > } > > The initializer is responsible for checking the domain and (optionally) the > code of the incoming NSError to map it to an instance of the Swift error > type. For example, AVError would adopt this protocol as follows: > > // Implementation detail: makes AVError conform to _ObjectiveCBridgeableError > extension AVError : > _ObjectiveCBridgeableError { > > init > ?(_bridgedNSError error: NSError) { > > // Check whether the error comes from the AVFoundation error domain > > > if error.domain != AVFoundationErrorDomain { return nil > } > > > // Save the error > > > self.error = > error > } > } > > We do not propose that _ObjectiveCBridgeableError become a public protocol, > because the core team has already deferred a similar proposal (SE-0058) to > make the related protocol _ObjectiveCBridgeable public. > > Other Issues > > NSError codes and domains are important for localization of error messages. > This is barely supported today by genstrings, but becomes considerably harder > when the domain and code are hidden (as they are in Swift). We would need to > consider tooling to make it easier to localize error descriptions, recovery > options, etc. in a sensible way. Although this is out of the scope of the > Swift language per se, it's an important part of the developer story. > > Impact on existing code > > This is a major source-breaking change for Objective-C APIs that operate on > NSError values, because those parameter/return/property types will change > from NSError to Error. There are ~400 such APIs in the macOS SDK, and closer > to 500 in the iOS SDK, which is a sizable number. Fortunately, this is > similar in scope to the Foundation value types proposal, and can use the same > code migration mechanism. That said, the scale of this change means that it > should either happen in Swift 3 or not at all. > > Future directions > > Better tooling for describing errors > > When adopting one of the new protocols (e.g., LocalizedError) in an enum, one > will inevitably end up with a number of switch statements that have to > enumerate all of the cases, leading to a lot of boilerplate. Better tooling > could improve the situation considerably: for example, one could use > something like Cocoa's stringsdict files to provide localized strings > identified by the enum name, case name, and property. That would eliminate > the need for the switch-on-all-cases implementations of each property. > > Round-tripping errors through userInfo > > The CustomNSError protocol allows one to place arbitrary key/value pairs into > NSError's userInfo dictionary. The implementation-detail > _ObjectiveCBridgeableError protocol allows one to control how a raw NSError > is mapped to a particular error type. One could effectively serialize the > entire state of a particular error type into the userInfo dictionary > viaCustomNSError, then restore it via _ObjectiveCBridgeableError, allowing > one to form a complete NSError in Objective-C that can reconstitute itself as > a particular Swift error type, which can be useful both for mixed-source > projects and (possibly) as a weak form of serialization for NSErrors. > > Alternatives considered > > Exposing the domain, code, and user-info dictionary directly > > This proposal does not directly expose the domain, code, or user-info > dictionary on ErrorProtocol, because these notions are superseded by Swift's > strong typing of errors. The domain is effectively subsumed by the type of > the error (e.g., a Swift-defined error type uses its mangled name as the > domain); the code is some type-specific value (e.g., the discriminator of the > enum); and the user-info dictionary is an untyped set of key-value pairs that > are better expressed in Swift as data on the specific error type. > > Bridging NSError to a new value type Error > > One could introduce a new value type, Error, that stores a domain, code, and > user-info dictionary but provides them with value semantics. Doing so would > make it easier to create "generic" errors that carry some information. > However, we feel that introducing new error types in Swift is already easier > than establishing a new domain and a set of codes, because a new enum type > provides this information naturally in Swift. _______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
