How to define optional methods in Swift protocol?

197,345

Solution 1

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.

Solution 2

In Swift 2 and onwards it's possible to add default implementations of a protocol. This creates a new way of optional methods in protocols.

protocol MyProtocol {
    func doSomethingNonOptionalMethod()
    func doSomethingOptionalMethod()
}

extension MyProtocol {
    func doSomethingOptionalMethod(){ 
        // leaving this empty 
    }
}

It's not a really nice way in creating optional protocol methods, but gives you the possibility to use structs in in protocol callbacks.

I wrote a small summary here: https://www.avanderlee.com/swift-2-0/optional-protocol-methods/

Solution 3

Here is a concrete example with the delegation pattern.

Setup your Protocol:

@objc protocol MyProtocol:class
{
    func requiredMethod()
    optional func optionalMethod()
}

class MyClass: NSObject
{
    weak var delegate:MyProtocol?

    func callDelegate()
    {
        delegate?.requiredMethod()
        delegate?.optionalMethod?()
    }
}

Set the delegate to a class and implement the Protocol. See that the optional method does not need to be implemented.

class AnotherClass: NSObject, MyProtocol
{
    init()
    {
        super.init()

        let myInstance = MyClass()
        myInstance.delegate = self
    }

    func requiredMethod()
    {
    }
}

One important thing is that the optional method is optional and needs a "?" when calling. Mention the second question mark.

delegate?.optionalMethod?()

Solution 4

Since there are some answers about how to use optional modifier and @objc attribute to define optional requirement protocol, I will give a sample about how to use protocol extensions define optional protocol.

Below code is Swift 3.*.

/// Protocol has empty default implementation of the following methods making them optional to implement:
/// `cancel()`
protocol Cancelable {

    /// default implementation is empty.
    func cancel()
}

extension Cancelable {

    func cancel() {}
}

class Plane: Cancelable {
  //Since cancel() have default implementation, that is optional to class Plane
}

let plane = Plane()
plane.cancel()
// Print out *United Airlines can't cancelable*

Please notice protocol extension methods can't invoked by Objective-C code, and worse is Swift team won't fix it. https://bugs.swift.org/browse/SR-492

Solution 5

In Swift 3.0

@objc protocol CounterDataSource {
    @objc optional func increment(forCount count: Int) -> Int
    @objc optional var fixedIncrement: Int { get }
}

It will save your time.

Share:
197,345
Selvin
Author by

Selvin

iOS Application developer

Updated on July 08, 2022

