コンテンツにスキップ

SwiftDataにおける ModelContainer と ModelContext の整理

このドキュメントでは、SwiftData における ModelContainerModelContext の役割と関係を整理します。

特に、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.swiftModelContainer を設定すると、
その配下の 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 のデータを扱える

と理解するのが正確です。


実務での感覚

小規模アプリでは、まず次の理解で十分です。

  1. MyApp.swiftModelContainer を設定する
  2. 各 View で @Environment(\.modelContext) を受け取る
  3. その 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 = その世界を操作する手

と覚えるとかなり分かりやすいです。