Reputation: 421
In other languages I can construct complex enums which hold multiple values / a class / a struct for each case. This seems to be impossible in Swift. At least I could not find a simple solution. So far I came up with these possibilities which include some boilerplate and are not as elegant as I am used to from other languages.
What would be the downside of each solution? Is there something else I could do? Would a class be a better solution?
In the end I want a finite, distinct, iterable construct of immutable values. I would like that to be an enum since they usually have benefits like knowing when they were matched exhaustively.
enum CategoryEnum: Int, CaseIterable {
case general = 9
case tech = 5
var value: Category? {
switch rawValue {
case 9:
return Category(name: "General Knowledge", id: rawValue, symbol: Image(systemName: "globe"))
case 5:
return Category(name: "Technical", id: rawValue, symbol: Image(systemName: "internaldrive"))
default:
return nil // or throw error to ged rid of optional?
}
}
}
struct Category {
static let GENERAL = Category(name: "General Knowledge", id: 9, symbol: Image(systemName: "globe"))
static let TECH = Category(name: "Technical", id: 5, symbol: Image(systemName: "internaldrive"))
static private let cases: [Int: Category] = [
GENERAL.id: GENERAL,
TECH.id: TECH
]
static func fromId(_ id: Int) -> Category? {
Category.cases[id]
}
static func values() -> Dictionary<Int, Category>.Values {
cases.values
}
let name: String
let id: Int
let symbol: Image
}
func testWithEnum() {
// iterating over all cases
for cat in CategoryEnum.allCases {
print(cat.value!.name)
}
// getting a case from the id
let catGen = CategoryEnum(rawValue: 9)
print(catGen!.value!.name)
// a specific case
print(CategoryEnum.general.value!.name)
}
func testWithStruct() {
// iterating over all cases
for cat in Category.values() {
print(cat.name)
}
// getting a case from the id
print(Category.fromId(9)!.name)
// a specific case
print(Category.TECH.name)
}
Upvotes: 0
Views: 2362
Reputation: 270790
I would do:
enum Category: Int, CaseIterable {
case general = 9
case tech = 5
var name: String {
switch self {
case .general: return "General Knowledge"
case .tech: return "Technical"
}
}
var symbol: Image {
switch self {
case .general: return Image(systemName: "globe")
case .tech: return Image(systemName: "internaldrive")
}
}
}
The one thing you might not like about this is that you can't see at a glance, what name and symbol a certain enum case has, compared to your struct solution.
Note that every time you add a case to this, you won't forget to add return
statements to each of the properties, because the switch statements would no longer be exhaustive and the compiler would complain.
You don't need a separate id
property. That seems to be just be the raw value. init(rawValue:)
is synthesised because you have a raw value, and allCases
is also synthesised due to CaseIterable
conformance.
What would be the downside of each solution?
Your enum solution returns an optional for its value
, but clearly each case must have a Category
associated with it, so it doesn't make much sense. As you can see in my code, you can switch on self
to make the switch exhaustive.
Your struct solution doesn't prevent people from creating other Category
s, so it's not "finite" as you wanted. You should write a private initialiser to prevent that if you want to go with a struct solution. Also, there's quite a lot of boilerplate for case
and fromId
. If you use an enum, those are synthesised by the compiler.
Upvotes: 2