SwiftDataの .modelContainer(…) と @Environment(\.modelContext) を完全分解


ゴール

@Environment(\.modelContext) には .modelContainer(...) の情報が書かれていないのに、どうして値が入るの?」という疑問を解決するために、Environment仕組みの流れSwiftDataでの使われ方を丁寧に解説します。


1. 大前提:Environment とは?

SwiftUI には Environment(環境値) という仕組みがあります。これは:

  • 親ビューで設定した値を
  • 子ビューに自動的に受け渡す

ための「辞書」のようなものです。キーと値のペアで構成され、ビュー階層を下へ下へと流れていきます。

例:

@Environment(\.colorScheme) var scheme   // ダーク/ライトモードを受け取る
@Environment(\.dismiss) var dismiss     // 画面を閉じるアクションを受け取る

これと同じ仕組みで modelContext も環境値として用意されています。


2. .modelContainer(...) がやっていること

WindowGroup {
    ContentView()
}
.modelContainer(for: Item.self)

このモディファイアは、実際には次の処理をまとめて行っています:

  1. ModelContainer を生成
    • 指定したモデル(Item など)からスキーマを作成。
    • それに基づいて 永続化の土台(ModelContainer) を構築。
  2. ModelContext を生成
    • そのコンテナに紐づく ModelContext(編集用の机) を作成。
  3. Environment にセット
    • EnvironmentValues.modelContext にこの ModelContext を格納。

3. @Environment(\.modelContext) がやっていること

@Environment(\.modelContext) private var context

これは単に:

  • Environment の「辞書」から
  • modelContext というキーを使って
  • 値(ModelContext)を取り出す

という宣言です。

重要ポイント:ここには「保存先はどこ」などの情報は一切書かれていません。
値を「注入」するのはあくまで 親ビュー側の .modelContainer(...) の役目です。


4. 流れを図にすると

App/Scene
   .modelContainer(for: Item.self)
   │
   └─ EnvironmentValues[.modelContext] = 生成された ModelContext

子ビュー
   @Environment(\.modelContext)
   │
   └─ EnvironmentValues[.modelContext] から値を注入

つまり:

  • 上(親)がセット
  • 下(子)が読む

という流れ。


5. 動かして確認

もし .modelContainer(...) を外すと…

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()   // ← .modelContainer を付け忘れ
        }
    }
}

この場合、@Environment(\.modelContext)何もセットされていない状態で取り出そうとするので、実行時エラーになります。

“No ModelContext in environment”

これで「セットしてないと値が入らない」ことが確認できます。


6. まとめ

  • .modelContainer(...)
    • ModelContainer(データベースの土台)を作成して
    • ModelContext(編集机)を生成し
    • Environment に登録する
  • @Environment(\.modelContext)
    • Environment 辞書から modelContext をキーに値を取り出すだけ
  • 両者は直接リンクしているわけではなく、EnvironmentValues を介して間接的につながっている。

7. 実務での使い分け

  • 読む係@Query(自動フェッチ、自動更新)
  • 書く係@Environment(\.modelContext)(insert/delete/save)

例:

// 読む
@Query private var items: [Item]

// 書く
@Environment(\.modelContext) private var context

Button("追加") {
    let newItem = Item(timestamp: .now)
    context.insert(newItem)
    try? context.save()
}

コメント

タイトルとURLをコピーしました