Swift 2, Protocol Extensions & Respondstoselector

Swift 2, protocol extensions & respondsToSelector

Interesting. Looks like a bug to me. It does recognize functions on a class, but not on extension. No matter what type Instance has. Moreover without an extension code would not be compilable, since protocol methods are non optional. So really looks like a bug/feature? in responds to selector implementation.

Swift: Using protocol extension results in unrecognized selector sent to instance

The problem is that Objective-C knows nothing of protocol extensions. Thus, you cannot use a protocol extension to inject a method into a class in such a way that Objective-C's messaging mechanism can see it. You need to declare bar in the view controller class itself.

(I realize that this is precisely what you were trying to avoid doing, but I can't help that.)

A sort of workaround might be this:

override func viewDidLoad() {
super.viewDidLoad()

foo = "testing"
let tapGesture = UITapGestureRecognizer(target: self, action: "baz")
self.view.addGestureRecognizer(tapGesture)
}

func baz() {
self.bar()
}

We are now using Objective-C's messaging mechanism to call baz and then Swift's messaging mechanism to call bar, and of course that works. I realize it isn't as clean as what you had in mind, but at least now the implementation of bar can live in the protocol extension.

(Of course, another solution would be to do what we did before protocol extensions existed: make all your view controllers inherit from some common custom UIViewController subclass containing bar.)

Is there any way in Swift 2.0 to make extension methods optional to implement?

Most people are now aware that Swift 2.0 can specialize extension methods around generic parameters, e.g.,

extension Array where T: Int {
// This method only appears when T is an Int
func seven() -> Int {
return 7
}
}

It's also possible to constrain Self in this way, and this is what makes "opt-in" extension methods possible. The key here is where Self: NSManagedObjectContext in the extension below:

public protocol ManagedObjectContextType {
func from<E: EntityType>(_: E.Type) -> EntityQuery<E>
func newEntity<E: NSManagedObject where E: EntityType>(_: E.Type) -> E
func newEntity<E: NSManagedObject where E: EntityType>() -> E
}

/**
Extend `NSManagedObjectContext` with this interface in your project to
benefit from these methods.
*/
extension ManagedObjectContextType where Self: NSManagedObjectContext {
/**
Initiates a query whose result type is `E`.
*/
public func from<E: EntityType>(_: E.Type) -> EntityQuery<E> {
return EntityQuery(builder: QueryBuilder()).context(self)
}

/**
Inserts a newly allocated entity of type `E` into this `NSManagedObjectContext`.
*/
public func newEntity<E: NSManagedObject where E: EntityType>(_: E.Type) -> E {
return NSEntityDescription.insertNewObjectForEntityForName(E.entityNameInManagedObjectModel(persistentStoreCoordinator!.managedObjectModel), inManagedObjectContext: self) as! E
}

/**
Inserts a newly allocated entity of type `E` into this `NSManagedObjectContext`.
*/
public func newEntity<E: NSManagedObject where E: EntityType>() -> E {
return newEntity(E)
}
}

The beauty of this is that the Swift 2.0 compiler recognizes in these extensions that self is an instance of NSManagedObjectContext, so you can use all of the appropriate methods.

Now, users of your framework can decide for themselves whether they want to add your extension methods by simply implementing the interface in their project:

extension NSManagedObjectContext: ManagedObjectContextType {}

This allows authors of frameworks to provide extensions and default implementations in a less cluttered way.

Why must Protocol defaults be implemented via Extensions in Swift?

The @optional directive is an Objective-C only directive and has not been translated to Swift. This doesn't mean that you can't use it in Swift, but that you have to expose your Swift code to Objective-C first with he @objc attribute.

Note that exposing to Obj-C will only make the protocol available to types that are in both Swift and Obj-C, this excludes for example Structs as they are only available in Swift!

To answer your first question, Protocols are here to define and not implement:

A protocol defines a blueprint of methods, properties, and other requirements [...]

And the implementation should thus be supplied by the class/stuct/enum that conforms to it:

The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements

This definition really applies to Protocols we use in our daily lives as well. Take for example the protocol to write a Paper:

The PaperProtocol defines a paper as a document with the following non-nil variables:

  • Introduction
  • Chapters
  • Conclusion

What the introduction, chapters and conclusion contain are up to the one implementing them (the writer) and not the protocol.

When we look at the definition of Extensions, we can see that they are here to add (implement) new functionalities:

Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you do not have access to the original source code.

So extending a protocol (which is allowed) gives you the possibility to add new functionality and hereby give a default implementation of a defined method. Doing so will work as a Swift only alternative to the @optional directive discussed above.

UPDATE:

While giving a default implementation to a protocol function in Switch does make it "optional", it is fundamentally not the same as using the @optional directive in Objective-C.

In Objective-C, an optional method that is not implemented has no implementation at all so calling it would result in a crash as it does not exist. One would thus have to check if it exists before calling it, in opposition to Swift with an extension default where you can call it safely as a default implementation exists.

An optional in Obj-C would be used like this:

NSString *thisSegmentTitle;
// Verify that the optional method has been implemented
if ([self.dataSource respondsToSelector:@selector(titleForSegmentAtIndex:)]) {
// Call it
thisSegmentTitle = [self.dataSource titleForSegmentAtIndex:index];
} else {
// Do something as fallback
}

Where it's Swift counterpart with extension default would be:

let thisSegmentTitle = self.dataSource.titleForSegmentAtIndex(index)

When to use respondsToSelector in swift?

There is scarcely any need to call respondsToSelector explicitly in Swift. The only time when you'd want to do it is in dealing with an NSObject, and in that case, it is presumably an AnyObject or a delegate protocol adopter, and in either case you can just use question-mark syntax (which calls respondsToSelector for you).

Check if optional protocol method is implemented in Swift?

Per The Swift Programming Language:

You check for an implementation of an optional requirement by writing
a question mark after the name of the requirement when it is called,
such as someOptionalMethod?(someArgument). Optional property
requirements, and optional method requirements that return a value,
will always return an optional value of the appropriate type when they
are accessed or called, to reflect the fact that the optional
requirement may not have been implemented.

So the intention is not that you check whether the method is implemented, it's that you attempt to call it regardless and get an optional back.

Default Implementation of Objective-C Optional Protocol Methods

You do it just exactly as you've implemented it. The difference ends up being in how the method is actually called.

Let's take this very simplified example:

@objc protocol FooProtocol {
optional func bar() -> Int
}

class Omitted: NSObject, FooProtocol {}
class Implemented: NSObject, FooProtocol {
func bar() -> Int {
print("did custom bar")
return 1
}
}

By adding no other code, I'd expect to have to use this code as such:

let o: FooProtocol = Omitted()
let oN = o.bar?()

let i: FooProtocol = Implemented()
let iN = i.bar?()

Where oN and iN both end up having type Int?, oN is nil, iN is 1 and we see the text "did custom bar" print.

Importantly, not the optionally chained method call: bar?(), that question mark between the method name in the parenthesis. This is how we must call optional protocol methods from Swift.

Now let's add an extension for our protocol:

extension FooProtocol {
func bar() -> Int {
print("did bar")
return 0
}
}

If we stick to our original code, where we optionally chain the method calls, there is no change in behavior:

Sample Image

However, with the protocol extension, we no longer have to optionally unwrap. We can take the optional unwrapping out, and the extension is called:

Sample Image

The unfortunate problem here is that this isn't necessarily particularly useful, is it? Now we're just calling the method implemented in the extension every time.

So there's one slightly better option if you're in control of the class making use of the protocol and calling the methods. You can check whether or not the class responds to the selector:

let i: FooProtocol = Implemented()

if i.respondsToSelector("bar") {
i.bar?()
}
else {
i.bar()
}

Sample Image

This also means you have to modify your protocol declaration:

@objc protocol FooProtocol: NSObjectProtocol

Adding NSObjectProtocol allows us to call respondsToSelector, and doesn't really change our protocol at all. We'd already have to be inheriting from NSObject in order to implement a protocol marked as @objc.

Of course, with all this said, any Objective-C code isn't going to be able to perform this logic on your Swift types and presumably won't be able to actually call methods implemented in these protocol extensions it seems. So if you're trying to get something out of Apple's frameworks to call the extension method, it seems you're out of luck. It also seems that even if you're trying to call one or the other in Swift, if it's a protocol method mark as optional, there's not a very great solution.



Related Topics



Leave a reply



Submit