I was wondering if it's possible to somehow switch over identities in Swift. I have a couple of class instances (Buttons) and an instance which is a supertype of those instances.
Currenly I'm doing a bunch of if instance_1 === currentSuperTypeInstance (called differently in the real codebase), but I think it's very ugly and verbose.
Is there a way to use switch to find out if the identity of an instance is the same (similar to what === operator does)?
One possibility would be to define your own ~= operator for this purpose, perhaps for matching ObjectIdentifier to AnyObject:
func ~=(id: ObjectIdentifier, object: AnyObject) -> Bool {
return id == ObjectIdentifier(object)
}
class X {}
let x = X()
let y = X()
switch x {
case ObjectIdentifier(x): print("x")
case ObjectIdentifier(y): print("y")
default: print("something else")
}
Thx Joe. I never was comfortable to use the ~= operator. The pattern from switch will be passed to the first parameter of the ~= overload while the expression before the switch body will land as the second parameter right?
At the risk of stating the obvious: If that happen to be instances of UIButton (or NSButton) then you can just match against the instances, for example
@IBOutlet var button1, button2: UIButton!
@IBAction func action(_ sender: UIControl) {
switch sender {
case button1: print("button 1")
case button2: print("button 2")
default: break
}
}
NSObject subclasses are all Equatable using NSObject's -isEqual: implementation. That'll work for identity testing as long as the subclass doesn't override -isEqual: with different meaning. If you want to ensure you're doing only identity testing, it'd be better to use a matching operation that explicitly uses identity.
Swift provides default implementations of the == and === operators and adopts the Equatable protocol for objects that derive from the NSObject class. The default implementation of the == operator invokes the isEqual: method, ...
The base implementation of the isEqual: provided by the NSObject class is equivalent to an identity check by pointer equality. ...
NSString, NSNumber are are typical examples where isEqual:is overridden. I would assume that this is not the case for UIControl and its subclasses, but am not sure if one can always rely on it.
Just a tip: When the type name is repeated multiple times and adds to noise, I just add a local type alias right above the usage site to reduce visual clutter. Like this:
typealias Id = ObjectIdentifier
switch Id(x) {
case Id(x): print("x")
case Id(y): print("y")
default: print("something else")
}
That's exactly why I don't trust == on subclasses of NSObject, it's an equal operator which should not check the identity but the equality of an instance.
== checks for substitutability, so if 2 instances with same values (e.g. 2 views with everything the same) are not substitutable (code won't perform the same outcomes if you swap them) then it makes sense that == actually works as ===.
More in general the object shouldn't conform to Equatable at all, but that can't be really done with NSObject while keeping it working together with isEqual:
I disagree with that (at least as long as we don't support factory inits for classes). An object in Swift currently is an instance of a class only which already means that this thing does require reference semantics and not value semantics. You can have multiple objects (non-subclasses of NSObject) that could have the same identifier and therefore considered equal regardless of all possible other states of such two objects.