Implement Swift protocol with Objective-C property getter

redent84

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
}
Ryan H.

Your UILabel and UIControl extensions satisfy the way you created your protocol because they already have a property called highlighted whose getter accessor method is isHighlighted.

Your HighlightableView does not satisfy the adoption of your Highlightable 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 like private var _highlighted = false.

In your case, since this is undesirable, you could remove the @objc attribute on your protocol.

protocol Highlightable: class {
    var highlighted: Bool { get set }
}

extension UILabel: Highlightable { }
extension UIControl: Highlightable { }

class HighlightableView: UIView, Highlightable {
    var highlighted = false
}

let label = UILabel()
label.isHighlighted // Prior to iOS 10, this property is called "highlighted"

let view = HighlightableView()
view.highlighted

let highlightables: [Highlightable] = [ label, view ]

for highlightable in highlightables {
    print(highlightable.highlighted)
}

// Prints:
// false
// false

However the property names would not be consistent across the concrete types.

Here is an alternative approach:

@objc protocol Highlightable: class {
    var isHighlighted: Bool { @objc(isHighlighted)get @objc(setHighlighted:)set }
}

extension UILabel: Highlightable { }
extension UIControl: Highlightable { }

class HighlightableView: UIView, Highlightable {
    private var _isHighlighted = false
    var isHighlighted: Bool {
        @objc(isHighlighted) get {
            return _isHighlighted
        }
        @objc(setHighlighted:) set {
            _isHighlighted = newValue
        }
    }
}

let label = UILabel()
label.isHighlighted = true

let view = HighlightableView()
view.isHighlighted

let highlightables: [Highlightable] = [ label, view ]

for highlightable in highlightables {
    print(highlightable.isHighlighted)
}

// Prints:
// false
// false

This exposes a consistent isHighlighted property across all of the concrete types while still conforming to Highlightable. 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 and UIControl's isHighlighted property was previously named highlighted. 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 to label.highlighted.

In the second example, these errors can be fixed by renaming all instances of isHighlighted to highlighted (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

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

Implement Objective c protocol in Swift

Implement protocol partially in Objective C and partially in Swift

How to implement objective C protocol in swift class?

How to define a Swift protocol with a getter and setter for an Objective-C NSArray

Is it a possible for an objective-c class to implement a swift protocol in the .h

A weak property in Objective-C class implementing a Swift protocol

Objective-c on the protocol defined a property protocol

Create a custom getter for an Objective-C property

Using a Swift protocol in Objective-C

Swift protocol in Objective-C class

Implementing an Objective-C protocol in Swift

Importing a Swift protocol in Objective-C class

Delegating Objective-C Protocol on Swift

swift, objective-c protocol implementation

Implementing Objective-C protocol in Swift class

Objective-C protocol not being recognized in Swift

Passing a Swift protocol to an Objective-C pointer

Passing dictionary objects to Objective C protocol in Swift

Objective-C protocol method invisible in Swift

How to implement block property of Objective C category

Matching a Swift protocol from an Objective-C Protocol instance

Swift protocol isn't triggering Objective C protocol method

How to implement a Swift protocol across structs with conflicting property names

Can't use concrete subclass to implement a property in protocol in Swift

To implement the following swift code in objective-c?

How to implement "didSelectButtonInCellAtIndexPath:" in Objective-C and Swift?

Swift let computed property to Objective C syntax

Swift property - getter ivar

How to implement an abstract getter with property