More Concise Way to Nest Enums for Access by Switch Statements in Swift

More concise way to nest enums for access by switch statements in Swift?

I think this would be much more concisely expressed with two enums and a tuple. Try this in a playground:

enum HorizontalPosition {
case Left
case Right
case Center
}

enum VerticalPosition {
case Top
case Bottom
case Center
}

typealias Location = (horizontal: HorizontalPosition, vertical: VerticalPosition)

let aLocation = Location(horizontal: .Left, vertical: .Bottom)

switch aLocation {

case (.Left, .Bottom): print ("left bottom")
case (.Center, .Center): print ("center center")
default: print ("everything else")
}

How do you use a switch statement with a nested enum?

I'm adding a more general answer for a few reasons.

  1. This is the only open question regarding nested enums and switch statements. The other one is sadly closed.
  2. The only legit answer does not show how to assign the value of a nested enum to a symbol. The syntax was not intuitive to me.
  3. None of the other answers have extensive case examples.
  4. An enum nested 3 levels deep is more illustrative of the required syntax. Using efremidze answer still took me a while to work it out.

enum Action {
case fighter(F)
case weapon(W)

enum F {
case attack(A)
case defend(D)
case hurt(H)

enum A {
case fail
case success
}
enum D {
case fail
case success
}
enum H {
case none
case some
}
}
enum W {
case swing
case back
}
}

// Matches "3 deep"
let action = Action.fighter(.attack(.fail))
// Matches "1 deep" because more general case listed first.
let action2 = Action.weapon(.swing)

switch action {
case .fighter(.attack(.fail)):
print("3 deep")
case .weapon:
print("1 deep")
case .weapon(.swing):
print("2 deep to case")
case .fighter(.attack):
print("2 deep to another enum level")
default:
print("WTF enum")
}

Swift: Nested enum with switch statement

Your approach will work, you just have a few issues with your implementation. Whether or not this approach is the best way is a different issue which would depend on your requirements. I would switch to an enum with associated values for the parent if possible.

  1. Your Nested1 enum case can't have the same name as your Nested1 enum. Enum cases should start with a lowercase letter anyway so this is easy to fix.

  2. Cases for an enum with an Int raw value start at 0 by default but you want your first case to map to 1, so you need to explicitly state that.

  3. Initializing an enum with a raw value returns an optional, so you should unwrap that optional before switching on it.

Fixing these issues should give you this, which prints "Helmet" and "Iron":

enum Parent: Int {

enum Nested1: Int {
case bow = 1
case sword
case lance
case dagger
}

enum Nested2: Int {
case wooden = 1
case iron
case diamond
}

case nested1 = 1
case nested2
case case3
case case4
case case5

}

let parent = 2
let nested = 2

guard let parentCase = Parent(rawValue: parent) else {
// Do something to handle invalid enum case here
fatalError()
}

switch parentCase {
case .nested1:
print("Weapon")

case .nested2:
print("Helmet")
guard let nestedCase = Parent.Nested2(rawValue: nested) else {
// Do something to handle invalid enum case here
break
}

switch nestedCase {
case .wooden:
print("Weapon")

case .iron:
print("Iron")

case .diamond:
print("Diamond")

default:
print("")
}

default:
print("")
}

Swift Switch access nested properties within 'case'

When you use multiple patterns in a single case of a switch, they must bind all of the same variables.

Swift sees this line:

case (let c as C, 4), let nonNilSpecific as? String:

and thinks you're trying to match either (let c as C, 4) or let nonNilSpecific as? String. Those two choices bind different variables, so in the case body it is impossible to know which variables have been bound.

Perhaps you wanted something like this:

switch (unknown, unknown.common) {
case (let a as A, 4):
break

case (let b as B, 4):
break

case (let c as C, 4) where c.specific != nil:
// force unwrap safe here, no closure needed
let nonNilSpecific = c.specific!

default: break
}

Use an if:

let tuple = (unknown, unknown.common)
if case (let a as A, 4) = tuple {
// use a
} else if case (let b as B, 4) = tuple {
// use b
} else if case (let c as C, 4) = tuple, let nonNilSpecific = c.specific {
// use c and nonNilSpecific
} else {
// default case
}

Managing multiple UIViews names into switch...case Swift statement

As suggested by Joakim comment, the solution is easier than I thought: a simple Array of names, and I post it to further reference. Thank you to the other suggestions, I'll study better.

In the controller scope:

var views = [UIView]()

In viewDidLoad() func:

