Swift - check if two objects are of same type or superclass-type

Viewed 403

I want to check if two objects belong to - or are of - the same superclass.

class myClass {}
class mySubclass: myClass {}

let myObject = myClass()
let myOtherObject = myClass()
let mySubclassedObject = mySubclass()

I know that I can check the type this way:

type(of: myObject) == myClass.self                // true
type(of: myObject) == type(of: myOtherObject)     // true

type(of: mySubclassedObject) == myClass.self      // false

The last statement returns false, as type(of: <T>) obviously returns the object's final class.

Another way for type checking is the is keyword:

mySubclassedObject is mySubclass     // true
mySubclassedObject is myClass        // true 

Here both statements evaluate to true, as the is keyword is obviously considering the superclass. But what I can't do that way is comparing two objects' types like this:

myOtherObject is myObject   // Use of undeclared type 'myObject'

I need to get results like this:

myObject is myOtherObject        // true 
mySubclassedObject is myObject   // true
myObject is mySubclassedObject   // false

I was hoping to find something like .isSubclassOf but this seems to be unavailable in Swift. So how could I get my desired result?

3 Answers

One way could be to try generics like this

func isObject<P>(_ object: Any, ofType other: P) -> Bool {
    object is P
}

isObject(myObject, ofType: myOtherObject)           //true
isObject(mySubclassedObject, ofType: myObject)      //true
isObject(myObject, ofType: mySubclassedObject)      //false

I suppose that you ask about the same functionality as Type.IsSubclassOf(Type) on .Net platform

Their doc says

the method Type.IsSubclassOf(Type) return true if the current Type derives from c; otherwise, false. This method also returns false if c and the current Type are equal. source: microsoft doc

Swift has the type check operation (is) to check whether an instance is of a certain subclass type but it returns true if both are the same type. This behavior makes it different from .Net Type.IsSubclassOf(Type) method. source

The solution is to develop a custom function which will be similar in behavior to the Type.IsSubclassOf(Type).

Any, AnyClass and AnyObject can't have an extensions so the best way it to create a global function:

func type<T: AnyObject >(_ object: AnyObject, isSubclassOf toto: T) -> Bool {
    if object is T {
        if Swift.type(of: object) === Swift.type(of: myOtherObject) {
            return false
        } else {
            return true
        }
    } else {
        return false
    }
}

The function above will pass the following tests:

// MARK: - --- UnitTests ---
import XCTest

class TypeIsSubclassOfTests: XCTestCase {
    func testSubclass() {
        XCTAssertFalse(type(myObject, isSubclassOf: myOtherObject))
        XCTAssertFalse(type(myOtherObject, isSubclassOf: myObject))
        XCTAssertTrue(type(mySubclassedObject, isSubclassOf: myObject))
        XCTAssertFalse(type(myObject, isSubclassOf: mySubclassedObject))
    }
}

The usage of this function is as follows:

if type(object1, isSubclassOf: object2) {
    debugPrint("object1 is a class which subclass from object2 class")
} else {
    debugPrint("object1 is the same class as object2 or is not a subclass of object2 class")
}

The is operator expects a type on the rhs (right hand side), not an instance/object.

Swift convention is that you don't begin a class name with a lower case letter. If I rewrite your code according to convention, substituting the class name for the object type, I get the results you expect:

class MyClass {}
class MySubclass: MyClass {}

let myObject = MyClass()
let myOtherObject = MyClass()
let mySubclassedObject = MySubclass()

type(of: myObject) == MyClass.self                // true
type(of: myObject) == type(of: myOtherObject)     // true

type(of: mySubclassedObject) == MyClass.self      // false

mySubclassedObject is MySubclass     // true
mySubclassedObject is MyClass        // true

myOtherObject is MyClass

myObject is MyClass        // true
mySubclassedObject is MyClass   // true
myObject is MySubclass   // false

The is operator expects a type on the rhs, not an object.

Related