Travis Griggs
Travis Griggs

Reputation: 22290

access swift enum case outside of switch?

Let's say I have a simple Pet enum:

enum Pet {
    case dog(name:String)
    case cat(name:String)

    var name:String {
        switch self {
        case .dog(let name):
            return name
        case .cat(let name):
            return name
        }
    }
}

(The fact that they have the same type of associated value and therefore feels redundant should be ignored)

Now I want to be able to equate pets. I could write:

extension Pet:Equatable { }

func == (a:Pet, b:Pet) -> Bool {
    return a.name == b.name
}

But this will allow a cat named "Spots" to be equal to a dog named "Spots". This I don't want. Is there an easy way to access the case of an enum instance? We usually do them in switch statements, but in this case, I wanted something more direct (one line), I might need to use it in a future Comparable extension after all.

I could do my own:

extension Pet {
    var petType:Int {
        switch self {
        case .dog:
            return 1
        case .cat:
            return 2
        }
    }
}

Now I can change my == implementation to be

func == (a:Pet, b:Pet) -> Bool {
    return a.petType == b.petType && a.name == b.name
}

Is there some built in Swift syntax sugar that would do the same thing for me? I tried type(of:), but that just returns Pet, nothing about the instance case.

Upvotes: 0

Views: 549

Answers (3)

par
par

Reputation: 17734

You can do something like this:

func ==(lhs: Pet, rhs: Pet) -> Bool {
    if case let Pet.cat(l) = lhs, case let Pet.cat(r) = rhs where l == r { return true }
    if case let Pet.dog(l) = lhs, case let Pet.dog(r) = rhs where l == r { return true }

   return false
}

But a switch probably looks better and is definitely easier to maintain.

Upvotes: 1

GetSwifty
GetSwifty

Reputation: 7756

I have sort of a hybrid approach that keeps things a tad more modular:

    enum Pet {

    case dog(name:String)
    case cat(name:String)

    var name: String {
        switch self {
        case .dog(let name):
            return name
        case .cat(let name):
            return name
        }
    }

    func equalCaseTo(otherPet: Pet) -> Bool {
        switch (self, otherPet) {
        case (.dog(_), .dog(_)), (.cat(_), .cat(_)):
            return true
        default:
            return false
        }
    }

    func equalNameTo(otherPet: Pet) -> Bool {
        return name == otherPet.name
    }
}

func ==(left:Pet, right:Pet) -> Bool {
    return left.equalCaseTo(right) && left.equalNameTo(right)
}

Upvotes: 1

OOPer
OOPer

Reputation: 47906

As far as I know, Swift does not provide us a shortcut to retrieve only case labels.

In your case you can write something like this utilizing your name property:

extension Pet: Equatable {
    static func == (a: Pet, b: Pet) -> Bool {
        switch (a, b) {
        case (.dog, .dog), (.cat, .cat):
            return a.name == b.name
        default:
            return false
        }
    }
}

If your Pet does not have the name property, you can write it as:

extension Pet: Equatable {
    static func == (a: Pet, b: Pet) -> Bool {
        switch (a, b) {
        case let (.dog(namea), .dog(nameb)),
             let (.cat(namea), .cat(nameb)):
            return namea == nameb
        default:
            return false
        }
    }
}

But in your case, isn't it sort of natural to use class hierarchy?:

class Pet {
    var name: String
    init(name: String) {
        self.name = name
    }
}

class Dog: Pet {}
class Cat: Pet {}

extension Pet {
    static func dog(name: String) -> Pet {
        return Dog(name: name)
    }
    static func cat(name: String) -> Pet {
        return Cat(name: name)
    }
}

extension Pet: Equatable {
    static func == (a: Pet, b: Pet) -> Bool {
        return type(of: a) === type(of: b) && a.name == b.name
    }
}

Upvotes: 2

Related Questions