2

I've got a class Email:

import SwiftyJSON

class Email: NSObject {

    required init?(JSON jsonObject: AnyObject) {
        let emailJsonObject = JSON(jsonObject)

        self.email = emailJsonObject["emailaddress"].stringValue
        self.emailType = emailJsonObject["emailtype"].stringValue
    }

   var email: String
   var emailType: String

}

func == (lhs: Email, rhs: Email) -> Bool {
    return lhs.email == rhs.email && lhs.emailType == rhs.emailType
}

Now if I have two Arrays:

let newEmails = emailsJsonObjects.map({ return Email(JSON: $0)! }).sort({ $0.0.email < $0.1.email })

let currentEmails = (self.emails as! [Email]).sort({ $0.0.email < $0.1.email })

Both have one element, and they have the same email and emailType, here's what I've got by comparing them:

(lldb) po newEmails.count
1

(lldb) po currentEmails.count
1

(lldb) po newEmails == currentEmails
false

(lldb) po newEmails[0] == currentEmails[0]
true

Am I missing something? Are the comparisons different?

diogocarmo
  • 960
  • 1
  • 11
  • 30
  • 2
    `newEmails == currentEmails` asks if the two arrays are the same array which, presumably they aren't. `newEmails[0] == currentEmails[0]` asks if the Email at index 0 in `newEmails` is the same as the Email at index 0 in `currentEmails`. – Paulw11 May 13 '16 at 12:07
  • Paulw11: I thought that too, but according to this (http://stackoverflow.com/a/27580740), it actually runs through all the array elements. And I did some testing in a Playground and it actually works as expected, it's just not working in this case. – diogocarmo May 13 '16 at 12:30
  • Perhaps it is just the debugger then. – Paulw11 May 13 '16 at 12:53
  • When you run the code for real (i.e. not in the debugger) does it work correctly (return `true`) or fail (return `false`)? – Robotic Cat May 13 '16 at 13:01
  • @Paulw11: it's not the debugger, my code is reporting the same thing. – diogocarmo May 13 '16 at 13:02
  • @RoboticCat: it doesn't, I have the same result as the debugger. – diogocarmo May 13 '16 at 13:02
  • I've just been checking my understanding on `NSHipster`'s article on Swift Comparison Protocols: http://nshipster.com/swift-comparison-protocols/ .Do you need to make the `Email` class `Equatable` as well? – Robotic Cat May 13 '16 at 13:09
  • @RoboticCat I tried making Email Equatable in a playground but got an error saying that it already conforms to Equatable (through NSObject). That's why I chose to override isEqual instead. – JeremyP May 13 '16 at 13:35
  • 1
    So basically, if the `Email` class does not need to be a subclass of `NSObject` it should follow the `Equatable` protocol instead. So it follows then that making a Swift class follow the `NSObject` protocol introduces possible side-effects. Interesting. Thanks for the info @JeremyP. – Robotic Cat May 13 '16 at 13:39

1 Answers1

3

The problem is that, because your objects inherit from NSObject the array is using isEqual() to do the comparison of array elements. Add the following override to your class definition

override func isEqual(object: AnyObject?) -> Bool
{
    if let rhs = object as? Email
    {
        return self.email == rhs.email && self.emailType == rhs.emailType
    }
    return false
}

And it should all be fine.

JeremyP
  • 84,577
  • 15
  • 123
  • 161
  • Interesting: so the `==` function is not mapped to `isEqual:` in the runtime. I did not know that. I was wondering if it was something to do with `reference` types vs `value` types though. Thanks for the info. – Robotic Cat May 13 '16 at 13:21
  • 1
    @RoboticCat In a sense it is. The default `isEqual` compares references and that is what happens if you don't override it. – JeremyP May 13 '16 at 13:33
  • That was it! Wow so weird. As @RoboticCat mentioned, making a Swift class subclass `NSObject` introduces side-effects. Interesting. Thanks for the help! – diogocarmo May 13 '16 at 15:50
  • @dccarmo If you think about it, in Objective-C, overriding `isEqual:` would have been obvious. It's a bit obscure here because of the implicit casting that is going on. – JeremyP May 17 '16 at 12:52