Grimxn
Grimxn

Reputation: 22487

How to check two instances are the same class/type in swift

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...

Upvotes: 49

Views: 37675

Answers (8)

adazacom
adazacom

Reputation: 453

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.

Upvotes: 2

Tom Howard
Tom Howard

Reputation: 4908

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

Upvotes: 4

user3206558
user3206558

Reputation: 392

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
}

Upvotes: 1

holex
holex

Reputation: 24041

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

Upvotes: 34

andreacipriani
andreacipriani

Reputation: 2591

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

Upvotes: 12

T. Benjamin Larsen
T. Benjamin Larsen

Reputation: 6393

Swift 3.0 (also works with structs)

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

Upvotes: 52

Alex Pretzlav
Alex Pretzlav

Reputation: 15615

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.

Upvotes: 48

Joseph Mark
Joseph Mark

Reputation: 9418

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.

Upvotes: 0

Related Questions