Comparing in Swift

The whole world is obsessed with comparison. Lebron vs. Jordan, Obama vs. Putin, Viper vs. Mountain, iOS vs. Android, and the list goes on. Let’s indulge ourselves in this perverse pleasure of comparison for a bit here. Say we want to compare The Dark Knight with Man of Steel. We can start by creating a class that stores movie information.

class Movie {
    let name: String
    var tomatometer: Int

    init(name: String, tomatometer: Int = 0) {
        self.name = name
        self.tomatometer = tomatometer
    }
}

Now let’s compare.

let darkKnight = Movie(name: "Dark Knight", tomatometer: 94)
let manOfSteel = Movie(name: "Man of Steel", tomatometer: 56)

if manOfSteel < darkKnight {
    println("The Dark Knight is a better movie than Man of Steel.")
}

Swift gets angry at us when we start comparing things willy-nilly. It shows us a rather cryptic message: Cannot invoke ‘>’ with an argument list of type ‘(Movie, Movie)’. Perhaps, it’s trying to warn us that “comparison is the thief of joy” as Theodore Roosevelt did. But we choose to ignore the words of wisdom and pursuade Swift to allow us to compare. Our weapon of persuasion happens to be the Comparable protocol.

class Movie : Comparable {
    let name: String
    var tomatometer: Int

    init(name: String, tomatometer: Int = 0) {
        self.name = name
        self.tomatometer = tomatometer
    }
}

func < (lhs: Movie, rhs: Movie) -> Bool {
    return lhs.tomatometer < rhs.tomatometer
}

func == (lhs: Movie, rhs: Movie) -> Bool {
    return lhs.name == rhs.name
}

We don’t need to work too hard to satisfy Swift’s needs as far as comparison is concerned. All we need to do is implement < and == operators. Swift will provide implementations for the rest: <=, >=, and >.

Did you notice that we had to provide declarations for < and == at global scope? Weird, huh? The declarations aren’t inside the Movie class because these operators aren’t supposed to be called as methods like this: manOfSteel.< darkKnight.

By the way, Comparable protocol itself inherits from two other protocols.

protocol Comparable : _Comparable, Equatable {
    func <=(lhs: Self, rhs: Self) -> Bool
    func >=(lhs: Self, rhs: Self) -> Bool
    func >(lhs: Self, rhs: Self) -> Bool
}
protocol _Comparable {
    func <(lhs: Self, rhs: Self) -> Bool
}
protocol Equatable {
    func ==(lhs: Self, rhs: Self) -> Bool
}

As it turns out, the two operators (< and ==) we were required to implement above come from _Comparable and Equatable protocols. Now the following code should print “The Dark Knight is a better movie than Man of Steel.” in the console.

if manOfSteel < darkKnight {
    println("The Dark Knight is a better movie than Man of Steel.")
}

Swift now allows us to do following comparisons as well.

if manOfSteel <= darkKnight {
    println("Didn't I already tell you The Dark Knight is better?")
}

if manOfSteel == darkKnight {
    println("Are you insane?")
}

if manOfSteel >= darkKnight {
    println("No way!!")
}

if manOfSteel > darkKnight {
    println("I think you're losing your mind.")
}

Let’s stop before we get addicted to this game of comparison.

 
3
Kudos
 
3
Kudos

Now read this

Fixing Core Data Concurrency Violations

With iOS 8.0 Apple gave us access to a tool for tracking down code that violates Core Data concurrency rules. This post outlines how to setup Xcode so that you can identify and fix the faulty code. Background According to the Core Data... Continue →