Swiftのswitch文を完全マスター:基本から応用まで

Swift

Swiftで条件分岐を行うとき、「if文」と並んでよくつかわれるのが「switch文」です。

C言語やJavaScriptのswitch文に慣れている人も多いと思いますが、Swiftのswitchは一味ちがいます

この記事では、Swiftのswitch文の基本構文から、便利なつかい方、注意点までを初心者にもわかりやすく紹介します。

条件分岐をスマートに書きたい人は必見です。

スポンサーリンク

switch文ってなに?

説明

switch文は、ひとつの値に対して複数の条件を効率的にチェックする制御構文です。

if文でも同じことができますが、switch文の方が読みやすく、書きやすい場合が多くあります。

if文との比較

if文での書き方

let fruit = "apple"

if fruit == "apple" {
    print("りんごです")
} else if fruit == "banana" {
    print("バナナです")
} else if fruit == "orange" {
    print("オレンジです")
} else {
    print("その他の果物")
}

switch文での書き方

let fruit = "apple"

switch fruit {
case "apple":
    print("りんごです")
case "banana":
    print("バナナです")
case "orange":
    print("オレンジです")
default:
    print("その他の果物")
}

どちらも同じ結果ですが、switch文の方がスッキリしていませんか?

switch文の基本構文

基本的な書き方

switch 値 {
case パターン1:
    // 処理1
case パターン2:
    // 処理2
default:
    // どのケースにも当てはまらない場合の処理
}

実際の例

let score = 85

switch score {
case 100:
    print("満点!素晴らしい!")
case 90:
    print("優秀です")
case 80:
    print("良くできました")
case 70:
    print("合格です")
default:
    print("もう少し頑張りましょう")
}

// 出力: 80以上90未満なので "良くできました" ではなく、
// 実際は85なので default が実行される

注意:上の例は間違いです。正しくは下記のように範囲指定が必要です。

let score = 85

switch score {
case 100:
    print("満点!素晴らしい!")
case 90..<100:
    print("優秀です")
case 80..<90:
    print("良くできました")    // 85はここに該当
case 70..<80:
    print("合格です")
default:
    print("もう少し頑張りましょう")
}

// 出力: "良くできました"

文字列でのswitch

let weather = "sunny"

switch weather {
case "sunny":
    print("☀️ 晴れです。お出かけ日和ですね!")
case "cloudy":
    print("☁️ 曇りです。過ごしやすそうです。")
case "rainy":
    print("🌧️ 雨です。傘を忘れずに!")
case "snowy":
    print("❄️ 雪です。足元に注意してください。")
default:
    print("🤔 天気がよくわかりません。")
}

// 出力: "☀️ 晴れです。お出かけ日和ですね!"

Swiftのswitch文の特徴

特徴1:break文が不要

他の言語(例:JavaScript)

// JavaScript例
switch (fruit) {
    case "apple":
        console.log("りんご");
        break;  // breakが必要
    case "banana":
        console.log("バナナ");
        break;  // breakが必要
}

Swift

// Swift
switch fruit {
case "apple":
    print("りんご")     // breakは不要
case "banana":
    print("バナナ")     // breakは不要
default:
    print("その他")
}

特徴2:すべてのケースを網羅する必要がある

let number = 5

// ❌ エラーになる例
/*
switch number {
case 1:
    print("1です")
case 2:
    print("2です")
// default がないのでエラー!
}
*/

// ✅ 正しい例
switch number {
case 1:
    print("1です")
case 2:
    print("2です")
default:               // 必要
    print("1でも2でもありません")
}

特徴3:空のケースは書けない

let value = 10

// ❌ エラーになる例
/*
switch value {
case 1:
    // 何も書かないとエラー
case 2:
    print("2です")
default:
    print("その他")
}
*/

// ✅ 正しい例
switch value {
case 1:
    break              // 何もしない場合はbreakを明示
case 2:
    print("2です")
default:
    print("その他")
}

複数の値をまとめて処理

カンマで区切って複数の値を指定

let character: Character = "a"

switch character {
case "a", "e", "i", "o", "u":
    print("母音です")
case "y":
    print("時々母音として扱われます")
default:
    print("子音です")
}

// 出力: "母音です"

より実用的な例

let httpStatusCode = 404

switch httpStatusCode {
case 200, 201, 202:
    print("✅ 成功")
case 300, 301, 302:
    print("🔄 リダイレクト")
case 400, 401, 403, 404:
    print("❌ クライアントエラー")
case 500, 501, 502, 503:
    print("💥 サーバーエラー")
default:
    print("❓ 不明なステータス")
}

// 出力: "❌ クライアントエラー"

曜日の判定例

let dayOfWeek = "金曜日"

