2

I'm trying to fix up an old tutorial from RayWenderlich's site, no longer supported. The warning appears in three files, Chain.swift, Cookie.swift and Swap.swift from the "How to Make a Game Like Candy Crush with SpriteKit and Swift:" tutorial

I'm at a loss even after exploring the available replies that appear in many places. I'm struggling to understand just what this code is doing so that I can fix it. I know it's just a warning, and I can probably ignore it, but the game is also showing X where blank tiles should appear so I suspect that it may have something to do with this?

The warning is this:

'Hashable.hashValue' is deprecated as a protocol requirement; conform type 'Chain' to 'Hashable' by implementing 'hash(into:)' instead

File example

  class Chain: Hashable, CustomStringConvertible {
  var cookies: [Cookie] = []
  var score = 0

  enum ChainType: CustomStringConvertible {
    case horizontal
    case vertical

    var description: String {
      switch self {
      case .horizontal: return "Horizontal"
      case .vertical: return "Vertical"
      }
    }
  }

  var chainType: ChainType
  init(chainType: ChainType) {
    self.chainType = chainType
  }

  func add(cookie: Cookie) {
    cookies.append(cookie)
  }

  func firstCookie() -> Cookie {
    return cookies[0]
  }

  func lastCookie() -> Cookie {
    return cookies[cookies.count - 1]
  }

  var length: Int {
    return cookies.count
  }

  var description: String {
    return "type:\(chainType) cookies:\(cookies)"
  }

  var hashValue: Int {
    return cookies.reduce (0) { $0.hashValue ^ $1.hashValue }
  }

  static func ==(lhs: Chain, rhs: Chain) -> Bool {
    return lhs.cookies == rhs.cookies
  }
}
Harry McGovern
  • 517
  • 5
  • 19
  • I saw that. Sorry, it's no help. The Apple documentation is even more obscure. – Harry McGovern Apr 04 '19 at 13:34
  • Background: `Hashable` moved away from asking conforming types for a `hashValue: Int` that describes themselves, to asking them to take in a `Hasher`, and "mix" themselves into it (by mixing in their fields). Previously people had difficulty deriving good hash values for objects with multiple fields, often resorting to hacks, like XORing all the elements (`a ^ b ^ c`), or worse, taking the string value of a string that concatinates the elements (`"\(a)-\(b)-\(c)".hashValue`). Now instead, you just tell the hasher what to hash, and it uses a proper hashing algorithm to do that on your behalf. – Alexander Nov 24 '19 at 19:15

1 Answers1

6

From the Hashable documentation:

Hashing a value means feeding its essential components into a hash function, represented by the Hasher type. Essential components are those that contribute to the type’s implementation of Equatable. Two instances that are equal must feed the same values to Hasher in hash(into:), in the same order.

And from the hash(into:) documentation:

The components used for hashing must be the same as the components compared in your type’s == operator implementation. Call hasher.combine(_:) with each of these components.

The implementation of

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

shows that cookies is the “essential component” which determines equality of instances. Therefore

func hash(into hasher: inout Hasher) {
    hasher.combine(cookies)
}

is a valid (and sensible) implementation of the Hashable requirement.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Ok, if I add that valid code, just add it, the error in that file goes away. But it still doesn't work. of course. If I remove the block with the static func as shown, it complains about the Chain. Type 'Chain' does not conform to protocol 'Equatable' – Harry McGovern Apr 04 '19 at 15:55
  • @HarryMcGovern: *Why* did you remove `static func ==` ? – Martin R Apr 04 '19 at 17:17
  • Thanks, that part now seems to be working. I need to post a new question related to this tutorial. – Harry McGovern Apr 05 '19 at 07:48
  • ok, its come up again in the game. var value = topLeft.hashValue value = value | topRight.hashValue << 1 value = value | bottomLeft.hashValue << 2 value = value | bottomRight.hashValue << 3 – Harry McGovern Apr 05 '19 at 08:26
  • 1
    @HarryMcGovern: I have no idea what you are talking about, the code does not appear anywhere in your question. I would suggest to ask a *new question* (with some self-contained code demonstrating the problem). – Martin R Apr 05 '19 at 09:16