48
votes

I know that I can check the type of a var in Swift with is

if item is Movie {
    movieCount += 1
} else if item is Song {
    songCount += 1
}

but how can I check that two instances have the same class? The following does not work:

if item1 is item2.dynamicType {
    print("Same subclass")
} else {
    print("Different subclass)
}

I could easily add a "class" function and update it in each subclass to return something unique, but that seems like a kludge...

8
Usually, such comparison is no needed in Swift.Sulthan
Well, the reason I used "subclass" rather than "class" in the example is the clue - it's quite common to have several interacting subclasses that are generally treated similarly, but do something special when they are the same - say for example subclasses of Animal which interact (move away from, say) all other subclasses, but not their own...Grimxn
No, it that case you check that both classes are Animal, you don't normally check that their classes are the same. If you have different classes with the same interface (they can interact with each other) but you don't want them to interact with each other in some cases then there is something seriously wrong with your design.Sulthan
If you are still open minded for a pure Swift solution for AnyObject, please take a look on my answer.holex
Excellent - many thanks! I've changed the accepted answer to yours.Grimxn

8 Answers

48
votes

I also answered How do you find out the type of an object (in Swift)? to point out that at some point Apple added support for the === operator to Swift Types, so the following will now work:

if item1.dynamicType === item2.dynamicType {
    print("Same subclass")
} else {
    print("Different subclass")
}

This works even without importing Foundation, but note it will only work for classes, as structs have no dynamic type.

47
votes

Swift 3.0 (also works with structs)

if type(of: someInstance) == type(of: anotherInstance) {
    print("matching type")
} else {
    print("something else")
}
34
votes

I feel necessary to quote from the Swift Programming Language documentation first of all:

Classes have additional capabilities that structures do not:

  • Type casting enables you to check and interpret the type of a class instance at runtime.

According to this, it may be helpful for someone in the future:

func areTheySiblings(class1: AnyObject!, class2: AnyObject!) -> Bool {
    return object_getClassName(class1) == object_getClassName(class2)
}

and the tests:

let myArray1: Array<AnyObject> = Array()
let myArray2: Array<Int> = Array()
let myDictionary: Dictionary<String, Int> = Dictionary()
let myString: String = String()

let arrayAndArray: Bool = self.areTheySiblings(myArray1, class2: myArray2) // true
let arrayAndString: Bool = self.areTheySiblings(myArray1, class2: myString) // false
let arrayAndDictionary: Bool = self.areTheySiblings(myArray1, class2: myDictionary) // false

UPDATE

you also can overload a new operator for doing such a thing, like e.g. this:

infix operator >!<

func >!< (object1: AnyObject!, object2: AnyObject!) -> Bool {
   return (object_getClassName(object1) == object_getClassName(object2))
}

and the results:

println("Array vs Array: \(myArray1 >!< myArray2)") // true
println("Array vs. String: \(myArray1 >!< myString)") // false
println("Array vs. Dictionary: \(myArray1 >!< myDictionary)") // false

UPDATE#2

you can also use it for your own new Swift classes, like e.g. those:

class A { }
class B { }

let a1 = A(), a2 = A(), b = B()

println("a1 vs. a2: \(a1 >!< a2)") // true
println("a1 vs. b: \(a1 >!< b)") // false
12
votes

Swift 3 - pay attention that comparing instances is not the same as checking if an istance is of a given type:

struct Model {}

let modelLhs = Model()
let modelRhs = Model()
type(of: modelLhs) == type(of: modelRhs) //true
type(of: modelLhs) == type(of: Model.self) //false
modelLhs is Model //true
3
votes

For subclasses of NSObject, I went with:

let sameClass: Bool = instance1.classForCoder == instance2.classForCoder

Another caveat of this method,

The private subclasses of a class cluster substitute the name of their public superclass when being archived.

Apple documentation

1
votes

i'm using this, looks helpful for me: it returns true only if all objects are of same type;

func areObjects<T>(_ objects: [Any], ofType: T.Type) -> Bool {
    for object in objects {
        if !(object is T) {
            return false
        }
    }
    return true
}
1
votes

In Swift 5 you can test whether one value is the same type as another, if one of the values is 'self', by using 'Self', its type. This works with structs, as you can see below.

(I am actually here looking for a way to do this without Self as I am trying to refactor something. The 'type(of: ship)' method mentioned above, looks like the most general way to do this. I have tested that it works with the example below.)

protocol Ship { var name: String {get} }

extension Ship {
    func isSameClass(as other: Ship) -> Bool {
        return other is Self
    }
}

struct Firefly: Ship { var name: String }
struct Trebuchet: Ship { var name: String }

func speak(about s1: Ship, and s2: Ship) {
    print(  "\(s1.name) and \(s2.name) are "
        +   "\(s1.isSameClass(as: s2) ? "" : "not ")"
        +   "the same class." )
}

func talk(about s1: Ship, and s2: Ship) {
    print(  "\(s1.name) and \(s2.name) are "
        +   "\(type(of: s1) == type(of: s2) ? "" : "not ")"
        +   "the same class." )
}

var serenity = Firefly(name: "Serenity")
var saffron = Firefly(name: "Saffron")
var inara = Trebuchet(name: "Inara")

speak(about: serenity, and: saffron)    // Serenity and Saffron are the same class.
speak(about: serenity, and: inara)      // Serenity and Inara are not the same class.
talk(about: serenity, and: saffron)     // Serenity and Saffron are the same class.
talk(about: serenity, and: inara)       // Serenity and Inara are not the same class.
0
votes

At the moment Swift types have no introspection, so there is no builtin way to get the type of an instance. instance.className works for Objc classes.