Swiftのguard文を理解するための10本ノック

Swiftのguard文の使い方を実例とともに紹介します。各例では、実際のコードと、その動作やポイントについて、分かりやすく解説しています。ぜひ参考にして、あなたのプロジェクトにも取り入れてみてください！

# guard文の基本
Swiftのguard文は、コードの冒頭で条件をチェックし、条件が満たされなかった場合に早期に処理を中断（早期リターン）するための便利な制御構造です。これにより、メインのロジック部分をネストせずに記述でき、コードの可読性と保守性が向上します。

``` Swift
guard 条件 else {
    // 条件が成立しない場合の処理（return, break, continue, throw などで早期に抜ける必要があります）
    print("条件不成立")
}
// ここに、条件を満たした場合の処理を書く
print("条件成立")
```
guard文では、指定した条件がfalseの場合にelseブロックが必ず実行され、その中で現在のスコープから抜け出す（例えば、関数からreturnする）必要があります。これにより、guard文以降のコードは、必ず条件が成立していることが保証されるため、安全に処理を進めることができます。

# 使い所
* ### オプショナル値の安全な取り扱い：
ユーザーからの入力や外部APIからのデータなど、予期しないnilが発生する可能性がある場合に、guard文で早めにチェックして安全にアンラップする。

* ### 入力の検証：
関数の引数が期待する条件（例えば、特定の範囲内の数値や正しいフォーマットの文字列など）を満たしているかどうかを検証し、問題があれば処理を中断する。

* ### ループ内での条件チェック：
ループの各イテレーションで条件を確認し、条件を満たさない場合にそのループをスキップする（continueなどを使って）。


## それではコード例を見ていきましょう！

# 1: オプショナルの値を安全にアンラップする
```Swift
func greet(name: String?) {
    guard let unwrappedName = name else {
        // nameがnilでないことを確認し、非オプショナルな変数unwrappedNameに束縛する
        print("名前がありません")
        return
    }
    // guardを通過した後は、unwrappedNameは必ず値を持っている
    print("こんにちは、\(unwrappedName)さん！")
}

greet(name: "太郎")  // 出力: こんにちは、太郎さん！
greet(name: nil)     // 出力: 名前がありません。
```
#### 解説
この例では、guard文を使ってオプショナルのアンラップと、num > 0という条件の両方を同時にチェックしています。いずれかの条件が満たされなければ、elseブロックが実行され、エラーメッセージを出力して関数を終了します。

# 2: オプショナルのアンラップと条件のチェックを同時に行う
``` Swift

func checkNumber(_ number: Int?) {
    // numberがnilでなく、かつ正の数であることを確認
    guard let num = number, num > 0 else {
        print("有効な正の数を入力してください。")
        return
    }
    print("入力された数は \(num) です。")
}

checkNumber(5)    // 出力: 入力された数は 5 です。
checkNumber(-3)   // 出力: 有効な正の数を入力してください。
checkNumber(nil)  // 出力: 有効な正の数を入力してください。
```

#### 解説
この例では、guard文を使ってオプショナルのアンラップと、num > 0という条件の両方を同時にチェックしています。いずれかの条件が満たされなければ、elseブロックが実行され、エラーメッセージを出力して関数を終了します。



# 3: 複数のオプショナルを一度にアンラップする
``` swift

func displayUserInfo(name: String?, age: Int?) {
    // nameとageの両方が有効で、かつageが0以上であることを確認
    guard let validName = name, let validAge = age, validAge >= 0 else {
        print("有効な名前と年齢を提供してください。")
        return
    }
    print("\(validName)さんは \(validAge) 歳です。")
}

displayUserInfo(name: "花子", age: 25)  // 出力: 花子さんは 25 歳です。
displayUserInfo(name: nil, age: 25)      // 出力: 有効な名前と年齢を提供してください。
displayUserInfo(name: "花子", age: -5)    // 出力: 有効な名前と年齢を提供してください。
```
#### 解説
ここでは、2つのオプショナル変数nameとageを同時にアンラップし、さらにageが負の値でないかをチェックしています。どれか1つでも条件を満たさなければ、elseブロックが実行され、関数は早期に終了します。



# 4: 配列が有効かつ空でないかをチェックする
``` swift
func printFirstElement(of array: [Int]?) {
    // 配列がnilでなく、かつ空でないことを確認
    guard let array, !array.isEmpty else {
        print("配列がくうか無効です")
        return
    }
    print("配列の最初の要素は\(array[0])です。")
}

printFirstElement(of: [10, 20, 30])  // 出力: 配列の最初の要素は 10 です。
printFirstElement(of: [])           // 出力: 配列が空か無効です。
printFirstElement(of: nil)          // 出力: 配列が空か無効です。
```
#### 解説
この関数は、引数として渡される配列がnilでなく、かつ空でないかをguard文でチェックしています。条件に合わない場合は、エラーメッセージを出力して関数を終了します。正常な場合のみ、配列の最初の要素を出力します。


# 5: ループ内での条件チェックにguardを使用する
``` swift
let numbers = [10, -5, 20, 0, 15]
for num in numbers {
    // 正の数であるかをチェック。そうでなければこのイテレーションをスキップする
    guard num > 0 else {
        print("\(num) は正の数ではありません。スキップします。")
        continue
    }
    print("\(num) は正の数です。")
}
/*
実行結果
10 は正の数です。
-5 は正の数ではありません。スキップします。
20 は正の数です。
0 は正の数ではありません。スキップします。
15 は正の数です。
*/
```

