SwiftのwillSetとdidSet:その概要、構文、およびオブザーバーの仕組み

著者: IT Sectr 公開日: 2026-06-20 読了時間: 9 分

willSetとdidSetは、Swiftのプロパティオブザーバー(property observers)であり、プロパティの値が変更される前後にコードを実行できます。computed propertiesとは異なり、オブザーバーは新しい値を計算せず、変更に反応するだけです。Appleのドキュメント『The Swift Programming Language』(2026年)によると、オブザーバーはデータ検証、インターフェースの同期、コード内の変更のログ記録に不可欠です。

重要なポイント

  • willSet — プロパティの新しい値を保存する前に呼び出される
  • didSet — 新しい値を保存した後に呼び出される
  • newValue — willSet内の暗黙的なパラメーターで、新しい値を含む
  • oldValue — didSet内の暗黙的なパラメーターで、古い値を含む
  • オブザーバー — 初期化中およびinit内では機能しない

SwiftにおけるwillSetとdidSetとは?

willSetとdidSetは、Swiftのプロパティオブザーバー(property observers)であり、stored propertiesの変更を追跡する組み込みのメカニズムです。

手動でのsetter実装やコールバックシステムが必要な他の言語とは異なり、Swiftは変更に反応するための宣言的な構文を提供します。オブザーバーはプロパティ宣言の直後に追加され、別途呼び出す必要はありません。

Apple Developer Documentation(2026年)によると、property observersは任意のクラス、構造体、または列挙型のstored propertiesでサポートされています。computed propertiesでは機能しません。値が格納されないためです。computed propertiesの場合、変更への反応はsetter内で直接実装されます。

willSetが呼び出されるタイミング

willSetは、プロパティに新しい値が割り当てられる直前に呼び出されます。willSet内では、暗黙的なパラメーターnewValueが利用可能で、設定される値を含みます。この時点では、プロパティの現在の値はまだ変更されていません。プロパティを介して直接古い値を読み取ることができます。

Swift Evolution提案SE-0001(2024年)によると、willSetは実際の変更前に検証またはログ記録を行う機能を提供します。willSetで例外がスローされた場合、新しい値は適用されず、オブザーバーはデータ保護メカニズムとして機能します。

didSetが呼び出されるタイミング

didSetは、新しい値が割り当てられた直後に呼び出されます。didSet内では、暗黙的なパラメーターoldValueが利用可能で、変更前の値を含みます。この時点で、プロパティにはすでに新しい値が含まれており、古い値と比較できます。

Swift by Sundell(2025年)によると、didSetは最も人気のあるオブザーバーです。これは、UIの更新、依存フィールドの再計算、変更後のサーバーへのデータ送信などの後処理で最も頻繁に必要とされるためです。

プロパティオブザーバーの構文

Property observersは、プロパティの直後に波括弧を使用して宣言されます。最小限の構文では1つのオブザーバーのみ必要ですが、両方を宣言することもできます。

swift
var score: Int = 0 {
    willSet {
        print("スコアが\(newValue)に変わります")
    }
    didSet {
        print("スコアが\(oldValue)から\(score)に変わりました")
    }
}

両方のオブザーバーはオプションです。willSetのみ、didSetのみ、または両方を指定できます。willSetでは、括弧内に名前を指定してnewValueの名前を変更できます。

swift
var username: String = "guest" {
    willSet(newName) {
        print("\(newName)を設定しようとしています")
    }
    didSet(oldName) {
        print("以前は\(oldName)、現在は\(username)")
    }
}

Swift Language Guide(2026年)によると、パラメーターの名前を変更すると、特に長い名前を持つ大規模プロジェクトでプロパティとオブザーバーを使用する場合に、コードの可読性が向上します。

willSetとdidSetの仕組み

実行順序は厳密に定義されています。最初にwillSet(古い値が利用可能)、次に代入、次にdidSet(新しい値が利用可能)です。

swift
class Temperature {
    var celsius: Double = 0.0 {
        willSet {
            print("気温が\(celsius)から\(newValue)に変わります")
        }
        didSet {
            if celsius > 100.0 {
                print("沸点を超えました!")
            }
        }
    }
}

重要な制限事項:インスタンス作成時のプロパティ初期化中はオブザーバーは呼び出されません。init内での値の代入はwillSetとdidSetをトリガーしません。これにより、構築段階での望ましくない副作用が防止されます。

Apple Swift Blog(2025年)によると、この動作は、コンストラクターでもsetterが呼び出される多くの他の言語とは異なります。Swiftは安全性を選択しており、オブザーバーはオブジェクトの初期化が完了した後にのみ動作を開始します。

ネストされた呼び出しと再帰

didSet内でプロパティを変更すると、オブザーバーが繰り返し呼び出される可能性があります。Swiftは再帰をブロックしません。プログラマーが手動で制御する必要があります。

swift
var counter: Int = 0 {
    didSet {
        if counter < 5 {
            counter += 1
        }
    }
}

