Home > Mobile >  Using swift protocols as Equatable argument
Using swift protocols as Equatable argument

Time:09-10

For example we have two simple protocols:

protocol Container {
    var items: [Item] {get}
}

protocol Item {
    var name: String {get}
}

And we want to use a function that requires Item to be Equatable, like:

extension Container {
    func indexOfItem(_ item: Item) -> Int? {
        items.firstIndex(of: item)
    }
}

Yet we can't write it like this, as firstIndex requires argument conforming to Equatable. But Item being protocol, can't conform to Equatable.

I've tried to implement it using type erasure, but it became too complicated.

So I am curious is there some workaround to implement such functions (provided that actual items are equatable)?

CodePudding user response:

Since you're using func firstIndex of an array in this func indexOfItem(_ item: Item) -> Int? therefore the Item has to be a concrete object (behind the scene of firstIndex func is comparing each element of an array and print out the index of the element).

There are 2 ways to do this

  • First is using associatedtype to keep your protocol generic
protocol Item: Equatable {
    var name: String { get }
}

protocol Container {
    associatedtype Item
    var items: [Item] { get }
}

struct MyItem: Item {
    var name: String
}

extension Container where Item == MyItem {
    func indexOfItem(_ item: Item) -> Int? {
        return items.firstIndex(of: item)
    }
}
  • Second is using an equatable object MyItem instead a protocol Item inside the Container protocol
protocol Item {
    var name: String { get }
}

protocol Container {
    var items: [MyItem] { get }
}

struct MyItem: Item, Equatable {
    var name: String
}

extension Container {
    func findIndex(of item: MyItem) -> Int? {
        return items.firstIndex(of: item)
    }
}

CodePudding user response:

Finally find simple enough solution:

То make protocol generic with associated type and constraint this type to Equatable.

public protocol Container {
    associatedtype EquatableItem: Item, Equatable
    var items: [EquatableItem] {get}
}

public protocol Item {
    var name: String {get}
}

public extension Container {
    func indexOfItem(_ item: EquatableItem) -> Int? {
        items.firstIndex(of: item)
    }
}

This compiles and now if I have some types

struct SomeContainer {
    var items: [SomeItem]
}

struct SomeItem: Item, Equatable {
    var name: String
}

I only need to resolve associatedtype to provide protocol conformance for SomeContainer type:

extension SomeContainer: Container {
    typealias EquatableItem = SomeItem
}
  • Related