Checking If a Swift Class Conforms to a Protocol and Implements an Optional Function

Checking if a Swift class conforms to a protocol and implements an optional function?

You are still thinking in Objective-C, embrace Swift!

Assuming that your protocol looks like this:

@objc protocol SKPhysicsContactDelegate {
optional func didBeginContact()
}

Try this:

if let delegate = gameScene as? SKPhysicsContactDelegate {
delegate.didBeginContact?()
}

Or a one liner:

(gameScene as? SKPhysicsContactDelegate)?.didBeginContact?()

Notice the ? after the method name in the call? It's because that method is optional and it won't get called if the object doesn't implement that method. And the if let branch won't get executed if the object doesn't conforms to SKPhysicsContactDeletegate protocol.


Check method existence without call

To check the existence of the method itself before calling, just omit the method call to get a reference to that methodand check it like any other variable:

if let method = (gameScene as? SKPhysicsContactDelegate)?.didBeginContact {
print("gameScene conforms to SKPhysicsContactDelegate and implements didBeginContact")
// Call it later whenever you want
method()
}

If you don't need to call it later, just check for nil:

if (gameScene as? SKPhysicsContactDelegate)?.didBeginContact != nil {
print("gameScene conforms to SKPhysicsContactDelegate and implements didBeginContact")
}

Check for static methods

Checking for optional static methods uses the same approach, but requires the class object instead of an instance of the class:

if (GameScene.self as? OptionalProtocol.Type)?.staticMethod != nil {
print("gameScene conforms to OptionalProtocol and implements staticMethod")
}

Notice GameScene.self for obtaining the object type and <protocol>.Type to cast to the protocol class instead of a protocol instance.


Full sample

Attached full sample for Playgrounds, Swift script or any online Swift compiler:

import Foundation

@objc protocol OptionalProtocol {
optional func instanceMethod()
optional static func staticMethod()
}

class Nothing {}
class Something: OptionalProtocol {}
class Bar: NSObject, OptionalProtocol {
func instanceMethod() {
print("Instance method")
}
}
class Foo: NSObject, OptionalProtocol {
static func staticMethod() {
print("Static method")
}
}

// Cast instances to 'Any' and classes to 'AnyClass'
let nothing: Any = Nothing()
let nothingClass: AnyClass = Nothing.self
let something: Any = Something()
let somethingClass: AnyClass = Something.self
let bar: Any = Bar()
let barClass: AnyClass = Bar.self
let foo: Any = Foo()
let fooClass: AnyClass = Foo.self

nothing is OptionalProtocol // false
(nothing as? OptionalProtocol)?.instanceMethod != nil // false
(nothing as? OptionalProtocol)?.instanceMethod?() // Does nothing
(nothingClass as? OptionalProtocol.Type)?.staticMethod != nil // false
(nothingClass as? OptionalProtocol.Type)?.staticMethod?() != nil // Does nothing

something is OptionalProtocol // true
(something as? OptionalProtocol)?.instanceMethod != nil // false
(something as? OptionalProtocol)?.instanceMethod?() // Does nothing
(somethingClass as? OptionalProtocol.Type)?.staticMethod != nil // false
(somethingClass as? OptionalProtocol.Type)?.staticMethod?() != nil // Does nothing

bar is OptionalProtocol // true
(bar as? OptionalProtocol)?.instanceMethod != nil // true
(bar as? OptionalProtocol)?.instanceMethod?() // Prints 'Instance method'
(barClass as? OptionalProtocol.Type)?.staticMethod != nil // false
(barClass as? OptionalProtocol.Type)?.staticMethod?() != nil // Does nothing

foo is OptionalProtocol // true
(foo as? OptionalProtocol)?.instanceMethod != nil // false
(foo as? OptionalProtocol)?.instanceMethod?() // Does nothing
(fooClass as? OptionalProtocol.Type)?.staticMethod != nil // true
(fooClass as? OptionalProtocol.Type)?.staticMethod?() != nil // Prints 'Static method'

In Swift, how can you test if an object implements an optional protocol method which differs by signature without actually calling that method?

As also shown in How do I resolve "ambiguous use of" compile error with Swift #selector syntax?, you can explicitly coerce a function reference to its expected type in order to resolve such ambiguities.

The only difference being, as such function references are to @optional protocol requirements done through optional chaining, you need to coerce to the optional type of the function. From there, you can do a comparison with nil in order to determine if both the delegate is non-nil, and it implements the given requirement.

For example:

import Foundation

@objc public protocol TestDelegate : AnyObject {
@objc optional func test()

// Need to ensure the requirements have different selectors.
@objc(testWithString:) optional func test(with string: String)
@objc(testWithInt:) optional func test(with int: Int)
}