switch dayOfWeek {
case "月曜日", "火曜日", "水曜日", "木曜日":
    print("平日です。お仕事頑張って!")
case "金曜日":
    print("金曜日!週末まであと少し!")
case "土曜日", "日曜日":
    print("週末です。ゆっくり休んでください。")
default:
    print("正しい曜日を入力してください")
}

// 出力: "金曜日!週末まであと少し!"

範囲(Range)をつかったパターンマッチ

数値の範囲指定

let temperature = 25

switch temperature {
case Int.min..<0:
    print("🥶 氷点下です。とても寒い!")
case 0..<10:
    print("❄️ 寒いです。暖かくしてください。")
case 10..<20:
    print("🧥 涼しいです。上着があると良いですね。")
case 20..<30:
    print("😊 過ごしやすい気温です。")
case 30..<40:
    print("🔥 暑いです。水分補給を忘れずに!")
case 40...:
    print("🌡️ 危険な暑さです!")
default:
    print("不正な温度です")
}

// 出力: "😊 過ごしやすい気温です。"

範囲演算子の種類

演算子意味説明
...閉区間1...51, 2, 3, 4, 5
..<半開区間1..<51, 2, 3, 4
...片側範囲5...5以上
..<片側範囲..<55未満

成績判定の例

let score = 87

switch score {
case 90...100:
    print("🏆 A: 優秀!")
case 80..<90:
    print("🎉 B: 良くできました")    // 87はここに該当
case 70..<80:
    print("👍 C: 合格です")
case 60..<70:
    print("😐 D: もう少し頑張りましょう")
case 0..<60:
    print("😢 F: 再試験です")
default:
    print("❓ 無効な点数です")
}

// 出力: "🎉 B: 良くできました"

where句を使った条件追加

説明

where句をつかうと、ケースにさらに細かい条件を追加できます。

基本的な例

let point = (x: 3, y: 4)

switch point {
case let (x, y) where x == y:
    print("対角線上の点です (x=\(x), y=\(y))")
case let (x, y) where x == 0:
    print("Y軸上の点です (x=\(x), y=\(y))")
case let (x, y) where y == 0:
    print("X軸上の点です (x=\(x), y=\(y))")
case let (x, y) where x > 0 && y > 0:
    print("第1象限の点です (x=\(x), y=\(y))")
case let (x, y):
    print("その他の点です (x=\(x), y=\(y))")
}

// 出力: "第1象限の点です (x=3, y=4)"

より実用的な例

let age = 25
let hasLicense = true

switch age {
case 0..<18:
    print("未成年です")
case 18... where hasLicense:
    print("運転できます!")
case 18... where !hasLicense:
    print("免許を取得してください")
default:
    print("無効な年齢です")
}

// 出力: "運転できます!"

タプル(組み合わせ)のパターンマッチ

説明

複数の値を組み合わせて判定したい場合、タプルをつかったパターンマッチが便利です。

座標の判定例

let coordinate = (x: 2, y: 3)

switch coordinate {
case (0, 0):
    print("原点です")
case (0, _):
    print("Y軸上の点です")
case (_, 0):
    print("X軸上の点です")
case (1...5, 1...5):
    print("第1象限の小さなエリアです")
case (-5...(-1), -5...(-1)):
    print("第3象限の小さなエリアです")
default:
    print("その他の位置です")
}

// 出力: "第1象限の小さなエリアです"

ゲームの勝敗判定

let gameResult = (playerScore: 85, computerScore: 78)

switch gameResult {
case (let player, let computer) where player > computer:
    print("🎉 プレイヤーの勝利! (\(player) vs \(computer))")
case (let player, let computer) where player < computer:
    print("😅 コンピューターの勝利 (\(player) vs \(computer))")
case (let score, let score):  // 同じ値の場合
    print("🤝 引き分けです (\(score) vs \(score))")
}

// 出力: "🎉 プレイヤーの勝利! (85 vs 78)"

列挙型(enum)とswitch文

説明

列挙型とswitch文は相性抜群です。特にすべてのケースを網羅する必要があるため、安全なコードが書けます。

基本的な列挙型

enum Direction {
    case north
    case south
    case east
    case west
}

let currentDirection = Direction.north

switch currentDirection {
case .north:
    print("🧭 北に向かっています")
case .south:
    print("🧭 南に向かっています")
case .east:
    print("🧭 東に向かっています")
case .west:
    print("🧭 西に向かっています")
}

// 出力: "🧭 北に向かっています"

関連値を持つ列挙型

enum Weather {
    case sunny
    case cloudy
    case rainy(intensity: Int)  // 雨の強さを数値で表現
    case snowy(depth: Double)   // 雪の深さを表現
}

let todayWeather = Weather.rainy(intensity: 7)

