Search code examples
swiftenumsequatableassociated-value

How to make a Swift enum with associated values equatable


I have an enum of associated values which I would like to make equatable for testing purposes, but do not know how this pattern would work with an enum case with more than one argument.

For example, summarised below I know the syntax for making heading equatable. How would this work for options, which contains multiple values of different types?

enum ViewModel {
    case heading(String)
    case options(id: String, title: String, enabled: Bool)
}

func ==(lhs: ViewModel, rhs: ViewModel) -> Bool {
    switch (lhs, rhs) {
    case (let .heading(lhsString), let .heading(rhsString)):
        return lhsString == rhsString
    case options...
    default:
        return false
    }
}

I know Swift 4.1 can synthesize conformance for Equatable for us, but at present I am not able to update to this version.


Solution

  • SE-0185 Synthesizing Equatable and Hashable conformance has been implemented in Swift 4.1, so that it suffices do declare conformance to the protocol (if all members are Equatable):

    enum ViewModel: Equatable {
        case heading(String)
        case options(id: String, title: String, enabled: Bool)
    }
    

    For earlier Swift versions, a convenient way is to use that tuples can be compared with ==.

    You many also want to enclose the compatibility code in a Swift version check, so that the automatic synthesis is used once the project is updated to Swift 4.1:

    enum ViewModel: Equatable {
        case heading(String)
        case options(id: String, title: String, enabled: Bool)
        
        #if swift(>=4.1)
        #else
        static func ==(lhs: ViewModel, rhs: ViewModel) -> Bool {
            switch (lhs, rhs) {
            case (let .heading(lhsString), let .heading(rhsString)):
                return lhsString == rhsString
            case (let .options(lhsId, lhsTitle, lhsEnabled), let .options(rhsId, rhsTitle, rhsEnabled)):
                return (lhsId, lhsTitle, lhsEnabled) == (rhsId, rhsTitle, rhsEnabled)
            default:
                return false
            }
        }
        #endif
    }