Overridden == function for Equatable type not called for custom class that subclasses NSCoding and NSObject

10,080

Solution 1

Because your class inherits from NSObject you do not need to use the swift protocol Equatable instead you must override the NSObject method isEquals:

Swift 3.x and above

class FooBar: NSObject, NSCoding {
  override func isEqual(_ object: Any?) -> Bool {
    return id == (object as? FooBar)?.id
  }
}

(Thanks to Kamchatka)

Swift 2.x

class FooBar: NSObject, NSCoding {
  override func isEqual(object: AnyObject?) -> Bool {
    return id == (object as? FooBar)?.id
  }
}

Solution 2

You are getting this error because NSObject already conforms to Equatable through its isEqual method.

So I'm not sure if this is the correct way of doing this, but you could override the isEqual method of NSObject:

class FooBar: NSObject, NSCoding {
...

override func isEqual(object: AnyObject?) -> Bool {
    return self == (object as? FooBar)
}
Share:
10,080
Crashalot
Author by

Crashalot

Hello. My friends call me SegFault. Describe myself in 10 seconds? You know those feisty, whip-smart, sometimes funny, and occasionally charming developers who dominate StackOverflow and consider Swift/Ruby/jQuery their native tongue? Yah, I buy coffee for them.

Updated on June 14, 2022

Comments

  • Crashalot
    Crashalot about 2 years

    The FooBar class below has to override the == function of the Equatable type.

    However, calling contains on an array of FooBar objects does not cause a breakpoint inside the custom == function to get invoked. Is it possible another == function is overriding this custom one?

    Note: Because FooBar must subclass from NSCoding and NSObject, FooBar does not list Equatable as a protocol because it causes this error:

    Redundant conformance of 'FooBar' to protocol 'Equatable'

    func ==(lhs: FooBar, rhs: FooBar) -> Bool {
        return lhs.id == rhs.id
    }
    
    class FooBar: NSObject, NSCoding {
         // Class def
    }
    
    // Both serverFooBars and gFooBars are [FooBar]
    let newFooBars = serverFooBars.filter { !gFooBars.contains($0) }
    
  • Crashalot
    Crashalot about 8 years
    Doing so caused an error, hence its omission. The error: Redundant conformance of 'Recipe' to protocol 'Equatable'
  • Blake Lockley
    Blake Lockley about 8 years
    @Crashalot is it required your class inherits from NSObject for any reason?
  • Crashalot
    Crashalot about 8 years
    Isn't it needed for NSCoding?
  • Daniel Krom
    Daniel Krom about 8 years
    look at the Equatable protocol, you must implement: func ==(lhs: Self, rhs: Self) -> Bool (inside the class)
  • Blake Lockley
    Blake Lockley about 8 years
    @Crashalot thats correct, the reason its saying its redundant because the class inherits from NSObject has the isEquals method.. though im going to check this out myself now
  • Blake Lockley
    Blake Lockley about 8 years
    @DanielKrom, i mention that must be implemented in my answer but i will edit it to make it more clear
  • Crashalot
    Crashalot about 8 years
    @DanielKrom inside the class generates an error; you're sure?
  • Crashalot
    Crashalot about 8 years
    @BlakeLockley yes the question is how to override == if you must also subclass NSCoding?
  • Daniel Krom
    Daniel Krom about 8 years
  • Blake Lockley
    Blake Lockley about 8 years
    @DanielKrom my bad i mis read your comment, no it must be implemented outside of the class because when you use the equatable function you are not within the scope of the class. e.g. fooBar == fooBar is not the same as calling a method on fooBar.
  • Blake Lockley
    Blake Lockley about 8 years
    @Crashalot override isEquals - ill edit my answer
  • Daniel Krom
    Daniel Krom about 8 years
    @BlakeLockley Hmm, The syntax is like global operator, but if you implement a protocol you have to implement the protocol functions inside the class that implements that, but I may be wrong
  • Crashalot
    Crashalot about 8 years
    Cool will try and report back!
  • Blake Lockley
    Blake Lockley about 8 years
    @DanielKrom thats true for all protocols with the exceptions being Equatable and Comparable or any other protocol that requires an overload implementation of an operator.
  • Crashalot
    Crashalot about 8 years
    That worked but unfortunately Blake was helping earlier and published his answer a bit sooner. Thanks though!
  • Crashalot
    Crashalot about 8 years
    Works thanks so much! Would be ideal if you included the self == (object as? FooBar) bit from Odrakir's answer as this is even more explicit for Swift newbies.
  • Odrakir
    Odrakir about 8 years
    I see that now. He didn't show how to still use the == override, though... :)
  • Blake Lockley
    Blake Lockley about 8 years
    @Crashalot ill share the love and upvote this one aswell, glad we all could help!
  • Blake Lockley
    Blake Lockley about 8 years
    @Crashalot no worries :) you could just include your implementation of == inside the isEquals then theres no reason to downcast it and call ==. But thats all down to preference.
  • Crashalot
    Crashalot about 8 years
    He updated the answer to show how to use the == override. It's true your answer is more explicit, but you can see how much time Blake spent beforehand and he gave the right answer -- just not the perfect answer like yours. Your answer was upvoted, though! :)
  • Crashalot
    Crashalot about 8 years
    How do you not downcast it?
  • Odrakir
    Odrakir about 8 years
    Can't see the part about overriding ==, but, ok, whatever...
  • Blake Lockley
    Blake Lockley about 8 years
    @Crashalot i'll edit my answer
  • Blake Lockley
    Blake Lockley about 8 years
    @Crashalot my mistake, you still need to perform a downcast for the other object but this way you dont need to worry about implementation of == .
  • Kamchatka
    Kamchatka over 7 years
    Note: In Swift 3 the signature of this method is now: override func isEqual(_ object: Any?) -> Bool {