Optional Protocol Requirements, I Can't Get It to Work

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



Leave a reply



Submit