How to Add More Cases for Enum in Swift

Adding a case to an existing enum with a protocol

Design

The work around is to use a struct with static variables.

Note: This is what is done in Swift 3 for Notification.Name

Below is an implementation on Swift 3

Struct:

struct Car : RawRepresentable, Equatable, Hashable, Comparable {

typealias RawValue = String

var rawValue: String

static let Red = Car(rawValue: "Red")
static let Blue = Car(rawValue: "Blue")

//MARK: Hashable

var hashValue: Int {
return rawValue.hashValue
}

//MARK: Comparable

public static func <(lhs: Car, rhs: Car) -> Bool {

return lhs.rawValue < rhs.rawValue
}

}

Protocol

protocol CoolCar {

}

extension CoolCar {

static var Yellow : Car {

return Car(rawValue: "Yellow")
}
}

extension Car : CoolCar {

}

Invoking

let c1 = Car.Red

switch c1 {
case Car.Red:
print("Car is red")
case Car.Blue:
print("Car is blue")
case Car.Yellow:
print("Car is yellow")
default:
print("Car is some other color")
}

if c1 == Car.Red {
print("Equal")
}

if Car.Red > Car.Blue {
print("Red is greater than Blue")
}

Note:

Please note this approach is not a substitute for enum, use this only when the values are not known at compile time.

Is it possible group & represent multiple cases as another case within an enum in Swift?

You can used nested Enum and a case with parameter

enum Devices {
case phone(iPhone)
case tablet(iPad)

enum iPhone {
case phone7
case phoneX
}

enum iPad {
case mini
case pro
}
}

let randomDevice = Devices.phone(.phone7)

switch randomDevice {
case .phone:
print("Its a phone")
default:
break
}

// prints "Its a phone"

Is it possible to assign multiple values in an enumeration? Swift language

No , enum can't have multiple raw values.But , you can use competed property like that

enum DollarCountries1{
case usa(String?)
var storedDollar : [String] {
["USA","Australia","Canada"]
}

var moneyType : String{
switch self {
case .usa(let str):
if storedDollar.contains(str!){
return "Dollar";
}
return "none"
default:
return "none"
}
}

}

let country = "USA"
var forUsa = DollarCountries1.usa(country)
print(forUsa.moneyType)

Creating enum cases dynamically

One idea is to use a struct as an intermediary between data and the view controller. This struct can be created and used by a method like tableview's cell count; each time your view needs to know its layout, it constructs itself using whether or not there exists pending data.

enum Section: Int, CaseIterable {
case title, accounts, pending, total
}

struct SectionData {

var sections: [Section]

init(hasPendingData: Bool) {
if (hasPendingData) {
sections = Section.allCases
} else {
sections = [.title, .accounts]
}
}

}

class MyViewController: UIViewController {

var pendingModelData: Data?

func sectionCount() -> Int {
return SectionData(hasPendingData: pendingModelData != nil).sections.count
}

}

Going a step further, you could use didSet on pendingModelData to update the view displaying sections.

swift enum get the associated value of multiple case with same parameters in single switch-case

You can put multiple enum values in the same case line, but you have to move the let into the ():

var value: Double {
switch self {
case .width(let value), .height(let value), .xxxxx1(let value):
return value
}
}

You might want to put each enum value on a separate line:

var value: Double {
switch self {
case .width(let value),
.height(let value),
.xxxxx1(let value):
return value
}
}

Unfortunately, that's about as elegant as it gets with enums with associated values. There's no way to get the associated value without explicitly listing the enums.

You can only combine values in the same line that have the same type of associated value, and all of the values have to bind to the same variable name. That way, in the code that follows, you know that the value is bound and what its type is no matter which enum pattern matched.

Multiple enum types list all cases

Here is a demo of possible solution. Prepared with Xcode 12.1 / iOS 14.1

demo

enum Fish: String, CaseIterable {
case goldfish
case blueTang = "blue_tang"
case shark
}

func view<T: CaseIterable & Hashable>(for type: T.Type) -> some View where T.AllCases: RandomAccessCollection {
VStack(alignment: .leading) {
Text(String(describing: type)).bold()
ForEach(type.allCases, id: \.self) { item in
Text(String(describing: item))
}
}
}

struct FishDemoView: View {
var body: some View {
view(for: Fish.self)
}
}

How to pass an enum case into a function that uses 'if case' to check for a property's case

The answer of Shadowrun is a good start. Through the CasePaths library I found EnumKit, which was partially the inspiration for CasePaths. However it gives much simpler methods to compare cases. See my implementation below.

Using this library comes with some nice bonuses, it allows enums that have associated values to be made equatable by just comparing the cases and ignoring the values. This might not always be desired but come in quite handy in a lot of cases. I use it to compare ReSwift Actions with Associated values in my tests.

import Foundation
import EnumKit

class NonEquatableObject {
}

enum MyEnum: CaseAccessible { // <-- conform to CaseAccessible
case justACase
case numberCase(Int)
case stringCase(String)
case objectCase(NonEquatableObject)
}

let myArray: [MyEnum] = [.justACase, .numberCase(100), .stringCase("Hello Enum"), .justACase]

if case .justACase = myArray[0] {
print("myArray[0] is .justACase")
}

if case .numberCase = myArray[1] {
print("myArray[1] is .numberCase")
}

func checkCase(lhsCase: MyEnum, rhsCase: MyEnum) -> Bool {
if case lhsCase = rhsCase {
return true
} else {
return false
}
}

// Usage:
if checkCase(lhsCase: .justACase, rhsCase: myArray[0]) { //<-- allows to pass variables or just the cases (unfortunately not without associated value.)
print("myArray[0] is .justACase")
}

if case myArray[3] = myArray[0] { //<-- allows this here too
print("myArray[0] is .justACase")
}

// Bonus: Adding equatable if associated values are not equatable. Looking at the case only.
extension MyEnum: Equatable {
static func == (lhs: MyEnum, rhs: MyEnum) -> Bool {
lhs.matches(case: rhs)
}
}

if myArray[3] == myArray[0] {
print("myArray[3] == myArray[0]")
}

Swift - extend an enum for only one of its cases

Moved from comments:

I think here enum is not a best idea. You should try to do this with protocol + 3 classes/structs implementing it. That will allow you both extend "only one case" and add more "cases" in it. Like here.

Quick info about linked article:

Why I don’t love Swift Enums any more


At the last two Sydney Cocoaheads, any time I see a use of Enums in a talk, I make sure to ask “Why an enum and not a protocol?”. This blog post will hopefully explain the smell of enums.



Firstly, I have to say that enums are great if you never need to extend the number of cases. However, incorrect usages of enums break the O in SOLID Principles.

Open Close Principle


A Type should be open to extension, but closed for edits.

And I believe the Expression Problem best explains the violation.

The Expression Problem


Add new methods; add new cases. Pick one.

I will attempt to walk you through the Expression Problem with enums, then proceed to “solve” it with the Visitor Pattern.



Related Topics



Leave a reply



Submit