views = [view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view0b, view1b, view2b, view3b, view4b, view5b, view6b, view7b, view8b, view9b, view10b]

In the switch...case statement:

        for i in 0...21
{
switch (parsedArray[i]) {
case "0":
views[i].backgroundColor = .systemRed // HERE [i] CALLS SINGLE UIVIEW NAME!
case "1":
views[i].backgroundColor = .systemGreen // HERE [i] CALLS SINGLE UIVIEW NAME!
default:
print("DEFAULT!")
}
}

How to use nested constants in Swift

Operator overloading may help you to achieve similar functionality.
Please look at my solution, you can extend it a lot more

enum Vehicle: String {
case car = "Car"
case airplane = "Airplane"

enum CarPart: String {
case door = "Door"
case window = "Window"
}
}

func > (left: Vehicle, right: Vehicle.CarPart) -> String {
return left.rawValue + right.rawValue
}

let string = .car > .window // "CarWindow"

How to test equality of Swift enums with associated values

Swift 4.1+

As @jedwidz has helpfully pointed out, from Swift 4.1 (due to SE-0185, Swift also supports synthesizing Equatable and Hashable for enums with associated values.

So if you're on Swift 4.1 or newer, the following will automatically synthesize the necessary methods such that XCTAssert(t1 == t2) works. The key is to add the Equatable protocol to your enum.

enum SimpleToken: Equatable {
case Name(String)
case Number(Int)
}
let t1 = SimpleToken.Number(123)
let t2 = SimpleToken.Number(123)

Before Swift 4.1

As others have noted, Swift doesn't synthesize the necessary equality operators automatically. Let me propose a cleaner (IMHO) implementation, though:

enum SimpleToken: Equatable {
case Name(String)
case Number(Int)
}

public func ==(lhs: SimpleToken, rhs: SimpleToken) -> Bool {
switch (lhs, rhs) {
case let (.Name(a), .Name(b)),
let (.Number(a), .Number(b)):
return a == b
default:
return false
}
}

It's far from ideal — there's a lot of repetition — but at least you don't need to do nested switches with if-statements inside.

Recursive Swift Either Enum with Generic Associated Values

So I discovered that Swift 2 introduced support for recursive enums ages ago - source! While this is great, I didn't manage to find any SO questions for this specific problem i.e. Generic Recursive Enums that had also been answered (here). I will summarise the final (partial solution) code now.

It turns out that I needed to describe what the problem is in terms of data structures. Essentially we want .left to contain either a value of type Left or a RecursiveEither<Left, Right>. With this simple idea, we can create two enums - one for the wrapper enum, and one for the nested enum which can take a Value or another wrapper.

enum RecursiveEither<Left, Right> {
case left(ValueOrLeftOrRight<Left, Left, Right>)
case right(ValueOrLeftOrRight<Right, Left, Right>)

var left: Left? {
guard case .left(let leftie) = self else { return nil }
return leftie.left
}

var right: Right? {
guard case .right(let rightie) = self else { return nil }
return rightie.right
}
}

enum ValueOrLeftOrRight<Value, Left, Right> {
case value(Value)
indirect case left(ValueOrLeftOrRight<Left, Left, Right>)
indirect case right(ValueOrLeftOrRight<Right, Left, Right>)

var left: Left? {
switch self {
case .value(let left): return left as? Left
case .left(let content): return content.left
default: return nil
}
}

var right: Right? {
switch self {
case .value(let right): return right as? Right
case .right(let content): return content.right
default: return nil
}
}
}

The call site is nice with this design:

let e = RecursiveEither<Int, Int>.left(.left(.left(.left(.value(3)))))

That being said, there are still limitations to this answer - assuming Left and Right won't change even in nested Either. Also, it's possible to design this differently with only one indirect case to wrap the Either type in another associated value - that might be more memory efficient since fewer frames get pushed onto the call stack at runtime.

I have posted a gist with attempts on this problem.

If anyone has suggestions to improve this implementation, feel free to add to this.

Accessing an Enumeration association value in Swift

The value is associated to an instance of the enumeration. Therefore, to access it without a switch, you need to make a getter and make it available explicitly. Something like below:

enum Number {
case int(Int)
case float(Float)

func get() -> NSNumber {
switch self {
case .int(let num):
return num
case .float(let num):
return num
}
}
}

var vInteger = Number.int(10)
var vFloat = Number.float(10.5)

println(vInteger.get())
println(vFloat.get())

Maybe in the future something like that may be automatically created or a shorter convenience could be added to the language.



Related Topics



Leave a reply



Submit