Check Whether Swift Object Is an Instance of a Given Metatype

Check whether Swift object is an instance of a given metatype

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:

struct AnyType {

let base: Any.Type
private let _canCast: (Any) -> Bool

/// Creates a new AnyType wrapper from a given metatype.
/// The passed metatype's value **must** match its static value,
/// i.e `T.self == base`.
init<T>(_ base: T.Type) {
precondition(T.self == base, """
The static value \(T.self) and dynamic value \(base) of the passed \
metatype do not match
""")

self.base = T.self
self._canCast = { $0 is T }
}

func canCast<T>(_ x: T) -> Bool {
return _canCast(x)
}
}

protocol P {}
class C : P {}
class D : C {}

let types = [
AnyType(P.self), AnyType(C.self), AnyType(D.self), AnyType(String.self)
]

for type in types {
print("C instance can be typed as \(type.base): \(type.canCast(C()))")
print("D instance can be typed as \(type.base): \(type.canCast(D()))")
}

// C instance can be typed as P: true
// D instance can be typed as P: true
// C instance can be typed as C: true
// D instance can be typed as C: true
// C instance can be typed as D: false
// D instance can be typed as D: true
// C instance can be typed as String: false
// D instance can be typed as String: false

The only limitation of this approach is that given we're performing the is check with T.self, we have to enforce that T.self == base. For example, we cannot accept AnyType(D.self as C.Type), as then T.self would be C.self while base would be D.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:

/// Returns `true` iff the given value can be typed as the given
/// **concrete** metatype value, `false` otherwise.
func canCast(_ x: Any, toConcreteType destType: Any.Type) -> Bool {
return sequence(
first: Mirror(reflecting: x), next: { $0.superclassMirror }
)
.contains { $0.subjectType == destType }
}

class C {}
class D : C {}

print(canCast(D(), toConcreteType: C.self)) // true
print(canCast(C(), toConcreteType: C.self)) // true
print(canCast(C(), toConcreteType: D.self)) // false
print(canCast(7, toConcreteType: Int.self)) // true
print(canCast(7, toConcreteType: String.self)) // false

We're using sequence(first:next:) to create a sequence of metatypes from the dynamic type of x 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 of T.self == base from our AnyType wrapper on by handling class metatypes separately:

struct AnyType {

let base: Any.Type
private let _canCast: (Any) -> Bool

/// Creates a new AnyType wrapper from a given metatype.
init<T>(_ base: T.Type) {

self.base = base

// handle class metatypes separately in order to allow T.self != base.
if base is AnyClass {
self._canCast = { x in
sequence(
first: Mirror(reflecting: x), next: { $0.superclassMirror }
)
.contains { $0.subjectType == base }
}
} else {
// sanity check – this should never be triggered,
// as we handle the case where base is a class metatype.
precondition(T.self == base, """
The static value \(T.self) and dynamic value \(base) of the passed \
metatype do not match
""")

self._canCast = { $0 is T }
}
}

func canCast<T>(_ x: T) -> Bool {
return _canCast(x)
}
}

print(AnyType(D.self as C.Type).canCast(D())) // true

The case where T.self is a class metatype should be the only case where T.self != base, as with protocols, when T is some protocol P, T.Type is P.Protocol, which is the type of the protocol itself. And currently, this type can only hold the value P.self.

How to check whether an object is kind of a dynamic class type in swift?

Personally, I think @JeremyP's suggestion to use Mirror is the best; though I would make a couple of tweaks to it:

/// Conditionally cast `x` to a given dynamic metatype value, taking into consideration
/// class inheritance hierarchies.
func conditionallyCast<T, U>(_ x: T, to destType: U.Type) -> U? {

if type(of: x) is AnyClass && destType is AnyClass { // class-to-class

let isCastable = sequence(
first: Mirror(reflecting: x), next: { $0.superclassMirror }
)
.contains { $0.subjectType == destType }

return isCastable ? (x as! U) : nil
}

// otherwise fall back to as?
return x as? U
}

Here we're using sequence(first:next:) to create a sequence of metatypes from the dynamic type of x through any superclass metatypes it might have (probably the first use of the function I've seen that doesn't look awful :P). In addition, we're falling back to doing an as? cast when we know we're not doing a class-to-class cast, which allows the function to also work with protocol metatypes.

