[swift-evolution] [Pitch] Consistent bridging for NSErrors at the language boundary

Michael Peternell michael.peternell at gmx.at
Sat May 14 03:51:21 CDT 2016

> Am 14.05.2016 um 09:31 schrieb Charles Srstka via swift-evolution <swift-evolution at swift.org>:
> I don’t think Cocoa is going away for a very, very long time. Even if it did, you’d still need some consistent way to turn an ErrorType into something human-readable, and currently we don’t have that. NSError’s userInfo field actually does a fairly decent job, aside from the absurdly long constant names.

I think Apple's Chief Deprecating Officer is already getting nervous and that point in time may be sooner. IMHO it's one of the big pains of Apple development that you have to learn new stuff every year, because oftentimes APIs are changed for no convincing reasons. Or one API is removed and a new API introduced, with no migration time. It's one of the reasons why I sometimes think about leaving the Apple platform as a developer (where I could go to is another question..). Having to adapt a program every two years at least just to keep it compiling is not programmer friendly. It's a good playground for language developers though. With Swift 3 it's a similar problem. At a company I work, we decided not to use Swift yet, because of Swift 3. Because with Swift 3 everything has to be re-done, so we better wait until Swift 3 comes out. And even after Swift 3 will be released, we will probably wait until the mid of 2017, just to be really sure that no breaking changes are planned and no "Swift 4" is coming. Until that point I'm going to use Swift only for small or educational projects (I'm currently writing a game in Swift on iOS). IMHO it would be nice to have a language that is *stable* and that keeps stable for at least 10 years. But can Swift really ever become fully stable? Or will it be replaced with something else as soon as it becomes stable, in the same way it is happening to Objective-C now?</rant>

For interoperability, ErrorType and NSError should be toll-free-bridged, like CFStringRef and NSString. Converting between them should be a no-op at runtime. I prefer runtime/ABI consistency over syntax/language consistency. MyErrorType2 should be represented as an NSError with domain @"MyErrorType2", whatever code is defined in that error type, and if you want userInfo you have to create the beast as an NSError object in the first place. I think userInfo is not visible in the Swift-enum-representation. If you want to have a Swift Error representation that includes userInfo, you'd have to either change the architecture or introduce special support on the language level (e.g. a magic `er.userInfo` of type `Dictionary<String,AnyObject>` for every `er: ErrorType` and a `er.withUserInfo(userInfo)` to add a userInfo dictionary to an error type: e.g. `MyErrorType2.fooConditionFound.withUserInfo([NSLocalizedDescriptionKey: "that was really bad"])` and maybe even a convenience method as a protocol extension like `MyErrorType.fooConditionFound.withLocalizedDescription(localizedString: "ReallyBad")`. And the key of a dictionary should really always be a String, not just an NSObject.)

(I know if you have something like `case SpecialError(Int)` in your ErrorType declaration, the above method does not work; you'd have to create an NSError-subclass for it. Or maybe not? Just add a "SpecialError_arg0" key to userInfo, value can be an NSNumber? There are more edge cases here but they are all solvable.)

On the other hand, I don't think that enumerations in general should support instance variables. One of the nice things for an enum is that I as a programmer can always be sure that it *is* just an enum, and nothing else. Adding iVars to enums would effectively turning enums to structs, and each time I see a switch statement I'll have to think "is this really all? or is there some stealth value attached to this enum? is every .MadeAMistake object always the same?" Keeping the inconsistency constrained to the ErrorType is much nicer than turning every enum into a struct.

There will always be rough edges when converting between two languages, that's unavoidable. Try to translate a text that contains a lot of the words "safety" and "security" into German. Good luck, they both translate to the same word. And so there also cannot be a perfectly consistent translation between ErrorType and NSError. If you want to achieve a good translation, you'd have to change the ErrorType to something different. E.g. a special language construct `def-error MyErrorType { case MadeAMistake; case RanOutOfCake }` - matching works the same as now and you have a userInfo property. And on non-objc-platforms, the NSError() name becomes unavailable and .userInfo always returns `[:]`. I'm not saying that this would be a beautiful solution; I'm saying that there is no beautiful solution to this problem.


More information about the swift-evolution mailing list