switch todayWeather {
case .sunny:
    print("☀️ 晴れです!")
case .cloudy:
    print("☁️ 曇りです")
case .rainy(let intensity) where intensity < 3:
    print("🌦️ 小雨です")
case .rainy(let intensity) where intensity < 7:
    print("🌧️ 普通の雨です")
case .rainy(let intensity):
    print("⛈️ 大雨です!強さ: \(intensity)")
case .snowy(let depth):
    print("❄️ 雪です。積雪: \(depth)cm")
}

// 出力: "⛈️ 大雨です!強さ: 7"

アプリの状態管理

enum AppState {
    case loading
    case loaded(data: [String])
    case error(message: String)
}

let currentState = AppState.loaded(data: ["項目1", "項目2", "項目3"])

switch currentState {
case .loading:
    print("⏳ データを読み込み中...")
case .loaded(let data):
    print("✅ データ読み込み完了!項目数: \(data.count)")
    for item in data {
        print("- \(item)")
    }
case .error(let message):
    print("❌ エラーが発生しました: \(message)")
}

// 出力:
// ✅ データ読み込み完了!項目数: 3
// - 項目1
// - 項目2  
// - 項目3

fallthrough の使い方

説明

Swiftでは通常、各ケースが実行されると自動的に処理が終了します。次のケースも実行したい場合はfallthroughをつかいます。

基本的な例

let number = 1

switch number {
case 1:
    print("1です")
    fallthrough     // 次のケースも実行
case 2:
    print("1または2です")
    fallthrough     // さらに次のケースも実行
case 3:
    print("1、2、または3です")
default:
    print("その他の数字です")
}

// 出力:
// 1です
// 1または2です
// 1、2、または3です

より実用的な例

let userRole = "admin"

switch userRole {
case "admin":
    print("🔧 管理者権限")
    fallthrough
case "moderator":
    print("🛡️ モデレーター権限")
    fallthrough
case "user":
    print("👤 ユーザー権限")
default:
    print("❓ 不明な権限")
}

// adminの場合の出力:
// 🔧 管理者権限
// 🛡️ モデレーター権限
// 👤 ユーザー権限

注意点

fallthroughは慎重につかいましょう。多用するとコードが読みにくくなります。

// ❌ 複雑すぎる例
let grade = "A"

switch grade {
case "A":
    print("優秀")
    fallthrough
case "B":
    print("良い")
    fallthrough
case "C":
    print("普通")
    fallthrough
default:
    print("評価完了")
}

// ✅ より良い書き方
switch grade {
case "A":
    print("優秀な成績です")
case "B":
    print("良い成績です")
case "C":
    print("普通の成績です")
default:
    print("評価対象外です")
}

オプショナル型とswitch文

説明

オプショナル型(nil の可能性がある値)もswitch文で処理できます。

基本的な例

let optionalName: String? = "田中"

switch optionalName {
case .some(let name):
    print("名前は \(name) です")
case .none:
    print("名前が設定されていません")
}

// または以下のように書くこともできます
switch optionalName {
case let name?:
    print("名前は \(name) です")
case nil:
    print("名前が設定されていません")
}

// 出力: "名前は 田中 です"

より実用的な例

let userAge: Int? = 25

switch userAge {
case let age? where age < 18:
    print("未成年です(\(age)歳)")
case let age? where age >= 18 && age < 65:
    print("成人です(\(age)歳)")
case let age? where age >= 65:
    print("高齢者です(\(age)歳)")
case nil:
    print("年齢が設定されていません")
default:
    print("無効な年齢です")
}

// 出力: "成人です(25歳)"

実用的なswitch活用例

HTTP ステータスコードの処理

func handleHttpResponse(statusCode: Int) {
    switch statusCode {
    case 200:
        print("✅ 成功")
    case 201:
        print("✅ 作成成功")
    case 400:
        print("❌ 不正なリクエスト")
    case 401:
        print("🔐 認証が必要です")
    case 403:
        print("🚫 アクセス禁止")
    case 404:
        print("🔍 ページが見つかりません")
    case 500...599:
        print("💥 サーバーエラー(\(statusCode))")
    default:
        print("❓ 不明なステータス(\(statusCode))")
    }
}

// 使用例
handleHttpResponse(statusCode: 200)  // ✅ 成功
handleHttpResponse(statusCode: 404)  // 🔍 ページが見つかりません
handleHttpResponse(statusCode: 500)  // 💥 サーバーエラー(500)

ファイルサイズの判定

func describeFileSize(bytes: Int) {
    switch bytes {
    case 0:
        print("空のファイルです")
    case 1..<1024:
        print("小さなファイルです(\(bytes) bytes)")
    case 1024..<(1024 * 1024):
        let kb = bytes / 1024
        print("中程度のファイルです(\(kb) KB)")
    case (1024 * 1024)..<(1024 * 1024 * 1024):
        let mb = bytes / (1024 * 1024)
        print("大きなファイルです(\(mb) MB)")
    case (1024 * 1024 * 1024)...:
        let gb = bytes / (1024 * 1024 * 1024)
        print("とても大きなファイルです(\(gb) GB)")
    default:
        print("無効なファイルサイズです")
    }
}

