Chapter 15: Enumerations
大綱
Declaring an enumeration
// 第一種宣告方式
enum Month {
case january
case february
case march
case april
case may
case june
case july
case august
case september
case october
case november
case december
}
// 第二種, 全部寫成一行, 用逗號分開
enum Month {
case january, february, march, april, may, june, july, august,
september, october, november, december
}
Deciphering an enumeration in a function
如何在function中使用enum
enum中所有case都需要被處理,不然compiler會報錯
少用default
There is another huge benefit to getting rid of the default. If in a future update someone added .undecember or .duodecember to the Month enumeration, the compiler would automatically flag this and any other switch statement as being non-exhaustive, allowing you to handle this specific case.
func semester(for month: Month) -> String {
switch month {
case .august, .september, .october, .november, .december:
return "Autumn"
case .january, .february, .march, .april, .may:
return "Spring"
case .june, .july:
return "Summer"
}
}
var month = Month.april
semester(for: month) // "Spring"
month = .september
semester(for: month) // "Autumn"
Code completion prevents typos
you’ll never have a typo in your member values. Xcode provides code completion:
Raw values
Swift enum values are not backed by integers as a default. That means january is itself the value
you can associate a raw value with each enumeration case simply by declaring the raw value in the enumeration declaration
// the compiler will automatically increment the values if you provide the first one and leave out the rest
enum Month: Int {
case january = 1, february, march, april, may, june, july, august, september, october, november, december
}
Accessing the raw value
func monthsUntilWinterBreak(from month: Month) -> Int {
return Month.december.rawValue - month.rawValue
}
Initializing with the raw value
use the raw value to instantiate an enumeration value with an initializer.
There’s no guarantee that the raw value you submitted exists in the enumeration, so the initializer returns an optional.
let fifthMonth = Month(rawValue: 5) // 回傳是optional
monthsUntilWinterBreak(from: fifthMonth) // Error: not unwrapped
String raw values
// 1 The enumeration sports a String raw value type
enum Icon: String {
case music
case sports
case weather
var filename: String {
// 2 Calling rawValue inside the enumeration definition is equivalent to calling self.rawValue
return "\(rawValue).png"
}
}
let icon = Icon.weather
icon.filename
Unordered raw values
enum Coin: Int {
case penny = 1
case nickel = 5
case dime = 10
case quarter = 25
}
let coin = Coin.quarter
coin.rawValue
Associated values
這個是enum中最強的特性,也是讓enum變得無敵好用的工具。
實用時機
Each enumeration case has zero or more associated values.
The associated values for each enumeration case have their own data type.
You can define associated values with label names like you would for named function parameters.
// 先定義enum with Associated values
enum WithdrawalResult {
case success(newBalance: Int)
case error(message: String)
}
// 在fun中處理各種enum狀況
func withdraw(amount: Int) -> WithdrawalResult {
if amount <= balance {
balance -= amount
return .success(newBalance: balance)
} else {
return .error(message: "Not enough money!")
}
}
// 使用
let result = withdraw(amount: 99)
switch result {
case .success(let newBalance):
print("Your new balance is: \(newBalance)")
case .error(let message):
print(message)
}
enum HTTPMethod {
case get
case post(body: String)
}
let request = HTTPMethod.post(body: "Hi there")
// 利用guard來解出body
guard case .post(let body) = request else {
fatalError("No message was posted")
}
print(body)
Enumeration as state machine
a state machine, meaning it can only ever be a single enumeration value at a time, never more
enum TrafficLight {
case red, yellow, green
}
let trafficLight = TrafficLight.red
Iterating through all cases
When you add : CaseIterable your enumeration gains a class method called allCases
enum Pet: CaseIterable {
case cat, dog, bird, turtle, fish, hamster
}
for pet in Pet.allCases {
print(pet)
}
Enumerations without any cases
Enumerations are quite powerful. They can do most everything a structure can, including having custom initializers, computed properties and methods
struct Math {
static func factorial(of number: Int) -> Int {
return (1...number).reduce(1, *)
}
}
let factorial = Math.factorial(of: 6) // 720”
let math = Math() // 不會有error, 但這個object並沒有任何意義, 不需要產生
// 像上面struct中只有一個static method的case, 應該改用enum比較恰當
enum Math {
static func factorial(of number: Int) -> Int {
return (1...number).reduce(1, *)
}
}
let factorial = Math.factorial(of: 6)
let math = Math() // ERROR: No accessible initializers, compiler會幫我們擋住這種無所謂的object
Optionals
Optionals are really enumerations with two cases:
.none means there’s no value.
.some means there is a value, which is attached to the enumeration case as an associated value.
var age: Int?
age = 17
age = nil
switch age {
case .none:
print("No value") // 此時age = nil, 會對應到 .none
case .some(let value):
print("Got a value: \(value)")
}
let optionalNil: Int? = .none
optionalNil == nil // true
optionalNil == .none // true
Key points
An enumeration is a list of mutually exclusive cases that define a common type.
Enumerations provide a type-safe alternative to old-fashioned integer values.
You can use enumerations to handle responses, store state and encapsulate values.
CaseIterable lets you loop through an enumeration with allCases.
Uninhabited enumerations can be used as namespaces and prevent the creation of instances.
Last updated
Was this helpful?