Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extend @objc protocol with Comparable in Swift

I am trying to extend my protocol Option with Comparable to use simple .sort() method.

Below short example only with Equatable to show errors.

@objc protocol Option: Equatable {
    var title: String { get }
    var enabled: Bool { get }
    var position: Int { get }
}

func ==(lhs: Option, rhs: Option) -> Bool {
    return lhs.position == rhs.position
}

The Option protocol must be marked as @objc or inherit from NSObjectProtocol because it will be used with UIKit.

Errors:

  1. @objc protocol 'Option' cannot refine non-@objc protocol 'Equatable'

  2. Protocol 'Option' can only be used as a generic constraint because it has Self or associated type requirements

Do you have any suggestion how to solve this problem?

like image 778
dtd Avatar asked Jan 14 '16 10:01

dtd


2 Answers

Equatable lives in the Swift world only, thus you cannot extend it to a protocol that will be used by Objective-C. Trying to do this results in error #1

Protocols that have a Self requirement (i.e. at least one method from the protocol declaration contains Self) cannot be used as arguments to functions, or to variable declarations, only as arguments to a generic clause, e.g. func doSomething<T: Option>(argument: T).

Removing Equatable from the Option protocol declaration, and declaring == as generic on Option will solve the compile errors. As for sorting, you can also overload the < operator, and sort via that operator (without needing to implement Comparable):

@objc protocol Option {
    var title: String { get }
    var enabled: Bool { get }
    var position: Int { get }
}

func ==<T: Option>(lhs: T, rhs: T) -> Bool {
    return lhs.position == rhs.position
}

func <<T: Option>(lhs: T, rhs: T) -> Bool {
    return lhs.position < rhs.position
}

This allows you to pass objects that conform to the protocol to UIKit, and to also compare them within your swift code.

class A: NSObject, Option { .. }
class B: NSObject, Option { ... }

let a = A()
let b = B()
a == b  // compiles, and returns true if a and b have the same position
let c: [Option] = [a, b]
c.sort(<) // returns a sorted array by the `position` field

One important note regarding the sorting code above: if you don't specify the type for c, then the compiler infers its type as [NSObject], and the sort call will not compile due to ambiguity of the < operator. You need to explicitly declare c as [Option] to take advantage of the overloaded operator.

like image 78
Cristik Avatar answered Oct 03 '22 16:10

Cristik


The issue can be fixed by the new protocol oriented programming features introduced in swift 2.0

@objc protocol 'Option' cannot refine non-@objc protocol 'Equatable'

As the error states, the Equatable protocol is a swift protocol that you can't to Obj C context

Protocol 'Option' can only be used as a generic constraint because it has Self or associated type requirements

You can achieve this in the following way:

@objc protocol Option {
    var title: String { get }
    var enabled: Bool { get }
    var position: Int { get }
}

extension Equatable where Self : Option
{

}

extension Comparable where Self : Option
{

}

func ==(lhs: Option, rhs: Option) -> Bool
{
    return lhs.position == rhs.position
}

func <(lhs: Option, rhs: Option) -> Bool
{
    return lhs.position < rhs.position
}

func >(lhs: Option, rhs: Option) -> Bool
{
    return lhs.position > rhs.position
}

And your class and implementation looks like:

class MyClass: Option
{
    @objc var title: String = ""
    @objc var enabled: Bool = true
    @objc var position: Int = 0

    init()
    {
    }

    convenience init(title : String, enabled : Bool, position: Int)
    {
        self.init()
        self.title    = title
        self.enabled  = enabled
        self.position = position
    }
}

let firstObj               = MyClass()
let secondObj              = MyClass()
let optionArray : [Option] = [firstObj, secondObj]

// Sort array of options
optionArray.sort(<)
like image 30
Midhun MP Avatar answered Oct 03 '22 16:10

Midhun MP