NSDictionary.isEqual(to:) with Swift dictionary compiles on macOS but not on iOS

The following code works when compiling for macOS:

print(NSMutableDictionary().isEqual(to: NSMutableDictionary()))

but produces a compiler error when compiling for iOS:

'NSMutableDictionary' is not convertible to '[AnyHashable : Any]'

NSDictionary.isEqual(to:) has the same signature on macOS and iOS. Why does this happen? Can I use NSDictionary.isEqual(_:) instead?

Answered by DTS Engineer in 824039022

Well, that is strange.

Written by Nickkk in 773934021
Why does this happen?

Because:

  • On iOS, the compiler is using -[NSDictionary isEqualToDictionary:]. Its parameter is of type NSDictionary, which gets imported as [AnyHashable: Any].

  • On macOS the compiler finds the -isEqualTo: method, added by a category on NSObject by <Foundation/NSScriptWhoseTests.h. Its parameter is nullable id, which gets imported as Any?.

That header is associated with Foundation’s AppleScript support, and thus isn’t present on iOS.

What’s weird is that the compiler chooses to use the category method. The dictionary method seems more specific. However, Swift’s overload resolution rules are both complex and not documented precisely, so it’s hard to say whether it’s right or wrong. If you want to ‘language lawyer’ that, I recommend that you bounce over to Swift Forums.

Written by Nickkk in 773934021
Can I use NSDictionary.isEqual(_:) instead?

Yes. Well, it’s NSObject.isEqual(_:), but NSDictionary overrides it with the right logic.

The whole -isEqualToXxx: stuff always confused me. I’m not really sure why it exists. Sure, it’s slightly faster, but how much does that actually buy you?

Alternative you could use Swift’s ==, because for NSObject subclasses that dispatches through -isEqual:.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Accepted Answer

Well, that is strange.

Written by Nickkk in 773934021
Why does this happen?

Because:

  • On iOS, the compiler is using -[NSDictionary isEqualToDictionary:]. Its parameter is of type NSDictionary, which gets imported as [AnyHashable: Any].

  • On macOS the compiler finds the -isEqualTo: method, added by a category on NSObject by <Foundation/NSScriptWhoseTests.h. Its parameter is nullable id, which gets imported as Any?.

That header is associated with Foundation’s AppleScript support, and thus isn’t present on iOS.

What’s weird is that the compiler chooses to use the category method. The dictionary method seems more specific. However, Swift’s overload resolution rules are both complex and not documented precisely, so it’s hard to say whether it’s right or wrong. If you want to ‘language lawyer’ that, I recommend that you bounce over to Swift Forums.

Written by Nickkk in 773934021
Can I use NSDictionary.isEqual(_:) instead?

Yes. Well, it’s NSObject.isEqual(_:), but NSDictionary overrides it with the right logic.

The whole -isEqualToXxx: stuff always confused me. I’m not really sure why it exists. Sure, it’s slightly faster, but how much does that actually buy you?

Alternative you could use Swift’s ==, because for NSObject subclasses that dispatches through -isEqual:.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you for the detailed explanation. == is perfect, I didn't know I could use it.

NSDictionary.isEqual(to:) with Swift dictionary compiles on macOS but not on iOS
 
 
Q