#### 解説
この例では、配列numbersの各要素に対して、guard文を使って値が正の数かどうかをチェックしています。もしnumが正の数でなければ、elseブロック内でメッセージを出力し、continueを使って次のループにスキップします。条件を満たす場合のみ、その数値に対して処理を続けます。

# 6: イニシャライザ内での条件チェック（failable initializer）
``` swift
struct Person {
    let name: String
    let age: Int
    
    // イニシャライザでオプショナルをチェックし、条件を満たさなければ初期化に失敗（nil）とする
    init?(name: String?, age: Int?) {
        guard let name = name, let age = age, age >= 0 else {
            return nil
        }
        self.name = name
        self.age = age 
    }
}

if let person = Person(name: "太郎", age: 30) {
    print("Person: \(person.name), \(person.age)")
} else {
    print("無効な値が渡されました。")
}

// Person: 太郎, 30



if let person = Person(name: nil, age: 30) {
    print("Person: \(person.name), \(person.age)")
} else {
    print("無効な値が渡されました。")
}

// 無効な値が渡されました
```

#### 解説
この例では、構造体Personのイニシャライザ内でguard文を用いて、nameとageが有効かつageが0以上であるかをチェックしています。条件を満たさなければnilを返すため、初期化が失敗します。guard文により、初期化前に必要な前提条件を明確に確認できます。

# 7: guard文とエラースローによる例外処理
``` swift
enum LoginError: Error {
    case invalidCredentials
}

func login(username: String?, password: String?) throws {
    // ユーザー名とパスワードが非nilかつ空文字でないことを確認
    guard let username = username, let password = password, !username.isEmpty, !password.isEmpty else {
        throw LoginError.invalidCredentials 
    }
    print("ユーザー \(username) がログインしました。")
}

do {
    try login(username: "user123", password: "secuewPassword")
    try login(username: nil, password: "password")
} catch {
    print("ログインエラー： \(error)")
}

// ユーザー user123 がログインしました。
// ログインエラー： invalidCredentials
```

#### 解説
この例では、guard文を使ってログインに必要なユーザー名とパスワードが正しく提供されているかをチェックしています。条件を満たさなければ、LoginError.invalidCredentialsエラーをスローして、呼び出し側にエラーを伝えます。エラーハンドリングと組み合わせることで、より堅牢なコードが実現できます。

# 8: 非同期処理内でのguard文による前提チェック
``` swift
import Foundation

func fetchData(from urlString: String, completion: @escaping (Result<String, Error>) -> Void) {
    // URL文字列から有効なURLオブジェクトを生成できるか確認
    guard let url = URL(string: urlString) else {
        completion(.failure(NSError(domain: "InvalidURL", code: -1, userInfo: nil)))
        return
    }
    
    // 擬似的な非同期処理（実際のネットワーク処理の代わり）
    DispatchQueue.global().async {
        // ここでは常に成功と仮定
        let result = "データ取得成功: \(url.absoluteString)"
        completion(.success(result))
    }
}

fetchData(from: "invalid_url") { result in
    switch result {
    case .success(let data):
        print(data)
    case .failure(let error):
        print("エラー: \(error.localizedDescription)")
    }
}
```
#### 解説
この例では、非同期処理を行う関数内でguard文を使用し、URL文字列が有効なURLに変換できるかをチェックしています。無効な場合は、直ちにcompletionクロージャをエラー結果とともに呼び出して処理を終了しています。非同期処理における早期リターンのパターンとして有用です。


# 9: 数値が特定の範囲内にあるかのチェック
``` swift

func processScore(_ score: Int) {
    // スコアが0から100の範囲内であることを確認
    guard (0...100).contains(score) else {
        print("スコアは０から１００の範囲でなければなりません")
        return
    }
    print("スコア \(score) を処理中")
}


processScore(85)   // 出力: スコア 85 を処理中...
processScore(150)  // 出力: スコアは0から100の範囲でなければなりません。
```
#### 解説
ここでは、guard文を使って数値が0から100の範囲内にあるかどうかをチェックしています。条件に合わなければエラーメッセージを出力して関数を終了するため、以降の処理は常に有効なスコアを前提に行えます。

# 10: 列挙型のパターンマッチングとguard文の組み合わせ
``` swift
enum ProcessState {
    case ready, running, completed
}

func startProcess(state: ProcessState) {
    // 状態が.readyでなければ早期に処理を終了する
    guard case .ready = state else {
        print("プロセスを開始できません。現在の状態: \(state)")
        return
    }
    print("プロセスを開始します。")
}

startProcess(state: .ready)
// プロセスを開始します

startProcess(state: .running)
// プロセスを開始できません。現在の状態: running

```

#### 解説
この例では、guard文とパターンマッチング（guard case）を組み合わせ、列挙型ProcessStateが期待する状態（.ready）であるかを確認しています。期待する状態でなければ早期に処理を中断し、エラーメッセージを表示することで、後続の処理が不適切な状態で実行されるのを防ぎます。


#
以上の例を通して、guard文がさまざまなシーン（イニシャライザ、エラーハンドリング、非同期処理、範囲チェック、パターンマッチング）でどのように使えるかを理解できるでしょう。guard文を用いることで、条件が満たされない場合の早期退出が容易になり、コードの可読性と安全性が向上します。

# 備考
この記事は、あるチャットでの質疑応答内容をもとにまとめたものです。各種サンプルコードや考え方は、実際のプロジェクトや仕様に合わせて適宜ご調整ください