Swift: check if generic type conforms to protocol

GenericsSwift

Generics Problem Overview


I have a protocol that I defined like so:

protocol MyProtocol {
   ...
}

I also have a generic struct:

struct MyStruct <T>  {
    ...
}

Finally I have a generic function:

func myFunc <T> (s: MyStruct<T>) -> T? {
   ...
}

I'd like to test inside of the function if the type T conforms to MyProtocol. Essentially I'd like to be able to do (~ pseudocode):

let conforms = T.self is MyProtocol

But this throws a compiler error:

error: cannot downcast from 'T.Type' to non-@objc protocol type 'MyProtocol'
   let conforms = T.self is MyProtocol
                  ~~~~~~ ^  ~~~~~~~~~~

I have also tried variations, like T.self is MyProtocol.self, T is MyProtocol, and using == instead of is. So far I haven't gotten anywhere. Any ideas?

Generics Solutions


Solution 1 - Generics

I have to say @Alex want to check if T type conforms to protocol rather than s. And some answerer didn't see clearly.

Check T type conforms to protocol like this :

if let _ = T.self as? MyProtocol.Type {
    //  T conform MyProtocol
}

or

if T.self is MyProtocol.Type {
    //  T conform MyProtocol
}

Solution 2 - Generics

A bit late but you can test if something responds to protocol with as ? test:

if let currentVC = myViewController as? MyCustomProtocol {
    // currentVC responds to the MyCustomProtocol protocol =]
}

EDIT: a bit shorter:

if let _ = self as? MyProtocol {
    // match
}

And using a guard:

guard let _ = self as? MyProtocol else {
    // doesn't match
    return
}

Solution 3 - Generics

The simplest answer is: don’t do that. Use overloading and constraints instead, and determine everything up-front at compile-time rather than testing stuff dynamically at runtime. Runtime type checking and compile-time generics are like steak and ice-cream – both are nice but mixing them is a bit weird.

Consider something like this:

protocol MyProtocol { }

struct MyStruct <T>  { let val: T }

func myFunc<T: MyProtocol>(s: MyStruct<T>) -> T? {
    return s.val
}

func myFunc<T>(s: MyStruct<T>) -> T? {
    return nil
}

struct S1: MyProtocol { }
struct S2 { }

let m1 = MyStruct(val: S1())
let m2 = MyStruct(val: S2())

myFunc(m1) // returns an instance of S1
myFunc(m2) // returns nil, because S2 doesn't implement MyProtocol

The downside being, you can’t establish dynamically if T supports a protocol at runtime:

let o: Any = S1()
let m3 = MyStruct(val: o)
myFunc(m3)  // will return nil even though o 
            // does actually implement MyProtocol

But, in all honesty, do you really need to do that inside your generic function? If you’re unsure what actual type something is, the better option may be to figure that out up-front rather than deferring it to later and prodding it inside a generic function to find out.

Solution 4 - Generics

let conforms = T.self is MyProtocol.Type

Solution 5 - Generics

you can also leverage swift's switch case pattern matching, if you want to handle multiple cases of type T:

func myFunc<T>(s: MyStruct<T>) -> T? {
    switch s {
    case let sType as MyProtocol:
        // do MyProtocol specific stuff here, using sType
    default:
        //this does not conform to MyProtocol
    ...
    }
}

Solution 6 - Generics

You need declare protocol as @objc:

@objc protocol MyProtocol {
    ...
} 

From Apple's "The Swift Programming Language" book:

> You can check for protocol conformance only if your protocol is marked with the @objc attribute, as seen for the HasArea protocol above. This attribute indicates that the protocol should be exposed to Objective-C code and is described in Using Swift with Cocoa and Objective-C. Even if you are not interoperating with Objective-C, you need to mark your protocols with the @objc attribute if you want to be able to check for protocol conformance. > >Note also that @objc protocols can be adopted only by classes, and not by structures or enumerations. If you mark your protocol as @objc in order to check for conformance, you will be able to apply that protocol only to class types.

Solution 7 - Generics

For test cases I check conformance like this:

let conforms: Bool = (Controller.self as Any) is Protocol.Type

Solution 8 - Generics

A modern answer will be like this: (Swift 5.1)

func myFunc < T: MyProtocol> (s: MyStruct<T>) -> T? {    ... }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlexView Question on Stackoverflow
Solution 1 - GenericsmaquanneneView Answer on Stackoverflow
Solution 2 - GenericsjlngdtView Answer on Stackoverflow
Solution 3 - GenericsAirspeed VelocityView Answer on Stackoverflow
Solution 4 - GenericsCarlos ChaguendoView Answer on Stackoverflow
Solution 5 - GenericsCampbell_SoupedView Answer on Stackoverflow
Solution 6 - GenericsrabbitinspaceView Answer on Stackoverflow
Solution 7 - GenericsDaewizal ForshizalView Answer on Stackoverflow
Solution 8 - GenericsstevenView Answer on Stackoverflow