37

This line of code used to work with Swift 2, but now is incorrect in Swift 3.

if gestureRecognizer.isMember(of: UITapGestureRecognizer) { }

I get this error: Expected member name or constructor call after type name.

What is the correct way to use isMember(of:)?

mfaani
  • 33,269
  • 19
  • 164
  • 293
Van Du Tran
  • 6,736
  • 11
  • 45
  • 55

4 Answers4

138

Most likely, you'll want to not only check the type, but also cast to that type. In this case, use:

if let gestureRecognizer as? UITapGestureRecognizer { }
else { /* not a UITapGestureRecognizer */ }

Swift casting operators

These operators are only available in Swift, but still work when dealing with Objective C types.

  • The as operator

    • The as operator performs a cast when it is known at compile time that the cast always succeeds, such as upcasting or bridging. Upcasting lets you use an expression as an instance of its type’s supertype, without using an intermediate variable.

    • This is the most preferable operator to use, when possible. It guarentees success, without worrying about unwrapping an optional or risking a crash.
  • The as? operator

    • The as? operator performs a conditional cast of the expression to the specified type. The as? operator returns an optional of the specified type. At runtime, if the cast succeeds, the value of expression is wrapped in an optional and returned; otherwise, the value returned is nil. If casting to the specified type is guaranteed to fail or is guaranteed to succeed, a compile-time error is raised.

    • This is the second most preferable operator to use. Use it to safely handle the case in which a casting operator can't be performed.

  • The as! operator

    • The as! operator performs a forced cast of the expression to the specified type. The as! operator returns a value of the specified type, not an optional type. If the cast fails, a runtime error is raised. The behavior of x as! T is the same as the behavior of (x as? T)!.

    • This is the least preferable operator to use. I strongly advise against abusing it. Attempting to cast an expression to an incompatible type crashes your program.


Swift type checking

If you merely want to check the type of an expression, without casting to that type, then you can use these approaches. They are only available in Swift, but still work when dealing with Objective C types.

  • The is operator

    • The is operator checks at runtime whether the expression can be cast to the specified type. It returns true if the expression can be cast to the specified type; otherwise, it returns false
    • Works on any Swift type, including Objective C types.
    • Swift equivalent of isKind(of:)
  • Using type(of:)

    • Unlike the is operator, this can be used to check the exact type, without consideration for subclasses.
    • Can be used like: type(of: instance) == DesiredType.self
    • Swift equivalent of isMember(of:)

Legacy (Objective C) methods for checking types

These are all methods on NSObjectProtocol. They can be used in Swift code, but they only apply work with classes that derive from NSObjectProtocol (such as subclasses of NSObject). I advise against using these, but I mention them here for completeness

  • isKind(of:)

    • Returns a Boolean value that indicates whether the receiver is an instance of given class or an instance of any class that inherits from that class
    • Avoid this in Swift, use is operator instead.
  • isMember(of:)

    • Returns a Boolean value that indicates whether the receiver is an instance of a given class
    • Avoid this in Swift, use type(of: instance) == DesiredType.self instead.
  • conforms(to:)

    • Returns a Boolean value that indicates whether the receiver conforms to a given protocol.
    • Avoid this in Swift, use is operator instead.
