I want to create a common ancestor for all highlightable views in Swift. I wanted that already existing UIKit classes that implement highlighted
property worked out of the box, so after reading this answer and checking that the Objective-C getter is defined as isHighlighted
I changed the protocol definition to this:
@objc protocol Highlightable {
var highlighted: Bool { @objc(isHighlighted) get set }
}
So the protocol implementation for UILabel
and UIControl
is as simple as this:
extension UILabel: Highlightable {}
extension UIControl: Highlightable {}
This works great, I can access and set the highlighted
property from Swift as Highlightable
instances. However, when I try to implement the protocol on my Swift classes with even the simplest implementation like this:
class HighlightableView: UIView, Highlightable {
var highlighted: Bool = false
}
I get this compilation error:
Objective-C method 'highlighted' provided by getter for 'highlighted' does not match the requirement's selector ('isHighlighted')
The only way I could get it to work is using computed properties, but it's not what I want.
class HighlightableView: UIView, Highlightable {
var highlighted: Bool { @objc(isHighlighted) get { return true } set {} }
}
My environment is Xcode 8.0 and Swift 3. Updated to XCode 8.2 and the error persists.
My current workaround is to completely avoid any naming conflict between Objective-C and Swift. But this is far from ideal:
@objc protocol Highlightable {
var _highlighted: Bool { get set }
}
extension UILabel: Highlightable {
var _highlighted: Bool {
get { return isHighlighted }
set { isHighlighted = newValue }
}
}
extension UIControl: Highlightable {
var _highlighted: Bool {
get { return isHighlighted }
set { isHighlighted = newValue }
}
}
class HighlightableView: UIView, Highlightable {
var _highlighted: Bool = false
}
Your
UILabel
andUIControl
extensions satisfy the way you created your protocol because they already have a property calledhighlighted
whose getter accessor method isisHighlighted
.Your
HighlightableView
does not satisfy the adoption of yourHighlightable
protocol because you have the@objc(isHighlighted)
requirement on your getter.You have to use computed properties to satisfy this. However, this means that you also need a backing store for the
highlighted
property. Something likeprivate var _highlighted = false
.In your case, since this is undesirable, you could remove the
@objc
attribute on your protocol.However the property names would not be consistent across the concrete types.
Here is an alternative approach:
This exposes a consistent
isHighlighted
property across all of the concrete types while still conforming toHighlightable
. The drawback here is the@objc
attribute is more pervasive in a context where is should not be necessary. That is, the@objc
attribute is not being used to expose a Swift protocol to Objective-C code.EDIT:
Looking at the iOS 10 API diffs for Swift (and doing some testing in Xcode 7.2),
UILabel
andUIControl
'sisHighlighted
property was previously namedhighlighted
. Using the code above while linking against iOS SDK 9.3 or lower will result in compile time errors.In the first example, these errors can be fixed by renaming the
label.isHighlighted
line tolabel.highlighted
.In the second example, these errors can be fixed by renaming all instances of
isHighlighted
tohighlighted
(except for those within the brackets of@objc
attributes).9.3 to iOS 10.0 API Differences: https://developer.apple.com/library/content/releasenotes/General/iOS10APIDiffs/index.html