Swift 2.2 #selector in protocol extension compiler error
I had a similar problem. here is what I did.
- Marked the protocol as @objc.
- Marked any methods I extended with a default behavior as optional.
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 thedynamic
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 theobjc
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
Add a Text Overlay with Avmutablevideocomposition to a Specific Timerange
Xcode 5/iOS 7 - Localization Not Working in Simulator
Nsdata to Nsstring with JSON Response
How to Show "Would Like to Send You Push Notifications" Alert View Again
Uicollectionviewcell Dynamic Height W/Two Dynamic Labels & Auto Layout
Play a Video from Youtube in a Avplayerviewcontroller in Swift
API Facebook iPhone , Possible to Post to a Friend's Wall
Navigationcontroller.Navigationitem VS Navigationitem
How to Resize Views When Keyboard Pops Up Using Auto Layout
iOS 5 Gm: <Error>: More Than Maximum 5 Filtered Album Lists Trying to Register. This Will Fail
Draggesture on a VStack with Lot of Buttons, How to Detect When the Drag Is Inside a Button