Swift における「Optional 型」について…

朝の実(Asanomi)アイコン アプリ開発

Swift のコードでは String? や Int? といった、 末尾に ? が付いた型によく出会います。

これらは Optional(オプショナル)型と呼ばれ、 Swift の安全性を支える重要な仕組みの一つです。

一方で、 「なぜ Optional 型が必要なのか」 「なぜそのまま使えないのか」 といった点で、最初は戸惑うことも多いと思います(私もその一人です)。

本記事では、Swift の公式ドキュメントをもとに、 Optional 型の役割と考え方を整理します。 あわせて、Optional を安全に扱うための構文である if let と guard let についても、 それぞれの役割に焦点を当てて解説します。

オプショナル型とは?

Swift の公式ドキュメントでは、Optional 型について次のように説明されています。

You use optionals in situations where a value may be absent.

意訳すると、「Optional 型は値が存在しない可能性がある場面で使用する」となります。

Optional 型は、単に 「nil を代入できる型」 という意味ではありません。
Optional 型は、「値が存在するかどうか」という状態を含めて表現する型です。
そのため、String と String? は 見た目は似ていても、まったく別の型として扱われます。

なぜ Optional 型が必要なのか

Optional 型が必要とされる理由を理解するために、 まずは Java のコードを例に見てみます。

String name = null;
System.out.println(name.length()); // 実行時例外(NullPointerException)が発生する

このコードはコンパイル時には問題ありませんが、 実行時に NullPointerException が発生します。

Java では、 「この値が null かもしれない」という情報が 型として表現されていないため、 実行するまで問題に気づけません。

同じことを Swift で書こうとすると、次のようになります。

let name: String? = nil 
print(name.count) // コンパイルエラー

Swift では、Optional 型の値をそのまま使おうとするとコンパイルエラーになります。
そのため、コードを書いている段階で問題に気づくことができます。

Optional 型を扱うための構文

アンラップ(unwrap)

Optional 型の値は、そのままでは中身を使うことができません。
これは、値が存在しない可能性(nil)を無視したまま処理が進んでしまうことを、
Swift がコンパイル時に防ぐようになっているからです。

そのため Swift では、
Optional 型の値を実際に使う前に、
「中身が存在することを確認し、取り出す」必要があります。

この「Optional の中身を取り出す操作」のことを、
アンラップ(unwrap) と呼びます。

Swift では、このアンラップを安全に行うための構文として、
if let や guard let が用意されています。

補足

公式ドキュメントでは、特に if let , guard let のアンラップを安全に行う構文を Optional Binding と呼んでいます。

if let

if let は、 条件分岐の中で Optional の中身を使いたい場合に使用します。

func printLength(text: String?) { 
    if let unwrappedText = text {
        // このブロック内では unwrappedText を String 型として使用可能
        print(unwrappedText.count) 
    } 
}

この例では、text が nil でない場合のみ、
if ブロックの中で unwrappedText を 通常の String として扱えます。

guard let

guard let は、 値が存在することを「処理を続けるための前提条件」として扱いたい場合に使用します。

func printLength(text: String?) {
    guard let unwrappedText = text else {
        return
    }
    // ここ以降は unwrappedText を String 型として使用可能

    print(unwrappedText.count)
}

この例では、text が nil の場合はその時点で処理を終了し、
nil でない場合のみ、以降の処理が続行されます。
そのため、guard の後では unwrappedText を 通常の String として
関数の残り全体で使用できます。

実際の使用例(朝の実)

私が現在開発中の朝専用SNSアプリ「朝の実」では、CloudKit の CKRecord をアプリ内モデルに変換する処理において guard let を使用しています。

CKRecord は、「キーに対応する値が存在しない」「想定していた型と異なる」といったケースが起こり得ます。そのため、モデル生成の前提として 必須フィールドがすべて正しく取得できること を保証する必要があります。

そこで、各フィールドの取得に as?(失敗すると nil を返すキャスト)を使い、その結果を guard let でまとめて検証・アンラップしています。
guard の条件部に複数の let … = … as? … を並べることで、どれか1つでも失敗した時点で処理を早期に終了できます。

一方、guard を抜けた後は、取得した値が すべて非Optionalとして揃っている状態になるため、以降の初期化処理を安全に記述できます。

import Foundation
import CloudKit

extension Post {
    init(record: CKRecord) throws {
        // as? は「型が合わない / 値がない」場合に nil を返す
        // guard let で必須項目をまとめてアンラップし、欠けていたら早期に失敗とする
        guard
            let id = record[PostRecordKey.id] as? String,
            let authorId = record[PostRecordKey.authorId] as? String,
            let text = record[PostRecordKey.text] as? String,
            let createdAt = record[PostRecordKey.createdAt] as? Date,
            let cheerCount = record[PostRecordKey.cheerCount] as? Int,
            let isDeletedInt = record[PostRecordKey.isDeleted] as? Int,
            let clientTimeZone = record[PostRecordKey.clientTimeZone] as? String
        else {
            throw CloudKitRepositoryError.decodingError
        }

        // guard let を抜けた後は、上の値はすべて「非Optional」として扱える
        self.id = id
        self.authorId = authorId
        self.text = text
        self.createdAt = createdAt
        self.cheerCount = cheerCount
        self.isDeleted = (isDeletedInt != 0)
        self.clientTimeZone = clientTimeZone
    }
}

まとめ

Optional 型は、 「値が存在しない可能性」を 型として明示するための仕組みです。

Swift では、 Optional 型をそのまま使うことはできず、 値が存在することを確認してから扱う必要があります。

if let と guard let は、 Optional 型を安全に扱うために用意された構文であり、 用途に応じて使い分けることが重要です。

sho shimizu

SwiftUI を用いた iOS アプリの個人開発を行っています。
これまでに2本のアプリを App Store にリリースしました。

現在は、朝専用SNS「朝の実(Asanomi)」を開発中です。
アプリを作る中で学んだことや、設計・実装で考えたことを、
あとから振り返れるように技術記事としてまとめています。

sho shimizuをフォローする
アプリ開発技術解説朝の実
シェアする
sho shimizuをフォローする