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
Uploading Image with Afnetworking 2.0
How to Capitalize Each Word in a String Using Swift iOS
Xcode 8 - Ib Designables - Failed to Render and Update Auto Layout Status, the Agent Crashed
Simulate Force Touch/3D Touch on iPhone 6S or iPhone 6S Plus Simulators
How to Detect When Keyboard Is Shown and Hidden
Programmatically Creating an Expanding Uitableviewcell
Get Image from Documents Directory Swift
Xcode - Ld: Library Not Found for -Lpods
How to Rotate Text for Uibutton and Uilabel in Objective-C
Xcode Is Looking for Core Data Entity Names with Dot; Not Compiling
How to Draw a Line Programmatically from a View Controller
What Is "Self" Used for in Swift
Parsing Nested Array of Dictionaries Using Object Mapper
How to Fix "No Valid 'Aps-Environment' Entitlement String Found for Application" in Xcode 4.3