6

I have an Objective-C class (that happens to be a button, but that is not important), and at another part of my (mixed language) project, I have an array of these buttons and I'd like to get the index of a button using the find() method. Like so:

func doSomethingWithThisButtonIndex(index:Int)
{
    let buttons = [firstButton, secondButton, thirdButton]
    if index == find(buttons, firstButton)
    {
        // we've selected the first button
    }
}

but I'm getting the

Type 'ImplicitlyUnwrappedOptional' does not conform to protocol equatable

Okay, so lets go to Objective-C and have ButtonThing implement <Equatable>. But it doesn't recognize that.

So what am I to do? For now I'm building around it, forcing the array to be an NSArray and using indexOfObject. But this is ugly. And frustrating.

nhgrif
  • 58,130
  • 23
  • 123
  • 163
Yerk
  • 948
  • 7
  • 19
  • If my answer doesn't satisfy you, then the only other answer is "It's impossible." You can't make an Objective-C class conform to a Swift protocol unless the protocol is annotated as `@objc` or if you do it from the Swift end. `Equatable` is not annotated as such and it can't possibly make sense for it to be so annotated. – nhgrif Jun 19 '15 at 17:12

2 Answers2

8

First, in Swift write a custom == operator function for your class.

Second, also in Swift, write a class extension that adds the Equatable protocol conformance.

Perhaps, for example:

func == (lhs: YourClass, rhs: YourClass) -> Bool {
    // whatever logic necessary to determine whether they are equal
    return lhs === rhs
}

extension YourClass: Equatable {}

And now your class conforms to Equatable, which is Swift specific. You can not do this on the Objective-C end because you can not write custom operators for Objective-C.

nhgrif
  • 58,130
  • 23
  • 123
  • 163
  • 1
    @Yerk Read the last line of the answer. You cannot do this for Objective-C. Swift's `Equatable` protocol requires a custom operator (specifically, `==`), and there is no way to do this with Objective-C. Perhaps more importantly, you should be trying to do this for Objective-C anyway. Objective-C's `==` is equivalent to Swift's `===` for object references. We shouldn't want to alter that 30-year-old Objective-C behavior... – nhgrif Jun 19 '15 at 15:36
  • 1
    Additionally, the `Equatable` protocol is not defined as being an `@objc` protocol, and therefore the protocol cannot be used from Objective-C at all. `Equatable` is a **purely** Swift protocol. The way to make an Objective-C class conform to this protocol is by extending the class from within Swift. Nothing else can make sense. You're effectively asking "How can I override the default behavior of `==` in Objective-C?" – nhgrif Jun 19 '15 at 15:38
  • @Alix no. Swift's `===` is roughly equivalent to Objective-C's `==`. It's a reference comparison. – nhgrif Aug 11 '15 at 14:33
  • so in objective-C if I want to test the equality between 2 objects how am I suppose to do it? – Honey Nov 22 '16 at 12:19
  • @Honey `[obj1 isEqual:obj2];`. Some classes, such as `NSString`, have other methods as well, such as `[str1 isEqualToString:str2];`. In this specific case, `isEqualToString:` and `isEqual:` are equivalent. And all objects inherit `isEqual:` from `NSObject`, but the default implementation is simply the `==` reference comparison. – nhgrif Nov 23 '16 at 15:56
  • Thanks. I think I wrote my question poorly... in Swift you have `struct Home { var rooms : Int var address: String var city : String}` and then to implement equality you have do a `func == (lhs : Home, rhs : Home) {lsh.rooms == rhs.rooms && lhs.address == rhs.address && lhs.city == rhs.city}` How do you implement such in Objective-c? – Honey Nov 23 '16 at 16:12
  • Implement the `isEqual:` method. You can't override operators in Objective-C (note the last sentence of this answer). – nhgrif Nov 23 '16 at 16:59
  • ohhk...so you mean just *like any other method that you write yourself* write one which its name is `isEqual:` which returns a bool. That's simple. So basically in Swift you are mentioning it in a more explicit way and also have the benefit of using a more readable way ie `==` – Honey Nov 23 '16 at 23:10
1

If your Objective C class is an NSObject, implement isEqual:

- (BOOL)isEqual:(_Nullable id)other;

This worked for me for Array.index(of: myobject) and == comparisons. NSObject is already Equatable so using a Swift extension does not work.

Peter Tseng
  • 11,991
  • 3
  • 64
  • 53