Swiftのguard文の使い方を実例とともに紹介します。各例では、実際のコードと、その動作やポイントについて、分かりやすく解説しています。ぜひ参考にして、あなたのプロジェクトにも取り入れてみてください!
guard文の基本
Swiftのguard文は、コードの冒頭で条件をチェックし、条件が満たされなかった場合に早期に処理を中断(早期リターン)するための便利な制御構造です。これにより、メインのロジック部分をネストせずに記述でき、コードの可読性と保守性が向上します。
guard 条件 else {
// 条件が成立しない場合の処理(return, break, continue, throw などで早期に抜ける必要があります)
print("条件不成立")
}
// ここに、条件を満たした場合の処理を書く
print("条件成立")
guard文では、指定した条件がfalseの場合にelseブロックが必ず実行され、その中で現在のスコープから抜け出す(例えば、関数からreturnする)必要があります。これにより、guard文以降のコードは、必ず条件が成立していることが保証されるため、安全に処理を進めることができます。
使い所
ユーザーからの入力や外部APIからのデータなど、予期しないnilが発生する可能性がある場合に、guard文で早めにチェックして安全にアンラップする。
関数の引数が期待する条件(例えば、特定の範囲内の数値や正しいフォーマットの文字列など)を満たしているかどうかを検証し、問題があれば処理を中断する。
ループの各イテレーションで条件を確認し、条件を満たさない場合にそのループをスキップする(continueなどを使って)。
それではコード例を見ていきましょう!
1: オプショナルの値を安全にアンラップする
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: オプショナルのアンラップと条件のチェックを同時に行う
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: 複数のオプショナルを一度にアンラップする
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: 配列が有効かつ空でないかをチェックする
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を使用する
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)
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文とエラースローによる例外処理
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文による前提チェック
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: 数値が特定の範囲内にあるかのチェック
func processScore(_ score: Int) {
// スコアが0から100の範囲内であることを確認
guard (0...100).contains(score) else {
print("スコアは0から100の範囲でなければなりません")
return
}
print("スコア \(score) を処理中")
}
processScore(85) // 出力: スコア 85 を処理中...
processScore(150) // 出力: スコアは0から100の範囲でなければなりません。
解説
ここでは、guard文を使って数値が0から100の範囲内にあるかどうかをチェックしています。条件に合わなければエラーメッセージを出力して関数を終了するため、以降の処理は常に有効なスコアを前提に行えます。
10: 列挙型のパターンマッチングとguard文の組み合わせ
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文を用いることで、条件が満たされない場合の早期退出が容易になり、コードの可読性と安全性が向上します。
備考
この記事は、あるチャットでの質疑応答内容をもとにまとめたものです。各種サンプルコードや考え方は、実際のプロジェクトや仕様に合わせて適宜ご調整ください