What is Metatype Type in Swift 5?

Junsu Kim
4 min readNov 21, 2021

--

What is Metatype type in Swift?

You’ve heard of what metatype types are, but you don’t really know what they are. I think it’s due to its weirdly named name “metatype”. Becuase in fact, it’s not the actual name of the type you use in your code. There is no such let myType: metatype = metatype() kind of code in Swift.

So let’s begin. What is a metatype type? To understand what metatype types are, you need a little context first.

Let’s say you want to compare two types. So if the two types are equal, you want to run some code. The first approach will be this:

if SomeClass == AnotherClass { ... } else { ... }

But as soon as you type that code in your Xcode, you will see a compile time error. And the error message will read something like this:

Expected member name or constructor call after type name

Why do you get this error message? Firstly, the == static function defined in Equatable protocol accepts two instances of type Self, so you can’t provide a type as an argument which is not an instance. Secondly, you need to think about the paradigm of Swift. Swift language is an Object Oriented Programming language (some say it’s a Protocol Oriented Programming language but that’s not the point here). You deal with objects and not the types of objects because code reusability is important in Swift language.

So if you want to work with just types of objects, you need some kind of way to wrap those types into an object so you can start manipulating them in your code. And that wrapper is the metatype type. Basically what the metatype type does is it makes your type a value.

Let’s see how to declare a value of type metatype. All you have to do is append .self to your type name and the system will create a value of type metatype for you.

let valueNotType: SomeClass.Type = SomeClass.self

One thing to note is that the explicit type is SomeClass.Type (if the wrapper was applied to protocols, the explicit type would be SomeProtocol.Protocol). That is the type of the wrapper, the metatype type. If you delete the .Type part in the above code, then valueNotType becomes an instance of type SomeClass.

let valueNotType: SomeClass = SomeClass() 
// this is just a regular instance.

Take caution that values of metatype type are not the same as instances of your type. For example, if this was the declaration of SomeClass

class SomeClass {
class func printHi() { print("Hi") }
func printHello() { print("Hello") }
}

You will be able to call printHi() on valueNotType, but won’t be able to call printHello(). The compiler will ask you to provide an instance of type SomeClass to printHello() because the method printHello() is indeed an instance method. In a similar way, you can call initializer function .init() on valueNotType, but you can’t call .init() on an instance of SomeClass type. .init() is a class function.

There is also a very useful and handy function named type(of:) which takes an instance of your type and returns a value of metatype type. So you can immediately do something like this:

let someClass = SomeClass()
if type(of:someClass) == SomeClass.self { print("the same") }
else { print("not the same") }

However, the type(of:) function typically is not used for the context above. Typically it is used to figure out the type of your instance at runtime.

class BaseClass { 
class func printName() { print("BaseClass") }
}
class SomeClass: BaseClass {
override class func printName() { print("SomeClass") }
}
let someClass: BaseClass = SomeClass()
type(of:someClass).printName() // this will print "SomeClass" to the console.

When the properties are initialized, someClass has BaseClass type. But at runtime, it has SomeClass type, so calling printName() on type(of:someClass) which returns a value of metatype type will print “SomeClass” to the console.

A real life example of metatype type is register(_:forCellReuseIdentifier:) method in UITableView class. The first parameter is of type AnyObject.Type which we learned above is a metatype type. So you provide a value of metatype type as an argument to the method:

myTableView.register(MyCustomCell.self, forCellReuseIdentifier: "MyCustomCell")

Lastly going back to our initial question, to compare two types for equality you want to compare their values of metatype type.

if SomeClass.self == AnotherClass.self { print("same types") }
else { print("not the same types") }

Thanks for reading! If you have any feedback or questions about the article, please let me know in the comments.

--

--