Objective-C is very very good at introspection. You can pull things into and out of types, and generally have a blast metaprogramming yourself to whatever goal you have in mind. Swift? Not so much.
Here is the example that the Swift Programming Language manual gives for printing a type name:
class SomeBaseClass { class func printClassName() { println("SomeBaseClass") }
If you type "Hello".dynamicType into a playground, it returns (Metatype), which is as frustrating as it is useless.
This morning, I was kicking around a bunch of code, trying to search for something better. This is where I got started, which is pretty unsatisfying. Everything that follows requires importing Foundation.
func PrintableTypeName(variable : T) -> String { // Test for direct ObjC if let v = variable as? NSObject { return v.classForCoder.description() } // If the object *can* be converted to ObjC type, do so // However you lose any specifics as to type // Comment this out for more interesting results if let v : NSObject = bridgeToObjectiveC(variable) as? NSObject { if let result : NSString = v.classForCoder.description() { return result } } // Native Swift switch variable { case let test as Double: return "Double" case let test as Int: return "Int" case let test as Bool: return "Bool" case let test as String: return "String" default: break } switch variable { case let test as Double[]: return "Double[]" case let test as Int[]: return "Int[]" case let test as Bool[]: return "Bool[]" case let test as String[]: return "String[]" default: break } return "*Unknown*" }
Tom D. looked at that, chuckled, and suggested I try doing a protocol instead. So I came up with this, which has the advantage of being shorter and more extendable.
protocol DiscoverableTypeName { var typeName : String {get} } extension NSObject : DiscoverableTypeName { var typeName: String { get {return self.classForCoder.description()}}}
So if you want to create a new class, so long as it’s Objective-C-y, it will work automatically — although it returns a “mangled” version of custom class names. (More about that below.) You can also add conformance to structures but you have to do so on a one-by-one-basis.
extension CGRect : DiscoverableTypeName { var typeName : String { get {return "CGRect"} } }
Lily Ballard finally woke up at that point, having had his morning coffee and a good chuckle at my struggles, and pointed out that I could simply use NSStringFromClass() with an object’s dynamic type. (This doesn’t work for structs.) For example:
class Foo {} var f = Foo() NSStringFromClass(f.dynamicType)
This returns _TtC11lldb_expr_03Foo. This is an LLDB “mangled” Swift string that specifies that Foo is a type (_T) of class (tC), folowed by 11-ish bytes of “lldb_expr_0”, followed by 3 bytes of the actual name, Foo. You can demangle that at the command line, although he adds this will probably go away in the GM.
% xcrun swift-demangle _TtC11lldb_expr_03Foo
_TtC11lldb_expr_03Foo ---> lldb_expr_0.Foo
Getting back to my typeName thing, keep in mind that it is bridging to ObjC instead of operating on a Swift string. Once you’ve gotten an ObjC class string, you can get the class back out from that, although I’m not sure how useful that is:
let classTest : AnyClass! = NSClassFromString("Hello".typeName)
As a final note, you can test for class using is.
let testArray : AnyObject[] = ["a", 1, 2.0]
var results : Bool[] = []
for t : AnyObject in testArray
{
results += t is String
}
results
2 Comments
Interesting to see your path to find a solution. I ended up going a different route and writing a class name parser that enables demangling at runtime: https://gist.github.com/jpsim/1b86d116808cb4e9bc30
A year+ later, Swift 2.0 beta has a mirror type which does a bit better – also there is a seemingly comprehensive list here:https://speakerdeck.com/jpsim/introspecting-swift