Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to override NSObject's default comparison in Swift

I have a custom class Player that conforms to NSObject, NSCoding. A Player object is instantiated with a id: String!

One of the issues I am encountering is when I perform an operation like the following:

let listOfPlayers = [Player]()
//populate listOfPlayers
if listOfPlayers.contains(aPlayer) ...

Specifically, the contains would return results based on the memory pointer, and not based on the id value. As such, I would get a true in some cases and false in others.

I would like to override the default comparison methods, and did the following:

func isEqual(object: AnyObject?) -> Bool {
    if let otherPlayer = object as? Player {
        return self.id == otherPlayer.id
    }
    return false
}


static func == (lhs: Player, rhs: Player) -> Bool {
    return lhs.id == rhs.id
}

However, these functions are not being executed. I have also tried to add override, but it returns an error "Method does not override any method from its superclass`

What is the right way to have a customer comparator so it is only comparing the id element?

Thanks!

like image 752
daspianist Avatar asked Sep 10 '25 20:09

daspianist


1 Answers

As of Swift 3, the isEqual method of NSObject takes an Any? parameter, so you are not overriding the correct method, that's why it is never called.

You should also override var hash: Int (equal objects must have the same hash) – otherwise the object will behave wrongly in hashable collections (sets, dictionaries):

class Player: NSObject {
    let id: String

    init(id: String) { self.id = id }

    override func isEqual(_ object: Any?) -> Bool {
        if let other = object as? Player {
            return self.id == other.id
        } else {
            return false
        }
    }

    override var hash: Int {
        return id.hashValue
    }
}

Some tests:

let p1 = Player(id: "a")
let p2 = Player(id: "a")

print(p1 == p2) // true
print(p1 != p2) // false

// Native Swift set:
let set = Set([Player(id: "x"), Player(id: "y"), Player(id: "z")])
print(set.contains(Player(id: "y"))) // true

// Foundation set:
let nsset = NSSet(objects: Player(id: "x"), Player(id: "y"), Player(id: "z"))
print(nsset.contains(Player(id: "y"))) // true
like image 162
Martin R Avatar answered Sep 13 '25 09:09

Martin R