Changes to Equatable in Swift 2 for NSObject-derived subclasses

Prior to Swift 2, a Swift class inheriting from NSObject could extend Equatable and provide its own == implementation. Swift 2, however, gives a compiler error on the Equatable conformance, since NSObject seems to provide its own implementation.


However, my == implementations for NSObject-derived classes no longer work because of this change - they're simply ignored in favor of the isEqual: method in NSObject.


Here's an example:


class TestObject: NSObject {
    let a: Int

    init(a: Int) {
        self.a = a
        super.init()
    }
}

func ==(lhs: TestObject, rhs: TestObject) -> Bool {
    print("calling equatable")
    return lhs.a == rhs.a
}


class EquatableTestTests: XCTestCase {
    func testExample() {
        let a = TestObject(a: 4)
        let b = TestObject(a: 4)
     
        let isEqual = a == b
        XCTAssert(isEqual)
     
        XCTAssertEqual(a, b)
    }
}

The first assert works, because I'm directly calling the == operator, but the second one fails, because the == operator that TestObject inherits from NSObject calls NSObject#isEqual:

Swift classes that don't inherit from NSObject pass both tests.

Since I couldn't find anything about this in the release notes, I'm not sure if this is an intentional language change, so I'm not sure if I should switch to overriding isEqual: in NSObject-derived subclasses or if this behavior shouldn't be relied on. I can fix this by overriding isEqual: in TestObject, but I'm not sure if this is an intentional change or a bug.


Does anyone know? Can you point me to something in the documentation to ease my worries?

You should definitely switch to using

-isEqual:
. Regardless of the Swift side of things, if your object ends up in the Objective-C world its equality is going to be based on
-isEqual:
.

Consider this Objective-C code:

@implementation SomeObjCClass

+ (void)testEqualityOfObject1:(id)obj1 toObject2:(id)obj2 {
    NSLog(@"Objective-C says: %@", [obj1 isEqual:obj2] ? @"YES" : @"NO");
}

@end

and a Swift test like this:

let a = TestObject(a: 4) 
let b = TestObject(a: 4) 
NSLog("Swift say: %@", (a == b) ? "true" : "false")
SomeObjCClass.testEqualityOfObject1(a, toObject2: b)

This prints (testing on Xcode 6.4) the following:

2015-09-11 09:39:48.493 xxx[1591:88426] Swift say: true
2015-09-11 09:39:48.495 xxx[1591:88426] Objective-C says: NO

which is not good, eh?

OTOH, an

-isEqual:
implementation is compatible with both 6.4 and 7.0 and, using it, both Swift and Objective-C agree about equality.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
Changes to Equatable in Swift 2 for NSObject-derived subclasses
 
 
Q