Lope
Lope

Reputation: 5397

difference between comparisons using type(of: ) and isMember(of: )

I am trying to look for instance of specific class in array of different classes (looking for index of specific ViewController in TabBarViewController)

I have a code that checks type using type(of: ) but it doesn't work:

func getTabIndex(_ rootClass: AnyClass) -> Int? {

    if let tabVC = UIApplication.shared.delegate?.window?!.rootViewController as? UITabBarController {
        for (index, vc) in tabVC.viewControllers!.enumerated() {
            if type(of: vc) == rootClass {
                return index
            }
        }
    }

    return nil
}

if type(of: vc) == rootClass always returns false.

When I change if type(of: vc) == rootClass to vc.isMember(of: rootClass) it works fine.

I assume isMember(of: ) is obj-c method while type(of: ) is pure swift, but I don't see why it would make difference on UIViewController.

Could somebody explain why type(of: ) doesn't work in this case? In both cases, I am comparing exact types, not the subclasses. TabBar has instances of FooViewController and BarViewController. When I call getTabIndex(FooViewController.self) I get nil when using type(of: ) and 0 when using isMember(of: )

EDIT: I am aware that tpye(of: ) checks for exact type while kind(of: ) checks for subclasses as well, but in my case, I was checking exact type, so type(of: ) should be working fine, hence the confusion.

When I printed result of type(of: ) it was exactly same as when I printed value of rootClass. I know this only means that their textual representation is same and underlying types might be different, but I don't understand why that would be case

EDIT2:

Original question used isKind(of:) method which was misleading, I changed it to isMember(of: ) to reflect more closely source of my confusion, that is why type(of: ) doesn't work when comparing exact types

Upvotes: 0

Views: 597

Answers (3)

Lope
Lope

Reputation: 5397

This issue was cause by Apptimize SDK. They are doing some shady stuff with classes for their UI editor to work. This results in changing type metadata for some classes (views, view controllers) and makes type(of: ) return false even if comparing same types. isMember(of: ) works because it's obj-c method and compares type in different, less rigorous way.

I suspect this can happen with other 3rd party libraries that try to be too clever as well, but in all other cases, type(of: ) should be working without any issues

Upvotes: 0

Adrian Bobrowski
Adrian Bobrowski

Reputation: 2794

type(of:)

usage: type(of: instance) == CheckingType.self

return true ONLY when instance is exactly CheckingType

isKind(of:)

this method is from stupid Objective-C and required NSObjectProtocol in swift use instance is CheckingType

usage: instance.isKind(of: CheckingType.self)

return true when instance is CheckingType or inherits from CheckingType

is

Swift version of isKind(of:)

usage: instance is CheckingType

return true when instance is CheckingType or inherits from CheckingType


Example

import Foundation

class Animal: NSObject { }
class Cat: Animal { }

Cat() is Cat // true
Cat() is Animal // true
Cat().isKind(of: Animal.self) // true
Cat().isKind(of: Cat.self) // true
type(of: Cat()) == Animal.self // false
type(of: Cat()) == Cat.self // true

Upvotes: 2

David Pasztor
David Pasztor

Reputation: 54716

Indeed, isKind(of:) is an Objective-C method that has been around since iOS2, while type(of:) is indeed a newer Swift method.

Moreover, they are used for different things. Even though in this use case you can achieve the same results using both, they are doing different things. isKind(of:) only return a bool indicating whether the receiver is an instance of the class in the parameter value or a class inheriting from that class, while type(of:) actually returns the dynamic type of the value. Using the latter, you don't have to have any information about the instance, you can still learn its type.

Reference: type(of:), isKind(of:)

Upvotes: 1

Related Questions