Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSErrorDomain + NS_ERROR_ENUM makes type lookup ambiguous. Why?

I have an error that used to look like this in Objective-C

NSString * const JKConfigurationErrorDomain;
typedef NS_ENUM(NSInteger, JKConfigurationCode) {
    JKConfigurationCodeUnknown,
    JKConfigurationCodeSomethingBad,
    JKConfigurationCodeParsing,
};

Now, this is ugly to use in Swift. But since Swift 4, we can use NSErrorDomain and NS_ERROR_ENUM to make the imported error much nicer in Swift:

NSErrorDomain const JKConfigurationErrorDomain;
typedef NS_ERROR_ENUM(JKConfigurationErrorDomain, JKConfigurationCode) {
    JKConfigurationCodeUnknown,
    JKConfigurationErrorSomethingBad,
    JKConfigurationErrorParsing,
};

This means I can now do stuff in Swift like this:

if let myError = error as? JKConfigurationError, myError.code = .somethingBad {
    // handle it
}

instead of having to cast error to NSError, then check its .domain then look at the .code which is an integer, etc.

So far, so good. But my library is called JKConfiguration and there is already a JKConfiguration object (the center piece of the library) in there and as soon as I start using JKConfiguration anywhere in the library code I get an error:

'JKConfiguration' is ambiguous for type lookup in this context

I don't get it, why? What does NSErrorDomain or NS_ERROR_ENUM do such that the type lookup becomes ambiguous and how can I fix it?

What I tried already:

  • use NS_SWIFT_NAME on the NS_ERROR_ENUM typedef and rename it to something else. Looking at the generated Swift header, the rename works, but doesn't solve the issue
  • Change the name of the error domain (and thus of the generated error type in Swift). Seems to work according to the generated Swift header, but the issue still persists. Why is that?
like image 813
Joachim Kurz Avatar asked Oct 18 '25 09:10

Joachim Kurz


1 Answers

The issue is not, as I initially thought, in the name of the error domain. Nor is it a problem with the library name. It’s a problem of the error enum‘s name, in the example above: JKConfigurationCode.

What the Compiler does for the enum cases of an NS_ERROR_ENUM is two-fold:

  • use the name of the enum and remove that prefix from all enum cases before importing them to swift
  • create an enum with the given name to hold those cases. If the given name ends with Code remove that suffix.

So that last part is the issue. It means that NS_ERROR_ENUM(AnyDomainName, JKConfigurationCode) generates an enum in Swift to hold the error codes with the name JKConfiguration (without the Code) prefix. But that type already exists in my example, which leads to the ambiguity.

So the solution is to change

NS_ERROR_ENUM(JKConfigurationErrorDomain, JKConfigurationCode)

to

NS_ERROR_ENUM(JKConfigurationErrorDomain, JKConfigurationSomethingCode)

Or similar. Don’t forget to update all the prefixes of your enum cases though, as it seems the compiler won’t find them if the prefixes don’t match the enum name.

Why doesn’t NS_SWIFT_NAME work to rename the enum? Best I can tell, NS_SWIFT_NAME causes the type to be renamed but not the cases. This leads to an empty type (Swift chooses a struct in that case) being generated for the error code as Swift doesn’t seem to find the cases. And the original container for the enum cases still has the offending name.

like image 195
Joachim Kurz Avatar answered Oct 20 '25 02:10

Joachim Kurz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!