I need to keep a collection of Swift metatypes and write a function which will check if a given object is an instance of one of them. I can do that easily in Java:
Class c = x.getClass();
c.isInstance(someObj)
However, I have no idea how to do that in Swift:
var isInt = 7 is Int.Type // compiles
let x = Int.self
var isInt = 7 is x // compiler error - Use of undeclared type 'x'
Is this even possible to be done in Swift?
Unfortunately, you can currently only use a named type with the
is
operator, you cannot yet use an arbitrary metatype value with it (although really IMO you ought to be able to).Assuming you have control over the creation of the metatypes that you want to compare against, one solution that achieves the same result would be to create a wrapper type with an initialiser that stores a closure that performs the
is
check against a generic placeholder:The only limitation of this approach is that given we're performing the
is
check withT.self
, we have to enforce thatT.self == base
. For example, we cannot acceptAnyType(D.self as C.Type)
, as thenT.self
would beC.self
whilebase
would beD.self
.However this shouldn't be a problem in your case, as we're just constructing
AnyType
from metatypes that are known at compile time.If however you don't have control over the creation of the metatypes (i.e you get handed them from an API), then you're quite a bit more limited with what you can do with them.
As @adev says, you can use
type(of:)
in order to get the dynamic metatype of a given instance, and the==
operator to determine if two metatypes are equivalent. However, one problem with this approach is that it disregards both class hierarchies and protocols, as a subtype metatypes will not compare equal with a supertype metatypes.One solution in the case of classes is to use
Mirror
, as also shown in this Q&A:We're using
sequence(first:next:)
to create a sequence of metatypes from the dynamic type ofx
through any superclass metatypes it might have.However this method still won't work with protocols. Hopefully a future version of the language will provide much richer reflection APIs that allow you to compare the relationship between two metatype values.
However, given the above knowledge of being able to use
Mirror
, we can use it to lift the aforementioned restriction ofT.self == base
from ourAnyType
wrapper on by handling class metatypes separately:The case where
T.self
is a class metatype should be the only case whereT.self != base
, as with protocols, whenT
is some protocolP
,T.Type
isP.Protocol
, which is the type of the protocol itself. And currently, this type can only hold the valueP.self
.