Hashable class extending NSObject?

I have some Swift classes in my project that extend an Objective-C base class, which in turn extends NSObject. I did this years ago when Swift was new in order to take advantage of some Objective-C code that was difficult to rewrite in Swift. It's not a common situation but it has been working fine for a long time.

One of these classes is used as the key to a Dictionary and thus needs to be Hashable. The way I did this was to implement an == function and override the 'hash' property. It is a very simple case where the identity of the object is based on a single integer:

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

override var hash: Int {
   return fishId
}

I believe that I initially tried to add "Hashable" to the class definition but was told it was redundant. I'm not sure why that is, but it worked fine without it.

Today I took the latest Xcode update to 15.2, and now my project won't compile anymore. The compiler error says that my class "does not conform to protocol Hashable". Adding Hashable to the class definition did not fix it. There are also some unusual errors about missing files, such as abi.json, swiftdoc, swiftmodule, and swiftsourceinfo.

Was this caused by the Xcode update? How do I fix it?

I believe that I initially tried to add Hashable to the class definition but was told it was redundant. I'm not sure why that is

Hashable is Swift’s protocol to indicate that an object is… well… hashable. It’s redundant when you create an NSObject subclass because NSObject itself conforms to Hashable. That’s because all Objective-C objects are hashable. If you don’t provide an implementation, NSObject uses a default one based on pointer equality.

The way I did this was to implement an == function and override the hash property.

That’s not the right approach for Objective-C objects [1]. Rather, you need to override the Objective-C isEqual(_:) method and hash property.

class Waffle: NSObject {

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

    var finish: String

    override func isEqual(_ other: Any?) -> Bool {
        guard let other = other as? Waffle else { return false }
        return self.finish == other.finish
    }
    
    override var hash: Int {
        return self.finish.hash
    }
}

Share and Enjoy

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

[1] It’s not quite the right way for Swift either. Modern Swift code should implement hash(into:).

Thanks for the response. I checked my code and found that I had already implemented the isEqual method but forgot to mention it in my original post.

Oddly, my app compiles and runs now even though I did not really change anything. Also oddly, when I build it I see the "Redundant conformance to Hashable" message show up for a moment, and then disappear. Possibly this is some kind of Xcode bug? It doesn't really matter at this point I guess. I'd feel better knowing why it happened initially but I can't really expect anyone to spend time debugging an Objective-C issue.

Frank

Hashable class extending NSObject?
 
 
Q