Alexander
  • 59,041
  • 12
  • 98
  • 151
  • Good catch, I'm actually writing a breakdown of all the various techniques. Hold tight – Alexander Nov 02 '16 at 19:51
  • @Hamish Hah, I meant `isKind(of:)`. Actually I can't figure out the swift equivalent of `isMember(of:)`. I would've thought you could use something like: `type(of: "") == String.self`, but that's invalid – Alexander Nov 02 '16 at 19:58
  • 1
    Huh, weird. Looks like the string literal is messing with the compiler somehow. `type(of: String()) == String.self` works fine. It also works if you assign it first... `let f = type(of: ""); if f == String.self {}` – Hamish Nov 02 '16 at 19:59
  • 1
    @Hamish Hmm, interesting. – Alexander Nov 02 '16 at 20:09
  • 1
    No problem – that edit just then was the last one, I promise ;) – Hamish Nov 02 '16 at 20:25
  • Great, but what if you need to check the type of an arbitrary object? You can't use `as? type(of: myObj)` or use a variable to refer to `type(of: myObj)` after the `as?`. – Alex Zavatone May 24 '19 at 16:18
  • 1
    @AlexZavatone Neither of those would work, nor does it make sense. Swift is a statically typed language. All types in the program have to be resolved at compile time. When you cast to `type(of: myObj)`, it's not known at compile time what type you're casting to (because `myObj` can be anything), so it doesn't provide any value what do ever. If you think about it, what methods/properties can you expect to use, on a variable whose type isn't known until runtime? – Alexander May 24 '19 at 16:28
  • I'm wondering why you recommend using Swift `is` instead of `isKind(of:)`, as AFAICS the `is` cannot work with runtime types, only types known at compile time. For example, say you are given 2 x `obj: Animal.Type`: one object with `type(of: obj) == Cat.self` and another with `Animal.self`, and can see that `Cat` inherits from `Animal`, and cannot change any of that. Now you want to see if the two Types you are given inherit. You cannot use `is` because obj isn't known at compile time, but you can use `isKind(of:)`... am I missing something? – xaphod Nov 28 '19 at 14:38
  • @xaphod I have no idea what you tried, but `is`/`as` are *definitely* aware of runtime types. They can also see through existential containers and see the contained type. https://repl.it/@alexandermomchilov/Runtime-type-checking-with-is – Alexander Nov 28 '19 at 14:44
  • That works because the thing on the right-hand side of the `is` (`Cat`) is known at compile time, not runtime. I've typed up an example of the problem I mean as concisely as I can -- can you show me what line 13 of this code should be? https://gist.github.com/xaphod/286a823e498e61784b9a1cf37b0fe442 – xaphod Nov 28 '19 at 15:08
  • 1
    @xaphod It doesn't matter that the right hand type is known at compile time. It just matters that it *is* a type, whether known (a concrete type), or unknown (a generic that won't be known until runtime). Using `is` with `tType: Animal.Type` fails, because `tType` is a metatype *object*, not a type. You just need a generic to solve your problem. See https://gist.github.com/xaphod/286a823e498e61784b9a1cf37b0fe442#gistcomment-3095633 – Alexander Nov 28 '19 at 15:18
  • Ah thank you, that's the syntactic I didn't know -- that generics allow you to use `is` with a metatype object. Thanks a lot! – xaphod Nov 28 '19 at 15:23
  • 1
    @xaphod You're not using `is` with a metatype object. You're using `is` with `T`, which *is* a type. The `ofType: T.Type` param is actually pretty much useless. It's just used to communicate to the type inference system what the value of `T` should be (since you're passing in `Dog.self`, the only value of `T` that works is `Dog`). Notice that `ofType` param isn't even used anywhere in the function's body. – Alexander Nov 28 '19 at 15:26
20

There are several ways to check the class of an object. Most of the time you will want to use either the is or the as? operators like so:

let gestureRecognizer: UIGestureRecognizer = UITapGestureRecognizer()

// Using the is operator
if gestureRecognizer is UITapGestureRecognizer {
    // You know that the object is an instance of UITapGestureRecognizer,
    // but the compiler will not let you use UITapGestureRecognizer specific
    // methods or properties on gestureRecognizer because the type of the
    // variable is still UIGestureRecognizer
    print("Here")
}

// Using the as? operator and optional binding
if let tapGestureRecognizer = gestureRecognizer as? UITapGestureRecognizer {
    // tapGestureRecognizer is the same object as gestureRecognizer and is
    // of type UITapGestureRecognizer, you can use UITapGestureRecognizer
    // specific methods or properties.
    print("Here")
}

// Using the type(of:) global function
if type(of: gestureRecognizer) == UITapGestureRecognizer.self {
    // gestureRecognizer is an instance of UITapGestureRecognizer, but not any
    // of its subclasses (if gestureRecognizer was an instance of a subclass of
    // UITapGestureRecognizer, the body of this if would not execute).
    // This kind of check is rarely usefull, be sure this is really what you
    // want to do before you use it.
    print("Here")
}

// Using the isKind(of:) method
if gestureRecognizer.isKind(of: UITapGestureRecognizer.self) {
    // Like for the is operator, you know that the object is an instance of
    // UITapGestureRecognizer (or any subclass of UITapGestureRecognizer).
    // This is the Objective-C version of the is operator and will only work
    // on classes that inherit from NSObject, don't use it in Swift.
    print("Here")
}

// Using the isMember(of:) method
if gestureRecognizer.isMember(of: UITapGestureRecognizer.self) {
    // gestureRecognizer is an instance of UITapGestureRecognizer, but not
    // any of its subclasses.
    // This is the Objective-C version of type(of:) and will only work on
    // classes that inherit from NSObject, don't use it in Swift.
    print("Here")
}
deadbeef
  • 5,409
  • 2
  • 17
  • 47
5

You have to use .self to refer the class type now.

let a = UITapGestureRecognizer()
print (a.isMember(of: UIGestureRecognizer.self))

There is also:

print (a is UITapGestureRecognizer)
Josh Homann
  • 15,933
  • 3
  • 30
  • 33
3

Swift 3:

if gestureRecognizer is UITapGestureRecognizer {
            //It's a tap
}
Zigglzworth
  • 6,645
  • 9
  • 68
  • 107