How to define optional methods in Swift protocol?
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.
Comments
-
Selvin almost 2 years
Is it possible in Swift? If not then is there a workaround to do it?
-
Selvin about 10 yearsCompiler Error: 'optional' attribute can only be applied to members of an @objc protocol.
-
George Green almost 10 yearsWith this solution you can't access self from within any of the protocol functions. This may cause issues in some cases!
-
Zag almost 10 years@GeorgeGreen You can access self. Mark the function variable as lazy and use a capture list inside the closure.
-
khunshan over 9 yearsOnly 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 over 9 yearsCan you describe a little more the "two levels of optional at play" part, namely this:
delegate?.indexDidChange?(index!)
? -
Blessing Lopes over 9 yearsIf 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 about 9 yearsMinor correction (too small to edit!) but it should be 'optional' not '@optional'
-
Alfie Hanssen about 9 years
weak var delegate : CollectionOfDataDelegate?
(ensure weak reference?) -
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 almost 9 yearsThis also requires you to mark your class implementing the protocol as
@objc
, not just the protocol. -
Johnathon Sullinger almost 9 years
@optional
isn't even the correct keyword. It'soptional
, and you must declare the class and protocol wit the@objc
attribute. -
Zag almost 9 years@khunshan, this method requires nothing being marked @ objc, what are you referring to?
-
khunshan almost 9 yearsits 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 almost 9 yearsThis is probably the cleanest way to do it in Swift. Too bad it does not work prior to Swift 2.0.
-
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 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 over 8 yearsGood writeup of the subtlety in dynamic dispatch to protocol or implementing class here: nomothetis.svbtle.com/…
-
Darko about 8 yearsBut 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 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 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 almost 8 yearsThis should be the correct answer. Simple, clean and explanatory.
-
mfaani over 7 yearsSo 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 over 7 yearsAny source of why
@objc
is needed on all members now? -
Sentry.co over 7 years@Gautam Sareriya: What do you think is best this approach or creating empty extension methods?
-
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 about 7 yearsI tried with your methods with
required
flag, but with errors:required
may only be used on 'init' declarations. -
akashivskyy about 7 yearsBecause "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 almost 7 yearsGood job! This should be the correct answer since it solves the problem without getting Objective-C runtime involved.
-
Eric almost 6 yearsThis. It's adherence to the "I" principle in the SOLID software development principles.
-
Roshana Pitigala almost 6 yearsWhile 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 about 5 yearsfrom 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 about 5 yearsFor the first error you mentionned, you need to make the protocol as a subclass of NSObject:
@objc protocol Health: AnyObject {...