このようなコードは、終了条件が提供されない場合、スタックオーバーフローを伴う完全な再帰を引き起こします。Stack Overflow Swift Community(2025年)によると、これはproperty observersを扱う際に初心者が犯す最も一般的な間違いの1つです。

構造体のプロパティのオブザーバー

構造体は、stored propertiesに対してwillSetとdidSetを制限なくサポートしています。構造体は値型であり、mutatingメソッド内でのプロパティの変更もオブザーバーをトリガーすることを覚えておくことが重要です。

swift
struct Point {
    var x: Double = 0.0 {
        didSet {
            print("Xが\(x)に変わりました")
        }
    }
    var y: Double = 0.0 {
        didSet {
            print("Yが\(y)に変わりました")
        }
    }
}

var point = Point()
point.x = 5.0

実際のプロジェクトでのオブザーバーの使用

プロパティオブザーバーは、SwiftプロジェクトでUI同期からデータ検証やログ記録まで、幅広いタスクに使用されています。

値の検証

didSetを使用すると、無効な値が設定された直後にロールバックまたは修正できます。これにより、Objective-Cの煩雑なsetterが不要になり、モデルレベルでのデータ整合性が確保されます。

swift
var age: Int = 0 {
    didSet {
        if age < 0 || age > 150 {
            age = oldValue
        }
    }
}

UI同期

関連するプロパティが変更されると、別途更新メソッドを呼び出すことなく、インターフェース要素を自動的に更新できます。

swift
var userName: String = "" {
    didSet {
        nameLabel.text = userName
    }
}

変更のログ記録

willSetは、デバッグや監査を目的としたログ記録に便利です。適用する前に時刻と新しい値を記録できるため、ログに分析用の元データが含まれていることが保証されます。

objc.io(2025年)によると、property observersは、単方向データフローを採用するアーキテクチャで特に有用であり、すべてのプロパティ変更が記録され、後で状態を再現できます。

computed propertiesとの比較

Computed propertiesはその場で値を計算し、ストレージを持ちません。一方、willSetとdidSetは実際の値を持つstored propertyで動作します。

特性Property ObserversComputed Properties
値の保存ありなし
変更時にコードを実行ありなし
宣言タイプvarvar (get/set)
アクセスパラメーターnewValue、oldValuenewValue(set内)
初期化初期値が必要不要

主な違い:computed propertyはアクセスごとに値を計算しますが、property observerは既存の値の変更に反応します。どちらを選ぶかは意味論に基づきます。プロパティが他のデータから派生する場合はcomputedを、変更を監視する必要がある独立した値の場合はwillSet/didSetを使用します。

使用時の一般的な間違い

最も一般的な間違いは、終了条件なしでのdidSetの再帰呼び出しです。didSet内でのプロパティ変更ごとにオブザーバーが再度トリガーされ、無限ループに陥ります。

2番目に一般的な間違いは、letプロパティにオブザーバーを使用しようとすることです。letは定数であるため、Swiftコンパイラーがエラーを出力します。

3番目の間違いは、初期化中にオブザーバーが呼び出されないことを無視することです。init内でwillSetが起動することを期待する開発者は、予期しない動作に遭遇します。

4番目の問題は、拡張機能内のプロパティにオブザーバーを適用することです。Swiftは、拡張機能内のstored propertiesにwillSet/didSetを追加することを禁止しています。

Ray Wenderlich(2025年)によると、これらの制限を理解することで、早期にバグを回避でき、Swiftコードの予測可能性が向上します。

よくある質問

willSetをdidSetなしで使用できますか?

はい。オブザーバーはオプションです。willSetのみ、didSetのみ、または両方を同時に宣言できます。

拡張機能内でオブザーバーは機能しますか?

いいえ。Swiftは、拡張機能内のstored propertiesにwillSetとdidSetを追加することを禁止しています。オブザーバーは元の型定義でのみ宣言されます。

init内でプロパティを変更するとオブザーバーは呼び出されますか?

いいえ。初期化中はオブザーバーは呼び出されません。これは、オブジェクト構築段階での副作用を防ぐ保護メカニズムです。

willSetはcomputed propertyのsetterとどう違いますか?

willSetは、値が変更される前に実行されるstored propertyオブザーバーです。computed propertyのsetterは新しい値を計算する方法であり、既存の値を監視するものではありません。

didSet内でプロパティを変更するとどうなりますか?

繰り返しdidSetを呼び出すと再帰が発生します。終了条件がない場合、スタックオーバーフローが発生してプログラムが終了します。

まとめ

  • willSet — プロパティ変更前に実行されるオブザーバー、newValueにアクセス可能
  • didSet — プロパティ変更後に実行されるオブザーバー、oldValueにアクセス可能
  • 構文 — オブザーバーはプロパティ宣言後に波括弧内で宣言する
  • 初期化 — オブザーバーはinit内およびデフォルト値設定時には機能しない
  • 用途 — 検証、UI同期、ログ記録、変更監査
  • 再帰 — didSet内でのプロパティ変更には明示的な終了条件が必要
  • Computed — computed propertiesと混同しないこと:オブザーバーは監視し、computedは計算する

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください