Swift

Optionalとエラー処理

if let、guard、Optional chaining、throwsを使い、失敗した入力も安全に扱います。

対象:Swift 6系。基礎はコンソールで学びます。iOSアプリの開発にはmacOSとXcodeが必要です。

01

このページの実行方法

入門のOptionalと関数までを終えた方向けです。例ごとにmain.swiftの全体を置き換え、swift main.swift で実行します。成功例と失敗例の両方を試します。

Windowsでは 入門のコンパイル手順と同じく、main.swiftを書き換えるたびにコンパイルして実行します。

02

guardで早めに戻る

guard は「この条件が満たされないなら終了する」と書く構文です。else側ではreturnなどで現在の処理を抜けます。取り出した値はguardの後でも使えます。

main.swift(全体を置き換え)SWIFT
func greet(_ name: String?) {
    guard let name = name, !name.isEmpty else {
        print("名前を入力してください")
        return
    }
    print("こんにちは、\(name)さん")
}
greet("Mity")
greet(nil)
greet("")
実行結果OUTPUT
こんにちは、Mityさん
名前を入力してください
名前を入力してください
03

Optional chaining

?. を使うと、途中の値がnilならアクセスを中止してnilを返します。?? は最後に表示する代替値を指定します。

main.swift(全体を置き換え)SWIFT
struct Profile { let city: String? }
let profile: Profile? = Profile(city: "東京")
let missing: Profile? = nil
print(profile?.city ?? "未登録")
print(missing?.city ?? "未登録")
実行結果OUTPUT
東京
未登録
04

失敗の理由を分ける

文字列を数値へ変換できない場合と、数値が範囲外の場合を別のエラーにします。呼び出し元はそれぞれに合う説明を表示できます。

main.swift(全体を置き換え)SWIFT
enum AgeError: Error {
    case notNumber
    case outOfRange
}
func parseAge(_ text: String) throws -> Int {
    guard let age = Int(text) else { throw AgeError.notNumber }
    guard (0...130).contains(age) else { throw AgeError.outOfRange }
    return age
}
for text in ["20", "二十", "-1"] {
    do {
        let age = try parseAge(text)
        print("受付: \(age)歳")
    } catch AgeError.notNumber {
        print("整数で入力してください")
    } catch AgeError.outOfRange {
        print("0〜130で入力してください")
    } catch {
        print("予期しないエラーです")
    }
}
実行結果OUTPUT
受付: 20歳
整数で入力してください
0〜130で入力してください
05

try?とdefer

try? は失敗をnilへ変換します。原因の説明が必要な入力処理ではdo-catchを使います。defer は現在のスコープを離れる直前に実行する処理で、途中のreturnやthrowがあっても呼ばれます。

main.swift(全体を置き換え)SWIFT
enum SampleError: Error { case failed }
func load(_ succeed: Bool) throws -> String {
    defer { print("後片付け") }
    if !succeed { throw SampleError.failed }
    return "読み込み完了"
}
let result = try? load(false)
print(result ?? "読み込めませんでした")
実行結果OUTPUT
後片付け
読み込めませんでした
try! は失敗すると停止します。例外を無視して成功したことにするのではなく、呼び出し元に失敗を伝える方針を決めます。
06

練習:数量の検証

文字列から1〜99の数量を取り出す関数を作ってください。"3"は3、"0"と"abc"は「数量が不正です」と表示します。

解答例と結果を確認する

まず自分で書いてから、処理の順番と結果を比べましょう。

main.swift(全体を置き換え)SWIFT
enum QuantityError: Error { case invalid }
func quantity(_ text: String) throws -> Int {
    guard let value = Int(text), (1...99).contains(value) else {
        throw QuantityError.invalid
    }
    return value
}
for text in ["3", "0", "abc"] {
    do { print(try quantity(text)) }
    catch { print("数量が不正です") }
}
実行結果OUTPUT
3
数量が不正です
数量が不正です

公式ドキュメントで確認する:Swift言語ガイド