How do you find out the type of an object (in Swift)?
Asked Answered
M

14

303

When trying to understand a program, or in some corner-cases, it's useful to find out what type something is. I know the debugger can show you some type information, and you can usually rely on type inference to get away with not specifying the type in those situations, but still, I'd really like to have something like Python's type()

dynamicType (see this question)

Update: this has been changed in a recent version of Swift, obj.dynamicType now gives you a reference to the type and not the instance of the dynamic type.

This one seems the most promising, but I haven't been able to find out the actual type so far.

class MyClass {
    var count = 0
}

let mc = MyClass()

# update: this now evaluates as true
mc.dynamicType === MyClass.self

I also tried using a class reference to instantiate a new object, which does work, but oddly gave me an error saying I must add a required initializer:

works:

class MyClass {
    var count = 0
    required init() {
    }
}

let myClass2 = MyClass.self
let mc2 = MyClass2()

Still only a small step toward actually discovering the type of any given object though

edit: I've removed a substantial number of now irrelevant details - look at the edit history if you're interested :)

Maidie answered 7/6, 2014 at 20:46 Comment(6)
possible duplicate of How to get the class of a variable in Swift?Hyperthyroidism
Interestingly, print(mc) or dump(mc) will print a summary (which you can get from toString(mc) or reflect(mc).summary), which will contain the class name in there somewhere. But it's not clear how to get just the class name yourself.Schiff
@David similar, but not all variables are class instances. Also that questions was really about checking if the type matches what the programmer is looking for, whereas I'm hoping to just find out the type wholesaleMaidie
possible duplicate of How do I print the type or class of a variable in Swift?Phan
possible duplicate of How to check two instances are the same class/type in swiftHokusai
use type(of: yourObjectRefference)Montmartre
S
371

Swift 3 version:

