Optional Protocol Requirements, I Can't Get It To Work
@objc protocol
requires @objc
implementation.
In your case:
class ThreeSource: CounterDataSource {
@objc var fixedIncrement = 3
// ^^^^^ YOU NEED THIS!
}
without it, Objective-C runtime can't find the property.
Swift optional protocol requirements
In Objective-C you can declare an optional member (method/computed property) in a protocol.
In pure Swift this is not possible unless you use the @objc annotation (but in this case structs and enums won't be able to conform to your protocol so it's not the best way to go).
So, how can you solve your problem?
Let's split SomeProtocol into 3 protocols
These are your protocols
protocol TypeAProtocol { }
protocol TypeBProtocol { }
protocol SomeProtocol {
associatedtype TypeA: TypeAProtocol
associatedtype TypeB: TypeBProtocol
var objA: TypeA? { get set }
var objB: TypeB? { get set }
}
Now we'll split SomeProtocol
into 3 protocols
protocol SomeProtocolBase {
associatedtype TypeA: TypeAProtocol
associatedtype TypeB: TypeBProtocol
}
protocol SomeProtocolPartA: SomeProtocolBase {
var objA: TypeA? { get set }
}
protocol SomeProtocolPartB: SomeProtocolBase {
var objB: TypeB? { get set }
}
That's it
Now you can write
class ClassOfTypeAProtocol: TypeAProtocol { }
class ClassOfTypeBProtocol: TypeBProtocol { }
class SomeClass: SomeProtocolPartA {
typealias TypeA = ClassOfTypeAProtocol
typealias TypeB = ClassOfTypeBProtocol
var objA: ClassOfTypeAProtocol?
}
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 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.
Optional Variables in protocol is possible?
protocol TestProtocol {
var name : String {set get}
var age : Int {set get}
}
Provide a default extension for the protocol. Provide the default implementation for all the variables set and get which u want them to be optional.
In below protocol, name and age are optional.
extension TestProtocol {
var name: String {
get { return "Any default Name" } set {}
}
var age : Int { get{ return 23 } set{} }
}
Now if I am conforming above protocol to any other class, like
class TestViewController: UIViewController, TestProtocol{
var itemName: String = ""
**I can implement the name only, and my objective is achieved here, that the controller will not give a warning that "TestViewController does not conform to protocol TestProtocol"**
var name: String {
get {
return itemName ?? ""
} set {}
}
}
Optional can only be applied to members of an @objc protocol
Swift doesn't allow protocols to have optional requirements-- if the protocol declares something, it's required. Objective-C has long had the idea of optional requirements, and Swift recognizes this when you use @objc
when declaring the protocol. So using @objc
is the easy way to get what you're after.
If you want a pure Swift solution, you need to add a protocol extension that includes default implementations of the methods. This doesn't make those methods optional, instead it says that any class that doesn't implement the method itself will use the default implementation instead. They're sort of optional in that classes don't have to implement them, but not really optional since that's only because a default implementation is available.
That would look something like this:
protocol DrawViewProtocol : class{
func drawViewDidEndEditing()
}
extension DrawViewProtocol {
func drawViewDidEndEditing() {}
}
class MyClass : DrawViewProtocol {
}
class MyOtherClass : DrawViewProtocol {
func drawViewDidEndEditing() {
print("Foo")
}
}
Now if I create an instance of MyClass
and call drawViewDidEndEditing()
, it uses the default implementation, which does nothing. If I create an instance of MyOtherClass
, the same method call prints "Foo".
Checking for optional protocol methods in Swift gives error?
The error you're getting makes sense because Swift can know at compile time that Bear
doesn't implement cough()
(whereas Objective-C wouldn't necessarily be able to know that).
To make your code compile, you need to define bear
using the Bearable
protocol instead of the Bear
class.
var bear: Bearable?
Which is probably what you'd want anyway. Otherwise, there's not much point in creating that protocol.
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?
Crash when calling optional protocol method
All the @optional
directive does is suppresses compiler warnings if the optional methods are not implemented. However, if you call a method that the class does not implement, the app will still crash, as the selector (method) you tried to call is not recognised by the class, since it's not implemented.
You can work around this by checking whether the delegate implements a method before calling it:
// Check that the object that is set as the delegate implements the method you are about to call
if ([self.photoDropDownDelegate respondsToSelector:@selector(dismissPhotoDropDown)]) {
// The object does implement the method, so you can safely call it.
[self.photoDropDownDelegate dismissPhotoDropDown];
}
This way, if the delegate object implements an optional method, it will be called. Otherwise, it won't, and your program will continue running as normal.
Note that you should still use the @optional
directive to denote methods that are optional to implement, in order to avoid compiler warnings when you don't implement them. This is particularly important for open source software or libraries that will be distributed to clients, as this directive tells the developers that haven't read your implementation, but can only see the header, that they don't need to implement these methods, and everything will still be fine.
Related Topics
How to Create Generic Closures in Swift
Generating Random Doable Math Problems Swift
How to Horizontally Center Content of Horizontal Scrollview
Secidentity + Force Cast Violation: Force Casts Should Be Avoided. (Force_Cast)
Is There an Firebase Realtime Database Equivalent to The Increment Method Available in Firestore
Urlcomponents Queryitems Losing Percent Encoding When Mutated
How to Provide Default Implementation of an Objective-C Protocol in a Swift Protocol Extension
How to Update Nslayoutconstraint in a UItableviewcell of Dynamic Height
Writing an Enum Case Check to a Bool Variable Without Equatable Conformance
Proper Way of Editing a Cocoapod Library
Swift 3/4 Dash to Camel Case (Snake to Camelcase)
User Specific Avatar in Jsqmessagesviewcontroller with Sdwebimage
Flattened Objects in JSON File to Nested Object Structure in Swift
Swiftui Go Back Programmatically from Representable to View
Force Refresh on Another Viewcontroller Component with Swift