// 使用例
describeFileSize(bytes: 500)        // 小さなファイルです(500 bytes)
describeFileSize(bytes: 2048)       // 中程度のファイルです(2 KB)
describeFileSize(bytes: 5242880)    // 大きなファイルです(5 MB)

曜日に応じた処理

import Foundation

func getTodayMessage() {
    let today = Calendar.current.component(.weekday, from: Date())
    
    switch today {
    case 1:  // 日曜日
        print("🌅 日曜日!ゆっくり休んでください")
    case 2:  // 月曜日
        print("💪 月曜日!新しい週の始まりです")
    case 3:  // 火曜日
        print("🔥 火曜日!エネルギッシュに行きましょう")
    case 4:  // 水曜日
        print("💧 水曜日!週の真ん中です")
    case 5:  // 木曜日
        print("🌳 木曜日!もう少しで週末ですね")
    case 6:  // 金曜日
        print("🎉 金曜日!週末まであと少し!")
    case 7:  // 土曜日
        print("🎈 土曜日!楽しい週末の始まりです")
    default:
        print("❓ 不明な曜日です")
    }
}

よくあるミスと対策

ミス1:defaultを忘れる

let number = 5

// ❌ エラーになる
/*
switch number {
case 1:
    print("1")
case 2:
    print("2")
// defaultがないとコンパイルエラー
}
*/

// ✅ 正しい
switch number {
case 1:
    print("1")
case 2:
    print("2")
default:
    print("1でも2でもない")
}

ミス2:空のケースを作る

let value = 10

// ❌ エラーになる
/*
switch value {
case 1:
    // 何も書かないとエラー
case 2:
    print("2")
default:
    print("その他")
}
*/

// ✅ 正しい方法1:breakを使う
switch value {
case 1:
    break  // 何もしない
case 2:
    print("2")
default:
    print("その他")
}

// ✅ 正しい方法2:コメントを入れる
switch value {
case 1:
    () // 何もしない(空のタプル)
case 2:
    print("2")
default:
    print("その他")
}

ミス3:範囲の重複

let score = 85

// ❌ 範囲が重複している
/*
switch score {
case 80...90:
    print("良い")
case 85...95:  // 85-90が重複している
    print("とても良い")
default:
    print("その他")
}
*/

// ✅ 正しい(重複なし)
switch score {
case 90...100:
    print("とても良い")
case 80..<90:
    print("良い")
case 70..<80:
    print("普通")
default:
    print("頑張ろう")
}

ミス4:型の不一致

let input = "123"

// ❌ 型が合わない
/*
switch input {
case 123:       // Stringと比較できない
    print("123")
default:
    print("その他")
}
*/

// ✅ 正しい方法1:文字列として比較
switch input {
case "123":
    print("123の文字列")
default:
    print("その他")
}

// ✅ 正しい方法2:数値に変換してから比較
if let number = Int(input) {
    switch number {
    case 123:
        print("123の数値")
    default:
        print("その他の数値")
    }
} else {
    print("数値ではありません")
}

パフォーマンスとベストプラクティス

switch vs if の使い分け

switchが良い場面

// ✅ 列挙型の場合
enum State {
    case loading, loaded, error
}

switch state {
case .loading:
    showLoading()
case .loaded:
    showContent()
case .error:
    showError()
}

ifが良い場面

// ✅ 複雑な条件の場合
if user.isLoggedIn && user.hasPremium && !user.isBlocked {
    showPremiumContent()
} else if user.isLoggedIn {
    showBasicContent()
} else {
    showLoginScreen()
}

効率的なパターンマッチング

// ✅ 効率的:よく使われるケースを上に
let httpStatus = 200

switch httpStatus {
case 200:           // 最も頻繁
    handleSuccess()
case 404:           // 2番目に頻繁
    handleNotFound()
case 500...599:     // 3番目
    handleServerError()
default:            // その他
    handleOtherStatus()
}

まとめ

Swiftのswitch文の重要ポイント

基本特徴

  1. break不要:各ケースが自動終了
  2. 網羅性必須:すべてのケースをカバー
  3. 空ケース禁止:必ず何らかの処理が必要

強力な機能

  • 範囲マッチング1...10, 10..<20
  • 複数値マッチングcase "a", "e", "i"
  • where句:追加条件の指定
  • パターンマッチング:タプルや列挙型との組み合わせ

使い分けガイド

場面推奨理由
列挙型の分岐switch網羅性チェック
数値範囲の判定switch範囲演算子が便利
複雑な条件if柔軟性が高い
単純な真偽値ifシンプル

コメント

タイトルとURLをコピーしました