This is an article for beginners. Even if you are not a beginner, you may discover something. I will write about enums of Swift grammar.
Operation confirmed with Swift 5.3
Are Raw Value and Associated Value available at the same time in Swift enums?
If you know this, you may not need to read this article in particular.
enum It is called an enumeration type. Defined by enum and case.
enum CompassPoint {
case north
case south
case east
case west
}
Variables are ** singular **.
If you know which enum type the variable is, you can omit the type like .east
.
var directionToHead = CompassPoint.north
directionToHead = .south
var directionToHeadLast: CompassPoint = .east
Switch In the Switch statement, a compile error will occur if all enum cases are not covered. You can also use default.
switch directionToHead {
case .north, .east, .south, .west: print("direction")
}
switch directionToHead {
case .north, .east, .south: print("direction")
default: print("other")
}
By adding CaseIterable, you can turn the enum element with a for statement.
It is like this
enum CompassPoint: CaseIterable {
case north
case south
case east
case west
}
CompassPoint.allCases.forEach{print($0)}
This will output the following:
north
south
east
west
Associated Values
Each element of the enum mentioned earlier can have some value.
enum Appliance {
case light(isOn: Bool)
case airCon(degree: Int, isOn: Bool)
}
var appliance: Appliance
appliance = .light(isOn: true)
appliance = .airCon(degree: 28, isOn: true)
When using it in a Switch statement, you can retrieve the Associated Value with let or var.
switch appliance {
case .light(let isOn):
print(isOn)
case .airCon(let degree, let isOn):
print(isOn, degree)
}
Note that the Case Iterable that came out earlier cannot be used for enums with ** Associated Values **!
When the associated value is all let or all var, it is multiplied as follows.
switch appliance {
case let .light(isOn):
print(isOn)
case let .airCon(degree, isOn):
print(isOn, degree)
}
You don't have to write multiple let by putting let
in front!
Raw Values
Unlike Associated Values, you can put ** values that are ** fixed at the time of writing in each enum case.
enum Step: Int {
case one = 1
case two = 2
case three = 3
}
print(Step.three.rawValue)
This will display 3
.
enum Step: Int {
case one, two, three
}
print(Step.one.rawValue)
What do you think this will show?
Actually
0
Is displayed.
Only when the Raw Value of Int or String is set, the value is entered without permission.
In the case of Int, 0
enters the first case and is incremented one by one.
In the case of String, the name of the case itself is entered.
Let's do more about Raw Value. It will cause problems, so let's guess the output string.
Q1
enum Step: Int,CaseIterable {
case one, two, three=99
}
Step.allCases.forEach{print($0.rawValue)}
0
1
99
Q2
enum Step: Int,CaseIterable {
case one, two=99, three
}
Step.allCases.forEach{print($0.rawValue)}
0
99
100
Q3
enum Step: Int,CaseIterable {
case one=12, two=11, three
}
Step.allCases.forEach{print($0.rawValue)}
There is a compilation error. For three, 12 is entered, which is an increment of 11 for two, but I get angry because this is covered with one.
Q4
enum Step: Int8,CaseIterable {
case one, two=127, three
}
Step.allCases.forEach{print($0.rawValue)}
This is also a compile error Int8 has a max of 127, so I get angry when the three value overflows Int8.
error: MyPlayground.playground:12:24: error: integer literal '128' overflows when stored into 'Int8'
case one, two=127, three
It can be an enum from a rawValue. Note that if you specify a value that is not in the enum, it will be ** nil **!
enum Step: Int, CaseIterable {
case one=1, two, three
}
Step(rawValue: 2) // two
Step(rawValue: 4) // nil
In other words, the return value will be Optional when initialized with rawValue. Use Optional Bindings when initializing with rawValue
guard let step = Step(rawValue: 4) else {
fatalError("undefined!!")
}
That's the first problem.
The answer is can
If you did it normally, this code
enum CompassPoint: String {
case north(Int)
case south(Int)
case east(Int)
case west(Int)
}
I get a compile error.
'CompassPoint' declares raw type 'String', but does not conform to RawRepresentable and conformance could not be synthesized
However, in reality, it can be managed.
The way to write the raw value of the enum explained this time is called shorthand notation
. It is written as follows.
enum Test: String {
case pass = "Yay!"
}
The condition to realize RawValue is the compliance with the protocol called RawRepresentable
, which is also in the above compilation error.
If there is even one Associated Value in the case, shorthand notation
will not automatically comply with RawRepresentable
.
So you can use Raw Value and Associated Value at the same time by doing the following.
enum TestResult {
case pass(Int)
case fail(Int)
}
extension TestResult: RawRepresentable {
typealias RawValue = String
init?(rawValue: RawValue) {
switch rawValue {
case "Yay!": self = .pass(1)
case "Sorry ...": self = .fail(0)
default:
return nil
}
}
var rawValue: RawValue {
switch self {
case .pass: return "Yay!"
case .fail: return "Sorry ..."
}
}
}
TestResult.pass(1).rawValue //Yay!
TestResult.fail(0).rawValue //Sorry ...
The caveat is that you have to put the Associated Value in init.
After all, how to write RawValue of shorthand notation
is just a shorthand expression, and it seems that the compiler makes it to be RawRepresentable
as above. (Reference: The RawRepresentable Protocol in Swift – Ole Begemann)
If you want to recursively use an enum for an Associated Value, you need to add the indirect keyword to that case.
enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression) //Recursion because the argument here contains itself
}
The indirect can also be brought to the beginning of the enum.
indirect enum ArithmeticExpression: CustomStringConvertible {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
}
Compile error
error: MyPlayground.playground:2:6: error: recursive enum 'ArithmeticExpression' is not marked 'indirect'
enum ArithmeticExpression: CustomStringConvertible {
^
indirect
let expression = ArithmeticExpression.addition(.number(10), .addition(.number(1), .number(2)))
print(expression.description)
I want to display the expression as a string in the above print. I tried to display the expression string recursively using CustomStringConvertible.
enum ArithmeticExpression: CustomStringConvertible {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
var description: String {
get {
switch self {
case .number(let num):
return String(num)
case .addition(let expr1, let expr2):
return "\(expr1.description) + \(expr2.description)"
}
}
}
}
Is it like this?
10 + 1 + 2
It was displayed safely!