Skip to content

Instantly share code, notes, and snippets.

@iComputerfreak
Last active July 30, 2020 20:59
Show Gist options
  • Save iComputerfreak/6469385c6784667bc96d79c7eec454a3 to your computer and use it in GitHub Desktop.
Save iComputerfreak/6469385c6784667bc96d79c7eec454a3 to your computer and use it in GitHub Desktop.
Conforming to the Equatable protocol in a class hierarchy
import Foundation
// MARK: - Vehicle.swift
class Vehicle {
let name: String
init(name: String) {
self.name = name
}
}
// MARK: - Car.swift
class Car: Vehicle {
let seats: Int
init(name: String, seats: Int) {
self.seats = seats
super.init(name: name)
}
}
// MARK: - Bicycle.swift
class Bicycle: Vehicle {
let color: String
init(name: String, color: String) {
self.color = color
super.init(name: name)
}
}
// MARK: - Vehicle+Equatable.swift
extension Vehicle: Equatable {
static func == (lhs: Vehicle, rhs: Vehicle) -> Bool {
return type(of: lhs) == type(of: rhs) && lhs.isEqual(to: rhs)
}
@objc fileprivate func isEqual(to other: Any) -> Bool {
// other does not need to have the type Vehicle, just some subclass of it. Since it is fileprivate, that is always true.
let other = other as! Vehicle
return self.name == other.name
}
}
extension Car {
// This function will only be called, after checking, that self and other have the same type, so we can assume, that other is of type Car too.
override fileprivate func isEqual(to other: Any) -> Bool {
let otherCar = other as! Car
return super.isEqual(to: other) && self.seats == otherCar.seats
}
}
extension Bicycle {
// This function will only be called, after checking, that self and other have the same type, so we can assume, that other is of type Car too.
override fileprivate func isEqual(to other: Any) -> Bool {
let otherBicycle = other as! Bicycle
return super.isEqual(to: other) && self.color == otherBicycle.color
}
}
// MARK: - Tests
// MARK: Test Setup
let vehicle1 = Vehicle(name: "Vehicle 1")
let vehicle2 = Vehicle(name: "Vehicle")
let vehicle3 = Vehicle(name: "Vehicle")
let car1 = Car(name: "Car", seats: 5)
let car2 = Car(name: "Vehicle", seats: 2)
let car3 = Car(name: "Vehicle", seats: 5)
let bicycle1 = Bicycle(name: "Car", color: "black")
let bicycle2 = Bicycle(name: "Vehicle", color: "blue")
let bicycle3 = Bicycle(name: "Vehicle", color: "blue")
let allVehicles = [vehicle1, vehicle2, vehicle3]
// Contains all Vehicle x Vehicle pairs
let vehiclePairs = allVehicles.flatMap({ (vehicle: Vehicle) -> [(Vehicle, Vehicle)] in
var returnArray: [(Vehicle, Vehicle)] = []
for v in allVehicles {
returnArray.append((vehicle, v))
}
return returnArray
})
// Contains all Vehicle x Vehicle pairs
let vehicleTriples = vehiclePairs.flatMap({ (pair: (Vehicle, Vehicle)) -> [(Vehicle, Vehicle, Vehicle)] in
var returnArray: [(Vehicle, Vehicle, Vehicle)] = []
for v in allVehicles {
returnArray.append((pair.0, pair.1, v))
}
return returnArray
})
// MARK: Reflexivity
for vehicle in allVehicles {
assert(vehicle == vehicle)
}
// MARK: Symmetry
for pair in vehiclePairs {
if pair.0 == pair.1 {
assert(pair.1 == pair.0)
} else {
assert(pair.1 != pair.0)
}
}
// MARK: Transitivity
for triple in vehicleTriples {
if triple.0 == triple.1 && triple.1 == triple.2 {
assert(triple.0 == triple.2)
}
}
// MARK: Arbitrary Tests
// We didn't implement != ourselves, but Swift automatically generated it, as "!(==)", so we have to check it for correctness too.
assert(vehicle1 != vehicle2)
assert(vehicle1 != vehicle3)
assert(vehicle2 == vehicle3)
assert(car1 != car2)
assert(car1 != car3)
assert(car2 != car3)
assert(bicycle1 != bicycle2)
assert(bicycle1 != bicycle3)
assert(bicycle2 == bicycle3)
assert(vehicle1 != car1)
assert(vehicle1 != car2)
assert(vehicle1 != car3)
assert(vehicle1 != bicycle1)
assert(vehicle1 != bicycle2)
assert(vehicle1 != bicycle3)
assert(vehicle2 != car1)
assert(vehicle2 != car2)
assert(vehicle2 != car3)
assert(vehicle2 != bicycle1)
assert(vehicle2 != bicycle2)
assert(vehicle2 != bicycle3)
assert(vehicle3 != car1)
assert(vehicle3 != car2)
assert(vehicle3 != car3)
assert(vehicle3 != bicycle1)
assert(vehicle3 != bicycle2)
assert(vehicle3 != bicycle3)
assert(car1 != vehicle1)
assert(car1 != vehicle2)
assert(car1 != vehicle3)
assert(car1 != bicycle1)
assert(car1 != bicycle2)
assert(car1 != bicycle3)
assert(car2 != vehicle1)
assert(car2 != vehicle2)
assert(car2 != vehicle3)
assert(car2 != bicycle1)
assert(car2 != bicycle2)
assert(car2 != bicycle3)
assert(car3 != vehicle1)
assert(car3 != vehicle2)
assert(car3 != vehicle3)
assert(car3 != bicycle1)
assert(car3 != bicycle2)
assert(car3 != bicycle3)
assert(bicycle1 != vehicle1)
assert(bicycle1 != vehicle2)
assert(bicycle1 != vehicle3)
assert(bicycle1 != car1)
assert(bicycle1 != car2)
assert(bicycle1 != car3)
assert(bicycle2 != vehicle1)
assert(bicycle2 != vehicle2)
assert(bicycle2 != vehicle3)
assert(bicycle2 != car1)
assert(bicycle2 != car2)
assert(bicycle2 != car3)
assert(bicycle3 != vehicle1)
assert(bicycle3 != vehicle2)
assert(bicycle3 != vehicle3)
assert(bicycle3 != car1)
assert(bicycle3 != car2)
assert(bicycle3 != car3)
let hiddenBicycle = Bicycle(name: "Vehicle", color: "black")
assert(type(of: (hiddenBicycle as Vehicle)) == Bicycle.self)
assert((hiddenBicycle as Vehicle) != vehicle2)
print("Test finished.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment