初学者の頃に知っておきたかった Swift の列挙型 Tips
はじめに
Swift の enum(列挙型)は柔軟性があり非常に強力で、私が Swift の中で最も好きな機能の一つです。そんな enum について、初学者の頃にぜひ知っておきたかったことをいくつか紹介します。
名前空間としての case 無し enum
case の無い enum はインスタンス化やオーバーライドができません。
enum CaselessEnum { }
CaselessEnum() // 🚫 'CaselessEnum' cannot be constructed because it has no accessible initializers
この性質を利用して、静的なメソッドやプロパティのグループを定義するためによく使われます。
enum Constant {
enum API {
static let baseURLString: String = "https://example.com"
}
}
Constant.API.baseURLString // "https://example.com"
enum FooAPIClient: APIClientProtocol {
static func fetchValue(for id: Foo.ID) async throws -> Foo {
...
}
static func fetchAllValues() async throws -> [Foo] {
...
}
}
実際、Apple の API にもそのようなユースケースがあります。
A namespace for types that serve as publishers.
case 無し enum を使うと、グループ化・構造化のための名前空間を意図したものであることが明確になります。
予約語での case 命名
特定のキーワードを case 名として使おうとすると、型チェッカーがコードを解析できないためエラーとなります。
enum Setting {
case custom
case default // 🚫 if this name is unavoidable, use backticks to escape it
}
エラーを回避するためにはキーワードをバッククォートで囲みます [1] 。
enum Setting {
case custom
case `default` // 👌
}
うれしいことに、呼び出すときにはバッククォートは要りません。
Setting.default // default
CaseIterable
プロトコル
CaseIterable
プロトコルに準拠することで、型の allCases
プロパティを使用して、すべての case のコレクションにアクセスできます。
enum CompassDirection: CaseIterable {
case north, south, east, west
}
CompassDirection.allCases.count // 4
CompassDirection.allCases.map({ "\($0)" }).joined(separator: ", ") // "north, south, east, west"
リスト UI のアイテムを enum で表現する場面などにおいては、表示したいすべてのアイテムをかんたんに取得できます。 Associated Values なしの enum に限られるという制約はあるものの大変便利です。
また CaseIterable
プロトコルはテストの文脈においても有用です。
enum に case を追加したのに、それに対応するテストケースを追加し忘れてしまう(そしてテストは成功してしまう👻)ことなどはよくあるのではないでしょうか?こんな場合にも enum をCaseIterable
プロトコルに準拠させておけば、テストの網羅性を保証することができます [2]。
enum Animal: CaseIterable {
case cat
case dog
var sound: String {
switch self {
case .cat:
return "Meow!"
case .dog:
return "Woof!"
}
}
}
import XCTest
final class AnimalTests: XCTestCase {
func test_sound() {
let testCases: [(line: UInt, animal: Animal, expected: String)] = [
(#line, .cat, "Meow!"),
(#line, .dog, "Woof!")
]
// テストケースの網羅性をチェック ✅️
Animal.allCases.forEach { animal in
XCTAssert(testCases.contains(where: { $0.animal == animal }), "\(animal) がテストされていません!")
}
for testCase in testCases {
XCTAssertEqual(testCase.animal.sound, testCase.expected, line: testCase.line)
}
}
}
まとめ
以上、Swift の enum に関する Tips でした。初学者の方の参考になれば幸いです。
Discussion