Swift 2.2 #Selector in Protocol Extension Compiler Error

Swift 2.2 #selector in protocol extension compiler error

I had a similar problem. here is what I did.

  1. Marked the protocol as @objc.
  2. Marked any methods I extended with a default behavior as optional.
  3. Then used Self. in the #selector.

    @objc public protocol UpdatableUserInterfaceType {
    optional func startUpdateUITimer()
    optional var updateInterval: NSTimeInterval { get }
    func updateUI(notif: NSTimer)
    }

    public extension UpdatableUserInterfaceType where Self: ViewController {

    var updateUITimer: NSTimer {
    return NSTimer.scheduledTimerWithTimeInterval(updateInterval, target: self, selector: #selector(Self.updateUI(_:)), userInfo: nil, repeats: true)
    }

    func startUpdateUITimer() {
    print(updateUITimer)
    }

    var updateInterval: NSTimeInterval {
    return 60.0
    }
    }

Swift 3 protocol extension using selector error

This is a Swift protocol extension. Swift protocol extensions are invisible to Objective-C, no matter what; it knows nothing of them. But #selector is about Objective-C seeing and calling your function. That is not going to happen because your on(tap:) function is defined only in the protocol extension. Thus the compiler rightly stops you.

This question is one of a large class of questions where people think they are going to be clever with protocol extensions in dealing with Cocoa by trying to inject Objective-C-callable functionality (selector, delegate method, whatever) into a class via a protocol extension. It's an appealing notion but it's just not going to work.

The method in the protocol extension is never get called, plus, an error in the book

You are correct, this is an error in the book. Concrete implementations are always selected before a protocol extension if the method is declared in the protocol definition itself.

However, if the method is added by protocol extension, is not declared in the protocol definition, and is also implemented in a concrete type, you can get the extension's implementation by casting the concrete type to the protocol.

In other words, if the sample code had included this:

protocol TeamRecord {
var wins: Int { get }
var losses: Int { get }
}

instead of this:

protocol TeamRecord {
var wins: Int { get }
var losses: Int { get }
var gamesPlayed: Int { get }
}

then the comments in the book would be correct.

There's a very thorough exploration of this here:

https://nomothetis.svbtle.com/the-ghost-of-swift-bugs-future

Issue with the call hierarchy of a swift protocol extension

Overriding superclass methods in extensions: only allowed for Objective-C compatible methods

First of all we note that you can only override a superclass method in an extension of a subclass if the method is Objective-C compatible. For classes deriving from NSObject, this is true for all instance methods (which is true here, as UIViewController derives from NSObject). This is covered in e.g. the following Q&A:

  • Can you override between extensions in Swift or not? (Compiler seems confused!)

Enforcing dynamic dispatch via Objective-C runtime

Now, from Interoperability - Interacting with Objective-C APIs - Requiring Dynamic Dispatch, we note the following

When Swift APIs are imported by the Objective-C runtime, there are no
guarantees of dynamic dispatch
for properties, methods, subscripts, or
initializers. The Swift compiler may still devirtualize or inline
member access to optimize the performance of your code, bypassing the
Objective-C runtime.

You can use the dynamic modifier to require that access to members be
dynamically dispatched through the Objective-C runtime
.

Moreover, from The Language Ref. - Declarations - Declaration Modifyers we read

dynamic (modifier)

Apply this modifier to any member of a class that can be represented
by Objective-C. When you mark a member declaration with the dynamic
modifier, access to that member is always dynamically dispatched using
the Objective-C runtime.
Access to that member is never inlined or
devirtualized by the compiler.

Because declarations marked with the dynamic modifier are dispatched
using the Objective-C runtime, they’re implicitly marked with the objc
attribute.

Enforcing dynamic dispatch for reachabilityDidChange(...) in A

Hence, if you add the dynamic modifier to method reachabilityDidChange(...) in your superclass A, then access to reachabilityDidChange(...) will always be dynamically dispatched using the Objective-C runtime, and hence find and make use of the correct overridden reachabilityDidChange(...) method in the class B extension, for instances of B. Hence,

dynamic func reachabilityDidChange(online: Bool) { ... } 

in A will fix the above.


Below follows a more minimal example of your issue above, redeemed by demanding dynamic dispatch via obj-c runtime for method foo() in class A (equivalent to your method reachabilityDidChange(...) in class A).

import UIKit

protocol Foo: class {
func foo()
}

extension Foo {
func foo() { print("default") }
}

class A: UIViewController, Foo {
dynamic func foo() { print("A") } // <-- note dynamic here
func bar() { self.foo() }
/* \
hence, foo() is dynamically dispatched here, and for
instances where "self === B()", this is correctly
resolved as the foo() in the extension to B */
}

class B : A { }

extension B {
override func foo() {
super.foo()
print("B")
}
}

let a = A()
a.bar() // A

let b = B()
b.bar() // A B
/* or, only "A" if not using dynamic dispatch */

Swift protocol extension with constraint won't make class to conform that protocol

This is a simple typo:

func dispayActivityIndicator() {

You dropped the "l" in "display".

But you should be getting a more specific warning:

note: protocol requires function 'displayActivityIndicator()' with type '() -> ()'

If you're not, that may indicate that you have some other error.



Related Topics



Leave a reply



Submit