I am having an issue trying to adopt the Hashable and NSCoding protocols within the same Swift class (or struct). Hashability and encode/decode both work independently. However, hashability is lost once an object has been restored by NSCoder.
First, I am having to make this a class instead of a struct since apparently NSCoding will not work with structs. Therefore, I apparently need to inherit from NSObject (which also happens to be Hashable). Unfortunately this built-in hashability of NSObject seems to be preventing me from making my own class hashable in the manner I want (I think.)
Here is my class:
class TileMapCoords : NSObject, NSCoding {
var row: Int
var column: Int
init(row: Int, column: Int) {
self.row = row
self.column = column
}
// Hashable
override var hashValue: Int {
return column*numTMRows + row
}
static func == (lhs: TileMapCoords, rhs: TileMapCoords) -> Bool {
return
lhs.row == rhs.row &&
lhs.column == rhs.column
}
// do I even need this?
override func isEqual(_ object: Any?) -> Bool {
if let otherCoords = object as? TileMapCoords {
return self == otherCoords
} else {
return false
}
}
// NSCoding
required init?(coder aDecoder: NSCoder) {
row = aDecoder.decodeInteger(forKey: "TileMapCoords.row")
column = aDecoder.decodeInteger(forKey: "TileMapCoords.column")
}
func encode(with aCoder: NSCoder) {
aCoder.encode(row, forKey: "TileMapCoords.row")
aCoder.encode(column, forKey: "TileMapCoords.column")
}
}
I am creating a set of these objects and encoding/decoding them as follows:
Encode:
aCoder.encode(itemsCollected, forKey: "GameData.itemsCollected")
Decode:
var itemsCollected = Set<TileMapCoords>()
itemsCollected = aDecoder.decodeObject(forKey: "GameData.itemsCollected") as! Set<TileMapCoords>
But then I found the objects in the set had different hash values upon decoding than they had upon encoding. So I made a workaround which I believed worked:
let itemsCollectedCopy = aDecoder.decodeObject(forKey: "GameData.itemsCollected") as! Set<TileMapCoords>
var itemsCollected = Set<TileMapCoords>()
for coords in itemsCollectedCopy {
itemsCollected.insert(coords) }
This "trick" seemed to correct the hash values when I printed them out, however the objects are still not being properly hashed (for reasons unknown). Namely when I do this:
if curGameData!.itemsCollected.contains(location) {
// do something
}
If I create a TileMapCoords object with the same row and column as an object in the itemsCollected set, the 'contains' method tells me it
1) Is in the set initially 2) Is not in the set once the set has been restored/decoded by NSCoder
The strange that is that the object IS in the set and I have verified that both my object and the one in the set have the same hash value, and are equal according to the == operator and the isEqual method.
I need help solving this issue or thinking of another means to have both hashability and NSCodability within the same class (perhaps not overriding NSObject?)