Then you can simply say:

extension Sequence {
func ofType<T>(_ metatype: T.Type) -> [T] {
return flatMap { conditionallyCast($0, to: metatype) }
}
}

protocol P {}
class Animal {}
class Mammal: Animal {}
class Monkey: Mammal, P {}
class Pig: Mammal {}
class Human: Mammal, P {}

let animals = [Monkey(), Pig(), Human(), Mammal(), Animal()]

let animalType: Animal.Type = Mammal.self
print(animals.ofType(animalType)) // [Monkey, Pig, Human, Mammal]

print(animals.ofType(P.self)) // [Monkey, Human]

Another option, assuming you're on an Apple platform (i.e have access to the Objective-C runtime), is to use the the Objective-C metaclass method isSubclass(of:) in order to check if a given metatype is equal, or is a subclass of another:

import Foundation

/// Conditionally cast `x` to a given dynamic metatype value, taking into consideration
/// class inheritance hierarchies.
func conditionallyCast<T, U>(_ x: T, to destType: U.Type) -> U? {

let sourceType = type(of: x)

if let sourceType = sourceType as? AnyClass,
let destType = destType as? AnyClass { // class-to-class

return sourceType.isSubclass(of: destType) ? (x as! U) : nil
}

// otherwise fall back to as?
return x as? U
}

This works because on Apple platforms, Swift classes are built on top of Obj-C classes – and therefore the metatype of a Swift class is an Obj-C metaclass object.

Check if a given metatype is an enum

For the fun of it, as a (workaround) hack, we could instantiate an instance of T and perform runtime instrospection on it using Mirror, specifically its displayStyle property. Before we proceed, we note that we'll only use this for debugging purposes

Mirrors are used by playgrounds and the debugger.

I'll also point out that we're really chasing our tail here as we resort to runtime to query things known (by the compiler, at least) at compile time.


Anyway, first of all, I'll rename enumCaseCount(...) to isEnum(...), as this question only covers querying whether a metatype is an enum or not. For similar (somewhat brittle) hacks to query the number of cases of a given enum, see:

  • How do I get the count of a Swift enum?

Now, the generic placeholder T in isEnum(...) only knows that it is a type conforming to Hashable, which doesn't give us any straight-forward way to instantiate an instance of T (if Hashable blueprinted, say, an initializer init(), we'd could readily construct an instance of T an perform runtime introspection upon it). Instead, we'll resort to manually allocating raw memory for a single T instance (UnsafeMutableRawPointer.allocate(bytes:alignedTo:)), binding it to T (bindMemory(to:capacity:)), and finally deallocating the memory (deallocate(bytes:alignedTo:)) once we've finished our runtime introspection of the instance referenced to by the pointer to the bound memory. As for the runtime introspection, we simply use Mirror to check whether its displayStyle is enum or not.

func isEnum<T: Hashable>(_: T.Type) -> Bool {
var result = false
// Allocate memory with size and alignment matching T.
let bytesPointer = UnsafeMutableRawPointer.allocate(
bytes: MemoryLayout<T>.size,
alignedTo: MemoryLayout<T>.alignment)
// Bind memory to T and perform introspection on the instance
// reference to by the bound memory.
if case .some(.`enum`) = Mirror(reflecting:
bytesPointer.bindMemory(to: T.self, capacity: 1).pointee)
.displayStyle {
print("Is an enum")
result = true
} else { print("Is not an enum") }
// Deallocate the manually allocate memory.
bytesPointer.deallocate(bytes: MemoryLayout<T>.size,
alignedTo: MemoryLayout<T>.alignment)
return result
}

Example usage:

enum SimpleEnum { case a, b, c }

enum SimpleStrEnum: String {
case a = "A"
case b = "B"
case c = "C"
}

enum SimpleExplicitIntEnum: Int { case a, b, c }

struct SimpleStruct: Hashable {
let i: Int
// Hashable
var hashValue: Int { return 0 }
static func ==(lhs: SimpleStruct, rhs: SimpleStruct) -> Bool { return true }
}

print(isEnum(SimpleEnum.self)) // true
print(isEnum(SimpleStrEnum.self)) // true
print(isEnum(SimpleExplicitIntEnum.self)) // true
print(isEnum(SimpleStruct.self)) // false

