Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

swift

I'm writing a helper function in Swift for use in SpriteKit games that will check if the collision detention has been set up correctly.

I want to check that my GameScene implements the protocol SKPhysicsContactDeletegate and also contains the didBeginContact function.

I have the following code, written with the help of Xcode's auto-complete:

    if GameScene(conformsToProtocol(SKPhysicsContactDelegate)) {
        if GameScene(respondsToSelector(didBeginContact(SKPhysicsContact))) {
            print("Self implements SKPhysicsContactDelegate and didBeginContact appears to be present")
        }
    }

Xcode then complains about my conformsToProtocol, complaining that

'Argument labels '(_:)' do not match any available overloads'

with no suggestion how to fix. It also objects to respondsToSelector, stating that

'Cannot convert value of type '(SKPhysicsContact).Type' (aka 'SKPhysicsContact.Type') to expected argument type 'SKPhysicsContact'

How can I check if my GameScene conforms to this protocol and also implements this function?

Edit: Here's my code based upon the answers:

if GameScene.self is SKPhysicsContactDelegate {
    print("Yes it's a delegate")
}

Output: Yes it's a delegate

let yy = (GameScene.self as? SKPhysicsContactDelegate)?.didBeginContact
print("yy is \(yy)")

Output: yy is nil

if (GameScene.self as? SKPhysicsContactDelegate)?.didBeginContact != nil {
    print("Yes it's a delegate and the function is there")
}

No output.

like image 620
Steve Ives Avatar asked Apr 07 '16 11:04

Steve Ives


People also ask

How can an optional method be defined in Swift protocols?

In order to declare optional protocols that work well with swift, declare the functions as variables instead of func's. Save this answer.

How do you make a function optional in Swift?

Swift protocols on their side do not allow optional methods. But if you are making an app for macOS, iOS, tvOS or watchOS you can add the @objc keyword at the beginning of the implementation of your protocol and add @objc follow by optional keyword before each methods you want to be optional.

What's the difference between a protocol and a class in Swift?

You can create objects from classes, whereas protocols are just type definitions. Try to think of protocols as being abstract definitions, whereas classes and structs are real things you can create.

Can a struct conform to a protocol Swift?

In Swift, protocols contain multiple abstract members. Classes, structs and enums can conform to multiple protocols and the conformance relationship can be established retroactively.


1 Answers

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'
like image 156
redent84 Avatar answered Oct 21 '22 08:10

redent84