本記事について
SwiftUIのデータ管理についてのアウトプットやメモの共有です。
本文に載っているコードは、説明用であり、コピペでは動かないためご容赦ください。
全体像の一部
今回は、参照型のデータオブジェクトを扱う場合のProperty Wrapperについて説明します。
まず全体像としては、以下の通りです。(説明部以外は省略しています。)

参照型のデータを扱う際、以下3つのProperty Wrapperを利用することができます。
@StateObject
@ObservedObject
@EnvironmentObject
これらを利用するには、まずObservableObjectプロトコルの準拠が必要です。
final class HomeViewModel: ObservableObject {
@Published var taskList: [TaskEntity] = [初期値]
}
このようにObservableObjectプロトコルを準拠した際、クラス内のプロパティに@Published
を付与することで、taskList
を監視することができます。
続いて、ObservableObjectを準拠したクラスをもとに、参照型データの扱い方について説明します。
@StateObject
について
全体像でも記載している通り、「参照型のデータオブジェクト、View自身でデータオブジェクトを保持する」の場合は、@StateObjectを利用します。(ちなみに、iOS14からです)
以下に、サンプルコードを示します。
struct HomeView: View {
@StateObject private var viewModel = HomeViewModel()
// レイアウト描画
var body: some View {
Button(action: {
viewModel.taskList.append(TaskEntity(taskName: "皿洗い", taskTime: 1))
}, label: {
Text("追加")
})
//省略
}
}
このサンプルコードの場合、viewModelが監視されているため、Buttonを押した際に、viewModelのtaskListが追加されるという実装になっています。
本来であれば、明示的な更新処理を入れる必要がありますが、SwiftUIのため必要ありません。
@ObservedObject
について
「参照型のデータオブジェクト、親Viewから渡されるデータオブジェクトを保持する」の場合は、@ObservedObject
を利用します。
以下に、サンプルコードを示します。
final class DataSource: ObservableObject {
@Published var taskList: [TaskEntity] = [初期値]
}
struct ParentView: View {
@StateObject private var dataSource = DataSource()
// レイアウト描画
var body: some View {
ChildView(dataSource: dataSource)
}
}
struct ChildView: View {
@ObservedObject var dataSource: DataSource // インスタンス生成は避ける
var body: some View {
VStack {
HStack {
Button(action: {
dataSource.taskList.append(TaskEntity(taskName: taskName, taskTime: 3))
}) {
Text("add")
}
}
.padding()
}
}
}
このように、ParentViewで生成したdataSourceをChildViewに渡します。Childで渡されたdataSourceはObservedObject
で宣言しています。そのため、dataSourceが監視されている状態になります。
@EnvironmentObject
について
「参照型のデータオブジェクト、アプリ全体から渡されるデータオブジェクトを保持する」の場合は、@ObservedObject
を利用します。
例えば、親Viewから一番下の階層のViewにデータを渡そうとした際、@EnvironmentObject
を利用することで階層を飛び越えてデータの監視が可能になります。

以下に、サンプルコードを示します。
struct ParentView: View {
var body: some View {
ChiledView()
}
}
struct ChiledView: View {
var body: some View {
GrandChiledView()
}
}
struct GrandChiledView: View {
@EnvironmentObject var dataSource: DataSource
var body: some View {
VStack {
Button("add") {
dataSource.taskList = TaskEntity(値の代入)
}
Text("Data: \(dataSource.taskList)")
}
}
}
struct GrandChildView_Previews: PreviewProvider {
@StateObject static var dataSource = DataSource()
static var previews: some View {
ParentView().environmentObject(dataSource)
}
}
親Viewに.environmentObject修飾子でデータをセットすることで、どの階層でもデータを監視することが可能となります。
終わりに
今回は以下の記事を参考に学習しました。
https://blog.personal-factory.com/2020/11/14/publish-swiftui-catalog-book/
ぜひ、参考にしてみてください。