type(of: yourObject)
Sunbow answered 19/10, 2016 at 13:27 Comment(10)
Fun fact. This does not work with implicitly unwrapped optionals! i.e. var myVar: SomeType!. Compiler gives the error "Cannot convert value of type 'SomeType!.Type' (aka 'ImplicitlyUnwrappedOptional<SomeType>.Type') to expected argument type 'AnyClass' (aka 'AnyObject.Type') Compiler suggest adding as! AnyClass after the type but then program crashes with some "EXC_BAD_INSTRUCTION" and other jiberrish that I cannot decipher.Feudalize
Indeed, this should be the accepted answer now that Swift 3 exists. Thank you Jeremy!Desiderate
If you are looking for the specific type name, when the type is of a protocol type, this may not work for you.Clift
If you have a String that is passed as type Any then type(of:) will output Any, not String.Borges
@Borges so what will be the solution. Can you provide?Openhearth
Depending on your objective: if let yourObjectAsString = yourObject as? String {Borges
Not when it's something like a presentedViewController. ▿ Optional<UIViewController> - some : <UIDocumentMenuViewController: 0x153d43050> po type(of: presentedViewController) Swift.Optional<__C.UIViewController> What we want back is UIDocumentMenuViewController. type(of: anyObject) doesn't do that.Merline
if type(of: yourObject) == MyObjectClass.self { ... }Demaggio
Tip: To get the dynamic type of self you can just use Self.self. E.g. To check if self is a subclass of your current class or not, you can use Self.self == ThisClass.self.Sidwohl
@Borges Swift 5.1 now has a solution for your described issue.Chinfest
M
113

Swift 2.0:

The proper way to do this kind of type introspection would be with the Mirror struct,

    let stringObject:String = "testing"
    let stringArrayObject:[String] = ["one", "two"]
    let viewObject = UIView()
    let anyObject:Any = "testing"

    let stringMirror = Mirror(reflecting: stringObject)
    let stringArrayMirror = Mirror(reflecting: stringArrayObject)
    let viewMirror = Mirror(reflecting: viewObject)
    let anyMirror = Mirror(reflecting: anyObject)

Then to access the type itself from the Mirror struct you would use the property subjectType like so:

    // Prints "String"
    print(stringMirror.subjectType)

    // Prints "Array<String>"
    print(stringArrayMirror.subjectType)

    // Prints "UIView"
    print(viewMirror.subjectType)

    // Prints "String"
    print(anyMirror.subjectType)

You can then use something like this:

    if anyMirror.subjectType == String.self {
        print("anyObject is a string!")
    } else {
        print("anyObject is not a string!")
    }
Medora answered 7/10, 2015 at 20:8 Comment(5)
This is great. Beware that if the object being mirrored is of optional type, then comparing it to a non-optional type will fail. String and Optional(String) are not the same.Osteoid
Exactly what i was looking for, wanted to know what is the type of the objectNigrosine
Is there a type of comparison in this context that will not fail when comparing optional against non-optional types?Clift
That's what I was looking for. Thank you @Gudbergur.Openhearth
This is sweet! But yeah definitely you should add for optional use case (ie: let anyObject: Any? = "testing"), check for nil -> if not nil -> make non-optional -> and continue with this answer as regularLuminal
G
60

The dynamicType.printClassName code is from an example in the Swift book. There's no way I know of to directly grab a custom class name, but you can check an instances type using the is keyword as shown below. This example also shows how to implement a custom className function, if you really want the class name as a string.

class Shape {
    class func className() -> String {
        return "Shape"
    }
}

class Square: Shape {
    override class func className() -> String {
        return "Square"
    }
}

class Circle: Shape {
    override class func className() -> String {
        return "Circle"
    }
}

func getShape() -> Shape {
    return Square() // hardcoded for example
}

let newShape: Shape = getShape()
newShape is Square // true
newShape is Circle // false
newShape.dynamicType.className() // "Square"
newShape.dynamicType.className() == Square.className() // true

Note:
that subclasses of NSObject already implement their own className function. If you're working with Cocoa, you can just use this property.

class MyObj: NSObject {
    init() {
        super.init()
        println("My class is \(self.className)")
    }
}
MyObj()
Guardianship answered 8/6, 2014 at 1:13 Comment(2)
Hey, not sure when it changed, but as Alex Pretzlav pointed out, the behavior has changed.Maidie
Yes. As of Swift 3.0, subjectType is no longer available, and dynamicType causes a deprecation message from the compiler.Audry
W
43

As of Xcode 6.0.1 (at least, not sure when they added it), your original example now works:

class MyClass {
    var count = 0
}

let mc = MyClass()
mc.dynamicType === MyClass.self // returns `true`

Update:

To answer the original question, you can actually use the Objective-C runtime with plain Swift objects successfully.

Try the following:

import Foundation
class MyClass { }
class SubClass: MyClass { }

let mc = MyClass()
let m2 = SubClass()

// Both of these return .Some("__lldb_expr_35.SubClass"), which is the fully mangled class name from the playground
String.fromCString(class_getName(m2.dynamicType))
String.fromCString(object_getClassName(m2))
// Returns .Some("__lldb_expr_42.MyClass")
String.fromCString(object_getClassName(mc))
Wooton answered 14/10, 2014 at 16:44 Comment(2)
Looks like they changed it to give you the type instead of an instance.Maidie
@Jiaaro, I updated my answer with what I think that you were looking for in your original questionWooton
F
39

If you simply need to check whether the variable is of type X, or that it conforms to some protocol, then you can use is, or as? as in the following:

var unknownTypeVariable = …

if unknownTypeVariable is <ClassName> {
    //the variable is of type <ClassName>
} else {
    //variable is not of type <ClassName>
}

This is equivalent of isKindOfClass in Obj-C.

And this is equivalent of conformsToProtocol, or isMemberOfClass

var unknownTypeVariable = …

if let myClass = unknownTypeVariable as? <ClassName or ProtocolName> {
    //unknownTypeVarible is of type <ClassName or ProtocolName>
} else {
    //unknownTypeVariable is not of type <ClassName or ProtocolName>
}
Febrifugal answered 15/10, 2014 at 14:5 Comment(2)
The second part of your answer is wrong. The 'if let' statement with as? conditional cast does the same as isKindOfClass as well, just also provides the result of the cast should it Succeed.Lexington
The equivalent of isMemberOfClass is the condition object.dynamicType == ClassName.self.Lexington
R
21

Swift 3:

if unknownType is MyClass {
   //unknownType is of class type MyClass
}
Roesch answered 7/12, 2016 at 8:39 Comment(1)
I think is exists from before Swift 3...?Chaetognath
J
12

For Swift 3.0

String(describing: <Class-Name>.self)

For Swift 2.0 - 2.3

String(<Class-Name>)
Jeff answered 29/11, 2016 at 6:9 Comment(1)
The important thing about this answer being correct for me, is that the resulting string exactly matches the class name - so I can use this to get a Core Data entity name from an NSManagedObject subclass. I used the Swift3 version.Catalepsy
F
10

Old question, but this works for my need (Swift 5.x):

print(type(of: myObjectName))
Fernandofernas answered 10/11, 2020 at 15:44 Comment(1)
This is a copy of @Jérémy Lapointe's answer above (https://mcmap.net/q/82639/-how-do-you-find-out-the-type-of-an-object-in-swift)Ophthalmology
S
9

Here is 2 ways I recommend doing it:

if let thisShape = aShape as? Square 

Or:

aShape.isKindOfClass(Square)

Here is a detailed example:

class Shape { }
class Square: Shape { } 
class Circle: Shape { }

var aShape = Shape()
aShape = Square()

if let thisShape = aShape as? Square {
    println("Its a square")
} else {
    println("Its not a square")
}

if aShape.isKindOfClass(Square) {
    println("Its a square")
} else {
    println("Its not a square")
}
Sulfapyridine answered 18/5, 2015 at 13:32 Comment(2)
print( aShape is Square ), is operator is more preferable.Reis
Good solution for me to get the type of objects.Hypsometry
C
7

Comment: I don't see how @JérémyLapointe answers the question. Using type(of:) only works by checking the compile-time information even if the actual type is a more specific subclass. There is now an easier way to dynamically query the type in Swift 5.1 without resorting to dynamicType like @Dash suggests. For more details on where I got this information, see SE-0068: Expanding Swift Self to class members and value types.


Code

Swift 5.1

// Within an instance method context
Self.self

// Within a static method context
self

This allows the use of Self as shorthand for referring to the containing type (in the case of structs, enums, and final class) or the dynamic type (in the case of non-final classes).

Explanation

The proposal explains well why this approach improves on dynamicType:

Introducing Self addresses the following issues:

  • dynamicType remains an exception to Swift's lowercased keywords rule. This change eliminates a special case that's out of step with Swift's new standards. Self is shorter and clearer in its intent. It mirrors self, which refers to the current instance.
  • It provides an easier way to access static members. As type names grow large, readability suffers. MyExtremelyLargeTypeName.staticMember is unwieldy to type and read.
  • Code using hardwired type names is less portable than code that automatically knows its type.
  • Renaming a type means updating any TypeName references in code. Using self.dynamicType fights against Swift's goals of concision and clarity in that it is both noisy and esoteric.

Note that self.dynamicType.classMember and TypeName.classMember may not be synonyms in class types with non-final members.

Chinfest answered 9/3, 2021 at 10:59 Comment(0)
O
3

If you get an "always true/fails" warning you may need to cast to Any before using is

(foo as Any) is SomeClass
Overthrow answered 17/3, 2016 at 10:8 Comment(0)
B
2

If a parameter is passed as Any to your function, you can test on a special type like so :

   func isADate ( aValue : Any?) -> Bool{
        if (aValue as? Date) != nil {
            print ("a Date")
            return true
        }
        else {
            print ("This is not a date ")
            return false
        }
    }
Behaviorism answered 2/11, 2020 at 9:1 Comment(0)
S
1

Depends on the use case. But let's assume you want to do something useful with your "variable" types. The Swift switch statement is very powerful and can help you get the results you're looking for...

    let dd2 = ["x" : 9, "y" : "home9"]
    let dds = dd2.filter {
        let eIndex = "x"
        let eValue:Any = 9
        var r = false

        switch eValue {
        case let testString as String:
            r = $1 == testString
        case let testUInt as UInt:
            r = $1 == testUInt
        case let testInt as Int:
            r = $1 == testInt
        default:
            r = false
        }

        return r && $0 == eIndex
    }

In this case, have a simple dictionary that contains key/value pairs that can be UInt, Int or String. In the .filter() method on the dictionary, I need to make sure I test for the values correctly and only test for a String when it's a string, etc. The switch statement makes this simple and safe! By assigning 9 to the variable of type Any, it makes the switch for Int execute. Try changing it to:

   let eValue:Any = "home9"

..and try it again. This time it executes the as String case.

Subsequence answered 6/12, 2015 at 13:11 Comment(0)
H
0
//: Playground - noun: a place where people can play

import UIKit

class A {
    class func a() {
        print("yeah")
    }

    func getInnerValue() {
        self.dynamicType.a()
    }
}

class B: A {
    override class func a() {
        print("yeah yeah")
    }
}

B.a() // yeah yeah
A.a() // yeah
B().getInnerValue() // yeah yeah
A().getInnerValue() // yeah
Hydrous answered 4/4, 2016 at 11:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.