👽

【Swift UI】@ObservedObjectと@StateObjectの違いについて 2023/12

2023/12/30に公開

ObservableObjectとは

ObservableObjectはプロトコルでありクラスに継承して使用します。

機能は、@publishedをつけた変数の値を監視して、変更されたらviewを更新します。
つまり、データバインディングをしてくれるプロトコルです。

@ObservedObjectと@StateObjectについて

@ObservedObjectと@StateObjectは、property wrapperです。つまり変数の値をを監視してくれます。
基本的にはviewで定義し、監視対象のデータやモデルが更新されるとviewを変更します。
@ObservedObjectと@StateObjectの違いは監視する範囲とライフサイクルです。
では、詳しく見ていきましょう!!

@ObservedObjectの使い方

  1. まず、ObservedObjectを継承したクラスを用意し、変数に@publishedをつけ監視対象にします。
    下記画像だと、
    DataModelクラスにObservedObjectプロトコルを継承させており、
    nameとisEnabledに@publishedをつけ変数の値を監視対象にしています。

    変数の前に@publishedをつけ忘れると値が変わってもviewは更新されません。

  2. 次に、viewで変数に@ObservedObjectを宣言します。宣言することで、変数を監視します。
    下記画像では、変数var model: DataModelに@ObservedObjectをつけることで、DataModelの変更を監視し、変更されるとViewを再描画します。
    DataModelのnameを変えているので、Textで描画されるnameが「Some Name」から「勉強するマン」になります。

@ObservedObjectのライフサイクル

@ObservedObjectと@StateObjectの最大の違いがこのライフサイクルです。
つまり、インスタンス化したデータの内容を保持できる期間が違います。
@ObservedObjectと@StateObjectを使う最大のメリットは他のViewと情報を共有することです。

親ビューを作って@ObservedObjectのライフサイクルを見ていきましょう!

親ビューからzennViewを読んだ場合、@ObservedObjectのライフサイクルは親Viewのbodyが更新される度です。
つまり、下記画像だとoyaViewが何らかのアクションで再描画されるとzennViewのButtonを押してnameを「勉強するマン」に変えていても「Some Name」に戻ります。

要約

「@ObservedObjectのライフサイクルは親ビューが表示されてから消えるまで」

@StateObjectの使い方

@ObservedObjectと同じです。
監視したい変数があるクラスにObservedObjectを継承させて、変数に@publishedをつける。
そして、Viewの変数に@StateObjectを宣言する。

@StateObjectのライフサイクル

@StateObjectはViewが表示されてから非表示になるまでです。
つまり、oyaViewが再描写されようがzennViewに影響はございません。
zennViewのButtonを押してnameを「勉強するマン」に変えているとoyaViewが再描画されてもnameが「勉強するマン」のままです。
ただし、今回の例だとoyaViewの中にzennViewを作ってDataModelをインスタンス化しているので、oyaViewを消すとzennViewも消えて変更がリセットされます。

要約

「@StateObjectのライフサイクルは自身のViewが表示されてから非表示になるまで」

@ObservedObjectと@StateObjectの違いまとめ

使い方
違いはない。
@ObservedObjectと@StateObjectで同じ、Viewで宣言するときにどっちをつけるか

ライフサイクル

@ObservedObject @StateObject
親ビューが表示されてから消えるまで 自身のViewが表示されてから非表示になるまで

@ObservedObjectを使うときの注意点

上記の使い方だと、自身のView(znnView)でDataModelをインスタンス化しているので、親ビューが再描写されるとライフサイクルが終了し値がリセットされる。
そのため、 @ObservedObjectではインスタンス化するのではなく、親Viewから渡されるデータを参照する場合に使うべきだと思います。

@StateObject var model: DataModelで、DataModelを参照する変数を宣言し、oyaViewでインスタンス化したDataModelを代入する。

自身のビュー 親ビュー

全体コード

雑で申し訳ございませんが、今回説明に使った全体コードを載せます。

Discussion