class C : TestDelegate {
func test() {}
func test(with someString: String) {}
func test(with someInt: Int) {}
}

var delegate: TestDelegate? = C()

if delegate?.test as (() -> Void)? != nil {
print("supports 'test'")
}

if delegate?.test(with:) as ((String) -> Void)? != nil {
print("supports 'test w/ String'")
}

if delegate?.test(with:) as ((Int) -> Void)? != nil {
print("supports 'test w/ Int'")
}

// supports 'test'
// supports 'test w/ String'
// supports 'test w/ Int'

Note that I've given the test(with:) requirements unique selectors in order to ensure they don't conflict (this doesn't affect the disambiguation, only allowing class C to conform to TestDelegate).

Check if class conforms to protocol

If you import obj-c then you can do something like you used to.

Otherwise, it's hard because protocols don't exist in the same way. Consider a registration based system for your factory. Each of your classes would register themselves by supplying a function or closure that can be called to return a new instance of that class, and the registration is against a string or some other type of identifier. This is where it would be good to have a protocol type, but in obj-c you were really doing the same thing with a string conversion. You could register against anything that is Equatable to keep things very generic.

Check if optional protocol method is implemented in Swift?

Per The Swift Programming Language:

You check for an implementation of an optional requirement by writing
a question mark after the name of the requirement when it is called,
such as someOptionalMethod?(someArgument). Optional property
requirements, and optional method requirements that return a value,
will always return an optional value of the appropriate type when they
are accessed or called, to reflect the fact that the optional
requirement may not have been implemented.

So the intention is not that you check whether the method is implemented, it's that you attempt to call it regardless and get an optional back.

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 {

iOS11 Swift 4 - how to check if Swift class conforms to protocol defined in Objective-C?

[MyClass conformsToProtocol:@protocol(MyProtocol)];

According to Apple Docs you can use conformsToProtocol:which returns a Boolean value that indicates whether the receiver conforms to a given protocol.


Example

@protocol MyProtocol
- (void)helloWorld;
@end

@interface MyClass : NSObject <MyProtocol>
@end

Will be exposed as:

console.log(MyClass.conformsToProtocol(MyProtocol)); 

var instance = MyClass.alloc().init();
console.log(instance.conformsToProtocol(MyProtocol))

How to define optional methods in Swift protocol?

1. Using default implementations (preferred).

protocol MyProtocol {
func doSomething()
}

extension MyProtocol {
func doSomething() {
/* return a default value or just leave empty */
}
}

struct MyStruct: MyProtocol {
/* no compile error */
}

Advantages

  • No Objective-C runtime is involved (well, no explicitly at least). This means you can conform structs, enums and non-NSObject classes to it. Also, this means you can take advantage of powerful generics system.

  • You can always be sure that all requirements are met when encountering types that conform to such protocol. It's always either concrete implementation or default one. This is how "interfaces" or "contracts" behave in other languages.

Disadvantages

  • For non-Void requirements, you need to have a reasonable default value, which is not always possible. However, when you encounter this problem, it means that either such requirement should really have no default implementation, or that your you made a mistake during API design.

  • You can't distinguish between a default implementation and no implementation at all, at least without addressing that problem with special return values. Consider the following example:

    protocol SomeParserDelegate {
    func validate(value: Any) -> Bool
    }

    If you provide a default implementation which just returns true — it's fine at the first glance. Now, consider the following pseudo code:

    final class SomeParser {
    func parse(data: Data) -> [Any] {
    if /* delegate.validate(value:) is not implemented */ {
    /* parse very fast without validating */
    } else {
    /* parse and validate every value */
    }
    }
    }

    There's no way to implement such an optimization — you can't know if your delegate implements a method or not.

    Although there's a number of different ways to overcome this problem (using optional closures, different delegate objects for different operations to name a few), that example presents the problem clearly.


2. Using @objc optional.

@objc protocol MyProtocol {
@objc optional func doSomething()
}

class MyClass: NSObject, MyProtocol {
/* no compile error */
}

Advantages

  • No default implementation is needed. You just declare an optional method or a variable and you're ready to go.

Disadvantages

  • It severely limits your protocol's capabilities by requiring all conforming types to be Objective-C compatible. This means, only classes that inherit from NSObject can conform to such protocol. No structs, no enums, no associated types.

  • You must always check if an optional method is implemented by either optionally calling or checking if the conforming type implements it. This might introduce a lot of boilerplate if you're calling optional methods often.



Related Topics



Leave a reply



Submit