Can Swift enums have multiple raw values?
Solution 1
No, an enum cannot have multiple raw values - it has to be a single value, implementing the Equatable
protocol, and be literal-convertible as described in the documentation.
I think the best approach in your case is to use the error code as raw value, and a property backed by a prepopulated static dictionary with the error code as key and the text as value.
Solution 2
You have a couple options. But neither of them involve raw values. Raw values are just not the right tool for the task.
Option 1 (so-so): Associated Values
I personally highly recommend against there being more than one associated value per enum case. Associated values should be dead obvious (since they don't have arguments/names), and having more than one heavily muddies the water.
That said, it's something the language lets you do. This allows you to have each case defined differently as well, if that was something you needed. Example:
enum ErrorType {
case teapot(String, Int)
case skillet(UInt, [CGFloat])
}
Option 2 (better): Tuples! And computed properties!
Tuples are a great feature of Swift because they give you the power of creating ad-hoc types. That means you can define it in-line. Sweet!
If each of your error types are going to have a code and a description, then you could have a computed info
property (hopefully with a better name?). See below:
enum ErrorType {
case teapot
case skillet
var info: (code: Int, description: String) {
switch self {
case .teapot:
return (418, "Hear me shout!")
case .skillet:
return (326, "I'm big and heavy.")
}
}
}
Calling this would be much easier because you could use tasty, tasty dot syntax:
let errorCode = myErrorType.info.code
Solution 3
I created a way of simulating this (No different than what Marcos Crispino suggested on his answer). Far from a perfect solution but allows us to avoid those nasty switch cases for every different property we want to get.
The trick is to use a struct as the "properties/data" holder and using it as a RawValue in the enum itself.
It has a bit of duplication but it's serving me well so far. Every time you want to add a new enum case, the compiler will remind you to fill in the extra case in the rawValue getter, which should remind you to update the init?
which would remind you to create the new static property on the struct.
Code to the Gist:
enum VehicleType : RawRepresentable {
struct Vehicle : Equatable {
let name: String
let wheels: Int
static func ==(l: Vehicle, r: Vehicle) -> Bool {
return l.name == r.name && l.wheels == r.wheels
}
static var bike: Vehicle {
return Vehicle(name: "Bicycle", wheels: 2)
}
static var car: Vehicle {
return Vehicle(name: "Automobile", wheels: 4)
}
static var bus: Vehicle {
return Vehicle(name: "Autobus", wheels: 8)
}
}
typealias RawValue = Vehicle
case car
case bus
case bike
var rawValue: RawValue {
switch self {
case .car:
return Vehicle.car
case .bike:
return Vehicle.bike
case .bus:
return Vehicle.bus
}
}
init?(rawValue: RawValue) {
switch rawValue {
case Vehicle.bike:
self = .bike
case Vehicle.car:
self = .car
case Vehicle.bus:
self = .bus
default: return nil
}
}
}
VehicleType.bike.rawValue.name
VehicleType.bike.rawValue.wheels
VehicleType.car.rawValue.wheels
VehicleType(rawValue: .bike)?.rawValue.name => "Bicycle"
VehicleType(rawValue: .bike)?.rawValue.wheels => 2
VehicleType(rawValue: .car)?.rawValue.name => "Automobile"
VehicleType(rawValue: .car)?.rawValue.wheels => 4
VehicleType(rawValue: .bus)?.rawValue.name => "Autobus"
VehicleType(rawValue: .bus)?.rawValue.wheels => 8
Solution 4
No, you cannot have multiple raw values associated with an enum.
In your case, you could have the raw value to be equal to the code, and have an associated value with the description. But I think the computed properties approach is the best option here.
Solution 5
One workaround if you wanted to have many static properties for a YourError could be to import a property list; you could set the root object to a dictionary, with your enum raw value as the key for each object, allowing you to easily retrieve static structured data for the object.
This has an example of importing and using a plist: http://www.spritekitlessons.com/parsing-a-property-list-using-swift/
That might be overkill for simply an error description, for which you could just use a hardcoded static function with a switch statement for your enum values, that returns the error string you need. Simply place the static function in the same .swift file as your enum.
For instance,
static func codeForError(error : YourErrorType) -> Int {
switch(error) {
case .Teapot:
return "I'm a Teapot"
case .Teacup:
return "I'm a Teacup"
...
default:
return "Unknown Teaware Error"
}
}
This has the benefit (compared to the .plist solution) of better accomodating localization. However, a .plist could just contain a key used for retrieving the proper localization, instead of a error string, for this purpose.
![Robert Atkins](https://i.stack.imgur.com/JJ9m5.jpg?s=256&g=1)
Robert Atkins
Updated on July 25, 2022Comments
-
Robert Atkins almost 2 years
I want to associate two raw values to an enum instance (imagine an enum representing error types, I want
Error.Teapot
to have an Int type propertycode
with value 418, and a String property set toI'm a teapot
.)Note the difference between raw values and associated values here—I want all
Teapot
instances to have acode
of 418, I don't want a unique associated value for eachTeapot
instance.Is there a better way than adding computed properties to the enum that
switch
ed onself
to look up the appropriate value?