Search code examples
swiftprotocolsequatable

Using swift protocols as Equatable argument


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)?


Solution

  • 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)
        }
    }