How to Define Optional Methods in Swift Protocol

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.

Optional Protocol methods in swift without using @objc

You can define default func implementation by:

protocol Opt {
func requiredFunc()
func optionalFunc()
}

extension Opt {
func optionalFunc() {}
}

With this you don't have to implement optionalFunc() in classes conforming to Opt, because they already have their default implementation.

How to define optional methods in a Swift protocol with Swift parameters?

The problem is this:

class HSCoolPerson {
// ...
}

As the error message plainly tells you, that type, the class HSCoolPerson, is completely invisible to Objective-C. And a protocol optional method is an Objective-C language feature; Swift merely borrows it, as it were. (That's why you have to say @objc protocol to get this feature.) So any time you want to define a protocol optional method, you have to do it in a way that Objective-C can understand, because it is Objective-C that is going to do the work for you.

To expose this class to Objective-C, simply derive it from NSObject:

class HSCoolPerson : NSObject {
// ...
}

Problem solved.

Swift Protocol Optional conformance via Non-Optional

If the protocol provides a default implementation that returns an optional:

protocol SomeProtocol {
var foo: Int? { get }
}

extension SomeProtocol {
var foo: Int? { return nil }
}

protocol-conforming types can then provide an overriding non-optional version of the variable/function:

struct StructB: SomeProtocol {
let foo: Int
}

I found this discussed on the Swift Evolution forum:

At the first glance I thought there is a rule that allows us to satisfy protocol requirements with non-optional types, but this resulted in an error. Only after further investigation I noticed that a default implementation must exist in order to 'kind of override' the requirement with a non-optional version.

https://forums.swift.org/t/how-does-this-rule-work-in-regard-of-ambiguity/19448

This Swift team also discusses allowing non-optional types to satisfy optional-value protocols:

Would it make any sense to allow protocol requirement satisfaction with non-optional types, like with failable init's? (Probably with some implicit optional promotion.)

Yep, totally! Except for the part where this changes the behavior of existing code, so we'd have to be very careful about it. This is considered part of [SR-522] Protocol funcs cannot have covariant returns

which is tracked on Stack Overflow here:

Why can't a get-only property requirement in a protocol be satisfied by a property which conforms?

Accessing optional method in protocol returns scope error

class MyClass: NSObject, MyProtocol { ... }

This says that MyClass implements MyProtocol, which allows, but does not require myFunction. The compiler can clearly see that MyClass does not implement myFunction, so self.myFunction() isn't a thing. It could be a thing, but it's clearly not a thing. The protocol doesn't play into this.

You could "work around" this by saying "I don't mean MyClass; I mean MyProtocol":

func doSomething() {
(self as MyProtocol).myFunction?()
}

But this is kind of silly IMO. self doesn't implement myFunction and this is known at compile time.

If the point is to implement this via subclassing, the correct way to do that is to just implement it as empty in the superclass:

class MyClass: NSObject, MyProtocol {
func myFunction() {}

func doSomething() {
myFunction()
}
}

This is precisely equivalent to "do nothing" while allowing overrides in subclasses.

How to add default implementation using generic protocol extension?

Maybe this would be simpler.

extension Comparable {
func limit(from minValue: Self, to maxValue: Self) -> Self {
return Swift.max(Swift.min(self, maxValue), minValue)
}
}

optional delegates with struct types - swift

I think the error messages you posted are self explanatory, Struct is not available in Objective-C runtime so when you annotate a protocol with @objc compiler gives you warning that struct can't be passed as an argument to such protocol.

How to achieve optional behaviour in pure swift?
Officially there is no equivalent of objective-C optional in swift. But empty default extensions will help you achieve the same behavior.

protocol PopupDelegate {
func popupItemSelected(item : PopupItem, identifier : String)
func popupItemMultipleSelected(item : [PopupItem], identifier : String)
}

extension PopupDelegate {
func popupItemSelected(item : PopupItem, identifier : String) { }
func popupItemMultipleSelected(item : [PopupItem], identifier : String) { }
}

Now whoever confirms to PopupDelegate need not implement methods as default implementation is already provided and because its empty implementation it's almost same as optional.

One caveat of this approach would be though, if you call respondsToSelector this will return true as there exists a default implementation but with optional you would get appropriate response.

Question about Delegate / Protocol requirements

Your code snippet is not quite how UITextFieldDelegate protocol is defined. Two observations:

  • The text field delegate protocol does not include a delegate property.

    Yes, the text field has a delegate property:

    @available(iOS 2.0, *)
    open class UITextField : UIControl, UITextInput, NSCoding, UIContentSizeCategoryAdjusting {

    ...

    weak open var delegate: UITextFieldDelegate? // default is nil. weak reference

    ...
    }

    But the delegate protocol has no requirement for a delegate property in the view controller (or whatever you specify as the delegate).

  • The methods are optional.

    The actual definition is as follows (found by pressing shift-command-o or “File” » “Open Quickly...”, making sure the Swift button is selected, and then searching for UITextFieldDelegate):

    public protocol UITextFieldDelegate : NSObjectProtocol {

    @available(iOS 2.0, *)
    optional func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool // return NO to disallow editing.

    @available(iOS 2.0, *)
    optional func textFieldDidBeginEditing(_ textField: UITextField) // became first responder

    @available(iOS 2.0, *)
    optional func textFieldShouldEndEditing(_ textField: UITextField) -> Bool // return YES to allow editing to stop and to resign first responder status. NO to disallow the editing session to end

    @available(iOS 2.0, *)
    optional func textFieldDidEndEditing(_ textField: UITextField) // may be called if forced even if shouldEndEditing returns NO (e.g. view removed from window) or endEditing:YES called

    ...
    }


Related Topics



Leave a reply



Submit