Home > Software engineering >  Swift workaround for protocols comparison
Swift workaround for protocols comparison

Time:11-19

I know comparing protocols doesn't make any sense but my situation is dictated by choices and decisions taken before me. A table view's data source is an array of RowViewModel.

protocol RowViewModel {}

there's nothing in there (yet) to make it even conform to Equatable. Then my table has different cells, all of which implement that protocol:

func getCells() -> [RowViewModel] {
    var rows = [RowViewModel]()
    rows.append(Cell1ViewModel())
    rows.append(Cell2ViewModel())
    rows.append(Cell3ViewModel())

    return rows
}

Cell's view model:

class Cell1ViewModel: RowViewModel {
    var cellTitle: String
    ...
}

This structure is convenient but it now shoots me in the back because I now need to calculate delta to send specific tableView indexes to insert / delete rows. To calculate delta I need RowViewModel to conform to Equatable, which is possible but seems like a workaround that defies the initial point of using this approach. I'd like to do something like this:

let oldList = rows
let newList = getCells()

let deltaAdded = newList.filter { !oldList.contains($0) }.compactMap { newList.firstIndex(of: $0) }
let deltaRemoved = oldList.filter { !newList.contains($0) }.compactMap { oldList.firstIndex(of: $0) }

What is the best practice here? Is there a way to write a comparison function for concrete types conforming to the RowViewModel?

CodePudding user response:

As I told in comment you would have something like:

class CellViewModel1: Equatable {

    // classes need explicit equatable conformance.
    static func == (lhs: CellViewModel1, rhs: CellViewModel1) -> Bool {
        // your implementation
        return true
    }

}

enum RowViewModel: Equatable { 
    
    // enum automatically is Equatable as long as all cases have Equatable associated types
    case cell1(CellViewModel1)

}

func test() {
    let oldList = [RowViewModel]()
    let newList = [RowViewModel]()

    let deltaAdded = newList.filter { !oldList.contains($0) }.compactMap { newList.firstIndex(of: $0) }
    let deltaRemoved = oldList.filter { !newList.contains($0) }.compactMap { oldList.firstIndex(of: $0) }
}

Notice that both enum and ViewModels must conform to Equatable. Still not 100% sure if this fits your necessities.

  • Related