SwiftDataにおける ModelContainer と ModelContext の整理¶
このドキュメントでは、SwiftData における ModelContainer と ModelContext の役割と関係を整理します。
特に、WPF / MVVM の感覚に寄せながら理解しやすい形でまとめます。
結論¶
ModelContainer- DBの土台
-
アプリ全体で使うデータ保存の構成や保存先を管理するもの
-
ModelContext - DB操作の窓口
- 追加・取得・更新・削除などを実際に行うための作業用オブジェクト
つまり、
ModelContainerは データベースの世界そのものModelContextは その世界に対して作業する手
というイメージです。
まずはざっくり理解する¶
ModelContainer¶
ModelContainer は、SwiftData における永続化の親玉です。
例えば以下のようなことを管理します。
- どの
@Modelを扱うか - どこに保存するか
- メモリ上だけにするか、ディスクに保存するか
- 永続化の設定全体
ModelContext¶
ModelContext は、ModelContainer の上で実際にデータ操作を行うためのオブジェクトです。
例えば以下のような操作を行います。
- insert
- fetch
- delete
- save
WPF / MVVM でたとえると¶
完全一致ではありませんが、感覚としては次のように考えると分かりやすいです。
ModelContainer- DBの構成、保存先、スキーマなどを含む永続化の土台
-
WPFでいうと
App.xaml.csで用意する DB基盤 や 永続化の親設定 に近い -
ModelContext - 実際にデータを操作する作業窓口
- WPFでいうと
DbContextや Repository が使う操作用インスタンスに近い
MyApp.swift で ModelContainer を設定する意味¶
例えば、以下のように書きます。
import SwiftUI
import SwiftData
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
HomeView()
}
.modelContainer(for: Alarm.self)
}
}
この .modelContainer(for: Alarm.self) は、
Alarmを永続化対象として登録する- SwiftUI の View 階層に
ModelContainerを設定する - 関連する
modelContextを View 側で使えるようにする
という意味を持ちます。
つまり、MyApp.swift で ModelContainer を設定すると、
その配下の View で SwiftData を扱えるようになります。
View ではどう使うのか¶
View では、modelContext を environment から受け取れます。
import SwiftUI
import SwiftData
struct HomeView: View {
@Environment(\.modelContext) private var modelContext
@Query private var alarms: [Alarm]
var body: some View {
List(alarms) { alarm in
Text(alarm.label)
}
}
}
このときのポイントは、
-
@Environment(\.modelContext)
→ データ操作用の窓口を受け取る -
@Query
→ 永続化されたデータを取得する
ということです。
よくある誤解¶
誤解¶
modelContext を初期化すると、その初期化したものだけ扱える
実際の理解¶
より正確には、
その modelContext が、どの ModelContainer に属しているかで操作対象が決まる
です。
つまり本質的に重要なのは、
ModelContext単体ではなく- その context がどの container にぶら下がっているか
です。
具体的な理解¶
例えば MyApp.swift で以下を設定したとします。
.modelContainer(for: Alarm.self)
このとき、配下の View で取得する modelContext は、
Alarm を扱う ModelContainer に属する ModelContext
です。
そのため、この modelContext を使って、
Alarmの追加Alarmの取得Alarmの更新Alarmの削除
ができます。
Context を新しく作るとどうなるか¶
例えば、ある ModelContainer から新しく ModelContext を作ることもできます。
let context = ModelContext(container)
この場合、
containerは同じcontextは別
です。
つまり、
同じ DBの土台に対する、別の作業窓口
になります。
したがって、
-
新しい context を作ったから別のデータしか扱えない
ではなく、 -
同じ container に属しているなら、同じデータ基盤を扱う別の操作窓口
と理解するのが正しいです。
何が変わると扱える対象が変わるのか¶
扱える対象が変わるのは、
基本的には 元になっている ModelContainer が変わるとき です。
例えば、
- A用の
ModelContainer - B用の
ModelContainer
があるとします。
その場合、
- A由来の
ModelContext - B由来の
ModelContext
では、扱う対象や保存先の世界が異なります。
つまり、
ModelContext の違いより、どの ModelContainer に属しているかの方が本質的に重要
です。
一言でまとめると¶
ModelContainer¶
DBの土台・永続化の親玉
ModelContext¶
そのDBを操作するための窓口
あなた向けの理解しやすい表現¶
ModelContainer- データベースそのものの構成と保存設定
-
アプリ全体のデータ管理の土台
-
ModelContext - そのデータベースに対する操作用セッション
- 実際に追加・更新・削除・取得を行うための窓口
したがって、
modelContextを初期化すると、その初期化したものだけ扱える
というよりは、
その
modelContextが接続しているModelContainerのデータを扱える
と理解するのが正確です。
実務での感覚¶
小規模アプリでは、まず次の理解で十分です。
MyApp.swiftでModelContainerを設定する- 各 View で
@Environment(\.modelContext)を受け取る - その
modelContextを使ってデータを操作する
例えば以下のような流れです。
import SwiftUI
import SwiftData
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
HomeView()
}
.modelContainer(for: Alarm.self)
}
}
import SwiftUI
import SwiftData
struct HomeView: View {
@Environment(\.modelContext) private var modelContext
@Query private var alarms: [Alarm]
var body: some View {
List(alarms) { alarm in
Text(alarm.label)
}
}
}
まとめ¶
ModelContainerは DBの土台ModelContextは DB操作の窓口- View は
@Environment(\.modelContext)で context を受け取れる - 新しく context を作っても、同じ container 由来なら同じデータ基盤を扱う
- 本質的に重要なのは どの container に属する context か
SwiftData を最初に理解する段階では、
ModelContainer = DBの世界
ModelContext = その世界を操作する手
と覚えるとかなり分かりやすいです。