Reputation: 76918
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()
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 :)
Upvotes: 305
Views: 281988
Reputation: 9925
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.
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 struct
s, enum
s, and final class
) or the dynamic type (in the case of non-final
class
es).
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 mirrorsself
, 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. Usingself.dynamicType
fights against Swift's goals of concision and clarity in that it is both noisy and esoteric.Note that
self.dynamicType.classMember
andTypeName.classMember
may not be synonyms in class types with non-final members.
Upvotes: 7
Reputation: 7778
Old question, but this works for my need (Swift 5.x):
print(type(of: myObjectName))
Upvotes: 11
Reputation: 139
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
}
}
Upvotes: 2
Reputation: 2617
For Swift 3.0
String(describing: <Class-Name>.self)
For Swift 2.0 - 2.3
String(<Class-Name>)
Upvotes: 13
Reputation: 304
Swift 3:
if unknownType is MyClass {
//unknownType is of class type MyClass
}
Upvotes: 21
Reputation: 15615
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`
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))
Upvotes: 43
Reputation: 17428
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()
Upvotes: 60
Reputation: 2694
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!")
}
Upvotes: 113
Reputation: 3414
//: 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
Upvotes: 0
Reputation: 1023
If you get an "always true/fails" warning you may need to cast to Any before using is
(foo as Any) is SomeClass
Upvotes: 3
Reputation: 1814
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>
}
Upvotes: 39
Reputation: 427
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.
Upvotes: 1
Reputation: 39181
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")
}
Upvotes: 9