Comments

  • Selvin
    Selvin almost 2 years

    Is it possible in Swift? If not then is there a workaround to do it?

  • Selvin
    Selvin about 10 years
    Compiler Error: 'optional' attribute can only be applied to members of an @objc protocol.
  • George Green
    George Green almost 10 years
    With this solution you can't access self from within any of the protocol functions. This may cause issues in some cases!
  • Zag
    Zag almost 10 years
    @GeorgeGreen You can access self. Mark the function variable as lazy and use a capture list inside the closure.
  • khunshan
    khunshan over 9 years
    Only classes, protocols, methods and properties can use @objc. In case you are using an Enum parameter in the @ objc protocol method defination, you are doomed.
  • Unheilig
    Unheilig over 9 years
    Can you describe a little more the "two levels of optional at play" part, namely this: delegate?.indexDidChange?(index!)?
  • Blessing Lopes
    Blessing Lopes over 9 years
    If we had written the protocol to have a non optional method like this: protocol CollectionOfDataDelegate{ func indexDidChange(index: Int) } then you would call it without the question mark: delegate?.indexDidChange(index!) When you set a optional requirement for a method in a protocol, the Type that will conform to it might NOT implement that method, so the ? is used to check for the implementation and If there is none the program will not crash. @Unheilig
  • Ali Beadle
    Ali Beadle about 9 years
    Minor correction (too small to edit!) but it should be 'optional' not '@optional'
  • Alfie Hanssen
    Alfie Hanssen about 9 years
    weak var delegate : CollectionOfDataDelegate? (ensure weak reference?)
  • Johnathon Sullinger
    Johnathon Sullinger almost 9 years
    @BlessingLopes Can you add your explanation of the delegate? usage to your answer? That information should really belong in there for others in the future. I want to upvote this, but that information should really be in the answer.
  • Johnathon Sullinger
    Johnathon Sullinger almost 9 years
    This also requires you to mark your class implementing the protocol as @objc, not just the protocol.
  • Johnathon Sullinger
    Johnathon Sullinger almost 9 years
    @optional isn't even the correct keyword. It's optional, and you must declare the class and protocol wit the @objc attribute.
  • Zag
    Zag almost 9 years
    @khunshan, this method requires nothing being marked @ objc, what are you referring to?
  • khunshan
    khunshan almost 9 years
    its an information on the topic that enums cannot be used in between swift and objc which the for other statements can be bridged with @objc keyword.
  • Entalpi
    Entalpi almost 9 years
    This is probably the cleanest way to do it in Swift. Too bad it does not work prior to Swift 2.0.
  • Ian Pearce
    Ian Pearce over 8 years
    @MattQuiros I'm finding that you do in fact need to declare the function in the protocol definition, otherwise the no-op extension function doesn't get overridden in your classes that conform to the protocol.
  • matthias
    matthias over 8 years
    @IanPearce is correct, and this appears to be by design. In the "Protocol Oriented Programming" talk (408) at WWDC, they talk about methods in the main protocol being "customization points" offered to conforming types. A required point of customization doesn't receive a definition in an extension; an optional point does. Methods on the protocol that generally should not be customized are wholly declared/defined in the extension to disallow conforming types to customize unless you specifically cast down to its dynamicType to show you want the conformer's custom implementation.
  • farhadf
    farhadf over 8 years
    Good writeup of the subtlety in dynamic dispatch to protocol or implementing class here: nomothetis.svbtle.com/…
  • Darko
    Darko about 8 years
    But in this way you can not check if the method is implemented or not (it always is, but empty). The delegate source in real life checks if the delegate implements the selector or not and then according to the result does different things.
  • Franklin Yu
    Franklin Yu about 8 years
    @Darko, in that case I think you can enable the optional method to throw MethodNotImplementedError or something similar (as in Java).
  • Darko
    Darko about 8 years
    @FranklinYu You can do this but then you are poluting your API design with 'throws' where it's in fact not needed. I like more the idea of "micro protocols". E.g. each method confirms to one protocol and then you can check for: if object is protocol
  • Echelon
    Echelon almost 8 years
    This should be the correct answer. Simple, clean and explanatory.
  • mfaani
    mfaani over 7 years
    So in other words you provide its implementation in the extension through defaulting it. However since the implementation is empty, ie it has no code, you can think of it as an optional method?
  • DevAndArtist
    DevAndArtist over 7 years
    Any source of why @objc is needed on all members now?
  • Sentry.co
    Sentry.co over 7 years
    @Gautam Sareriya: What do you think is best this approach or creating empty extension methods?
  • Vadim Eisenberg
    Vadim Eisenberg over 7 years
    @Antoine I think you should make the function in the extension public, since the functions in the protocols are public by definition. Your solution will not work when the protocol will be used outside of its module.
  • Ben
    Ben about 7 years
    I tried with your methods with required flag, but with errors: required may only be used on 'init' declarations.
  • akashivskyy
    akashivskyy about 7 years
    Because "optional requirement" (which is an oxymoron itself) goes against the idea of contract + prevents static dispatch. One of the Swift Core Team members (I don't remember who that was exactly) said that Swift only has "optional protocol requirements" because it is needed for Obj-C interop. Also, the idea of native "optional requirements" was quickly discarded in the early days of swift-evolution.
  • Lukas
    Lukas almost 7 years
    Good job! This should be the correct answer since it solves the problem without getting Objective-C runtime involved.
  • Eric
    Eric almost 6 years
    This. It's adherence to the "I" principle in the SOLID software development principles.
  • Roshana Pitigala
    Roshana Pitigala almost 6 years
    While this may answer the question it's better to add some description on how this answer may help to solve the issue. Please read How do I write a good answer to know more.
  • Mec Os
    Mec Os about 5 years
    from swift documentation - "Protocol requirements with default implementations provided by extensions are distinct from optional protocol requirements. Although conforming types don’t have to provide their own implementation of either, requirements with default implementations can be called without optional chaining."
  • tontonCD
    tontonCD about 5 years
    For the first error you mentionned, you need to make the protocol as a subclass of NSObject: @objc protocol Health: AnyObject {...