Can you check if a Type (not an instance) is a subclass of another Type?

To expand upon Martin R's great answer you can make an array extension like the following:

extension Array {
func first<T>(ofType: T.Type) -> T.Type? {
return first { $0 is T.Type } as? T.Type
}
func first<T>(ofExactType type: T.Type) -> T.Type? {
return first { $0 as? Any.Type == type } as? T.Type
}
}

class Vehicle {}
class Car : Vehicle {}
class Honda: Car {}

let carTypes = [Honda.self, Vehicle.self, Car.self] // Inferred type [Vehicle]
print(carTypes.first(ofType: Car.self) ?? "n/a") // prints Honda
print(carTypes.first(ofExactType: Car.self) ?? "n/a") // prints Car

Also, just FYI, $0 as? Any.Type == type is the same as doing $0 as? Any.Type == T.self. Either one would work.

Checking if Any.Type conforms to a protocol in Swift

To check the pure Swift code, you can do:

protocol MySwiftProtocol: AnyObject {
}

class MySwiftClass: MySwiftProtocol {
}

if MySwiftClass.self as? MySwiftProtocol.Type != nil {
print("conforms")
} else {
print("does not conform")
}

or more simply:

if MySwiftClass.self is MySwiftProtocol.Type {

Cast to a Metatype Type in Swift?

First of all, as takes a type, not an expression, on the right-hand side. So what you have is a syntax error.

What you seem to be trying to do is "cast" to a type that is computed at runtime. What would that even mean? Let's first consider what is a "cast".

Usually, when we have a cast expression x as T, it has two components:

  • At compile-time: The entire cast expression x as T has compile-time type T?, which allows you to do stuff with the resulting expression that you maybe cannot do on x directly. In other words, it allows you to change the compile-time type.
  • At runtime: It checks whether the runtime type of x is a subtype of T, and if it is, it evaluates to the optional containing that value, otherwise, it evaluates to nil.

If the type T is not known at compile-time, then obviously you cannot do the compile-time part of it. (The compile-time type of the resulting expression cannot, obviously, depend on something which is not known at compile-time.)

The other part, the runtime component, could that be done with a type computed at runtime? Sure. For example,

import Foundation
let afterCast : Super? =
(superA as AnyObject).isKindOfClass(subType) ? superA : nil

It's not clear if that is what you want.

what is T.Type in swift

  • T.Type is used in parameters and constraints to mean "the type of the thing itself, not an instance of the thing".

    For example:

    class Example {
    static var staticVar: String { return "Foo" }
    var instanceVar: String { return "Bar" }
    }

    func printVar(from example: Example) {
    print(example.instanceVar) // "Bar"
    print(example.staticVar) // Doesn't compile, _Instances_ of Example don't have the property "staticVar"
    }

    func printVar(from example: Example.Type) {
    print(example.instanceVar) // Doesn't compile, the _Type_ Example doesn't have the property "instanceVar"
    print(example.staticVar) // prints "Foo"
    }
  • You get a reference to a Type's .Type (the Type object itself) at runtime by calling TheType.self. The syntax TheType.Type is used in type declarations and type signatures only to indicate to the compiler the instance vs. type distinction. You can't actually get a reference to, for example, Int's type at runtime or in your function implementations by calling Int.Type. You would call Int.self

  • In the example code var someValue: Int, the specific notation identifier: Type (in this case, someValue: Int) means that someValue will be an instance of Int. If you wanted someValue to be a reference to the actual type Int, you would write var someValue: Int.Type = Int.self Remember that the .Type notation is only used when declaring types and type signatures to the compiler, and the .self property is used in actual code to retrieve a reference to the type object itself at execution time.

  • The reason why JSONDecoder().decode requires a parameter of T.Type (where T conforms to Decodable) is because any type conforming to Decodable has an initializer init(from decoder: Decoder). The decode method will need to call this init method on a type that conforms to Decodable, not on an instance of a type that conforms to Decodable. For example:

    var someString: String = ""
    someString.init(describing: 5) // Not possible, doesn't compile. Can't call an initializer on an _instance_ of String
    var someStringType: String.Type = String.self
    someStringType.init(describing: 5) // Iniitializes a String instance "5"


Related Topics



Leave a reply



Submit