はじめに (対象読者・この記事でわかること)
この記事は、Swiftでアプリ開発を始めたばかりの方〜中級者向けです。
「if文が入れ子になって読みにくい」「条件が増えるたびにelse ifが増えて頭を抱えている」といった悩みを抱えている方に最適です。
記事を読み終えると、guard文やwhere節、switch文でのパターンマッチングを使って、複雑な条件をフラットで読みやすいコードにリファクタリングできるようになります。さらに「早期return」「条件の否定を避ける」「意味単位で切り出す」といった実践的なテクニックも身につき、チーム開発でも指摘されにくいコードが書けるようになります。
前提知識
この記事を読み進める上で、以下の知識があるとスムーズです。 - Swiftの基本的な文法(変数・定数、型推論) - 関数の定義と呼び出し方 - Optional型の基礎(nilチェックの必要性を理解していること)
入れ子地獄からの脱却:複雑なif文が生まれる背景
Swiftのif文は直感的に書けますが、ビジネスロジックが絡むと一瞬でネストが深くなります。例えば、ECアプリの決済処理で「クレジット残が不足していない」「有効期限が切れていない」「3Dセキュア認証が完了している」「会員ランクがGold以上」という4つを同時にチェックしなければならないケースを考えてみましょう。
素朴にif文を重ねると、以下のような「カギ括弧の山」が誕生します。
Swiftif payment.credit >= price { if payment.expirationDate > Date() { if payment.is3DSecure { if user.rank.rawValue >= Rank.gold.rawValue { processOrder() } else { showError("ランク不足") } } else { showError("3Dセキュア未完了") } } else { showError("有効期限切れ") } } else { showError("残高不足") }
このコードは動作しますが、可読性の低さと保守の難しさが問題です。新しい条件が1つ増えるたびに階層が深くなり、テストケースも指数的に増えます。そこでSwiftが提供する「複雑な条件を読みやすく書くための仕組み」を活用していきましょう。
複雑な条件を1行で表現する:guard・where・switchの実践テクニック
Step1: guard文で「早期return」を実現する
guard文は、条件を満たさない場合に即座に処理を中断する構文です。ネストを深くせずに「想定外の状態」を排除できるため、ビジネスロジックをフラットに保てます。先ほどの決済処理をguardで書き直すと、以下のようにスッキリします。
Swiftfunc checkout(price: Int) { guard payment.credit >= price else { showError("残高不足") return } guard payment.expirationDate > Date() else { showError("有効期限切れ") return } guard payment.is3DSecure else { showError("3Dセキュア未完了") return } guard user.rank >= .gold else { showError("ランク不足") return } processOrder() }
guard文のポイントは「条件を満たさないケースを先に潰す」ことで、メイン処理を右にずらさないことにあります。これにより、コードを読む人は「何がダメなのか」を即座に把握でき、メイン処理に集中できます。
Step2: where節で「条件を1行に畳み込む」
複数の条件を「&&」で繋ぐと横スクロールが必要になるほど長くなりがちです。そこでwhere節を使うことで、意味単位で条件を畳み込めます。例えば、サーバから取得したJSONをモデル化する際、以下のようなコードがあったとします。
Swiftif let name = json["name"] as? String { if !name.isEmpty { if name.count <= 30 { self.name = name } } }
このコードをwhere節を使って整理すると、以下のように1行で表現できます。
Swiftif let name = json["name"] as? String, !name.isEmpty, name.count <= 30 { self.name = name }
さらに、where節はswitch文でも使えるため、列挙型と組み合わせると強力です。例えば、APIのレスポンスコードを扱うケースを考えます。
Swiftswitch response.status { case .ok where !response.body.isEmpty: showData(response.body) case .ok: showEmptyMessage() case .unauthorized: retryLogin() case .notFound: showNotFound() default: showGenericError() }
where節を使うことで、「ステータスコードが200かつボディが空でない」という複雑な条件を、1行で読みやすく表現できています。
Step3: switch文で「型と値を同時に分岐」する
if文では扱いづらい「型による分岐」を、switch文のパターンマッチリングで簡潔に書けます。特に、SwiftのEnumは連想値(associated value)を持てるため、1つのswitch文で「型」と「その中身」を同時にチェックできます。例えば、決済手段が「クレジットカード」「コンビニ決済」「電子マネー」の3種類あり、それぞれで必要な情報が異なるケースを考えます。
Swiftenum PaymentMethod { case credit(number: String, securityCode: String) case convenience(store: StoreType, phone: String) case emoney(type: EmoneyType, id: String) } func validate(_ method: PaymentMethod) -> Bool { switch method { case .credit(let number, let securityCode) where number.count == 16 && securityCode.count == 3: return true case .convenience(let store, let phone) where store != .unknown && phone.count == 11: return true case .emoney(let type, let id) where type != .unknown && !id.isEmpty: return true default: return false } }
switch文では、where節を使って「連想値の中身」まで1行で検証できます。これをif文で書こうとすると、ネストが深くなるうえに、どの条件でfalseになったのかを判別するのが難しくなります。
ハマった点:「guard let」で変数名を間違えるとコンパイルエラーが分かりにくい
guard文を使い始めた頃、以下のようなミスをよく犯していました。
Swift// 間違い:変数名を typo guard let name = json["name"] as? String else { return } guard !name.isEmpty else { return } print(naem) // コンパイルエラー: Use of unresolved identifier 'naem'
通常のif letでは、スコープ内で変数が有効なため、タイポに気づきやすいのですが、guard文では「その後の全てのスコープ」で有効になるため、タイポした変数名を使った場所でエラーが出ます。これが原因で「何故エラーになるのか」が分かりにくく、ハマりました。
解決策:Xcodeの「Fix All in Scope」を活用する
上記のような「変数名のタイポ」は、Xcodeの「Fix All in Scope」(Control+Option+Command+F)で一括修正できます。さらに、Swift 5.7から導入された「アンラップ省略構文」を使うと、変数名を繰り返さずに済み、タイポの機会自体を減らせます。
Swift// Swift 5.7以降:アンラップ省略 guard let json["name"] as? String, !name.isEmpty // nameが自動的に利用可能 else { return }
また、変数名を「省略形ではなく単語単位でフルスペル」にすることで、タイポの際に赤波線が入りやすくなり、早期発見に繋がります。例えば「nm」ではなく「userName」とすることで、タイポした際に「unUserName」となり、視覚的に異常を察知しやすくなります。
まとめ
本記事では、Swiftで複雑なif文を整理するための3つのテクニックを紹介しました。
- guard文で「早期return」を実現し、ネストをフラットに
- where節で「条件を意味単位で畳み込み」横スクロールを抑制
- switch文のパターンマッチングで「型と値を同時に分岐」
この記事を通して、読みにくかったビジネスロジックが、スッキリとした上から下への流れに生まれ変わったのではないでしょうか。
次回は、「Swift Concurrencyで非同期処理の制御構造を読みやすくする」について、実践的なサンプルを交えて解説予定です。
参考資料
- Swift公式言語ガイド - Control Flow
- Swift公式言語ガイド - Enumerations
- Swift Evolution 0345:
if letshorthand for unwrapped optionals
