MENU

【SwiftData】”NSFetchRequest could not locate an NSEntityDescription for entity name”エラーの完全解決ガイド

SwiftDataを使ったアプリ開発をしていると、次のようなエラーに遭遇することがあります。

Thread 1: "NSFetchRequest could not locate an NSEntityDescription for entity name 'MyList'"

このエラーは、iOS 17以降で導入された新しいデータ永続化フレームワーク「SwiftData」を学ぶ際に、初心者が遭遇しやすい典型的なエラーです。SwiftDataは内部的にCore Dataを使用しているため、このようなCore Data由来のエラーメッセージが表示されることがあります。

本記事では、このエラーの意味と具体的な解決方法を、Swift初心者の方にもわかりやすく解説します。

目次

エラーの意味を理解しよう

このエラーメッセージは、「MyListという名前のエンティティ(モデル)が見つかりません」という意味です。

SwiftDataは、裏側でCore Dataを使用してデータを管理しています。そのため、@Modelマクロで定義したクラスが正しくCore Dataのエンティティとして認識されていない場合、このエラーが発生します。

エラーが発生する典型的な状況

  • @Modelマクロが正しく適用されていない
  • ModelContainerにモデルが登録されていない
  • Schemaを使用している場合、モデルが含まれていない
  • データが一度も保存されていない
  • ビルドキャッシュの問題でモデルが認識されていない

よくあるコード例

import SwiftData
import SwiftUI

@Model
class MyList {
    var name: String
    var items: [String]
    
    init(name: String, items: [String] = []) {
        self.name = name
        self.items = items
    }
}

struct ContentView: View {
    @Query var lists: [MyList]  // ここでエラーが発生
    
    var body: some View {
        List(lists, id: \.name) { list in
            Text(list.name)
        }
    }
}

エラーの原因と解決方法

1. @Modelマクロの付け忘れ・設定ミス

SwiftDataでは、データモデルとして使用するクラスに必ず@Modelマクロを付ける必要があります。

解決方法

// ❌ 間違い:@Modelマクロがない
class MyList {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

// ✅ 正しい:@Modelマクロを追加
import SwiftData

@Model
class MyList {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

アクセス修飾子がある場合は、@Modelを先に書きます。

@Model
public class MyList {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

2. ModelContainerにモデルが登録されていない

アプリの起動時にModelContainerを作成し、使用するモデルを明示的に登録する必要があります。

解決方法

import SwiftUI
import SwiftData

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: MyList.self)
    }
}

複数のモデルを使用する場合は、配列で指定します。

.modelContainer(for: [MyList.self, MyItem.self, Category.self])

3. Schemaにモデルが含まれていない

Schemaを明示的に定義している場合、そのSchemaに含まれていないモデルはエンティティとして認識されません。マイグレーションやバージョン管理を行う際に発生しやすいエラーです。

問題のあるコード例

@main
struct MyApp: App {
    let container: ModelContainer
    
    init() {
        let schema = Schema([
            MyItem.self,
            Category.self
            // MyList.selfが含まれていない!
        ])
        
        let configuration = ModelConfiguration(schema: schema)
        container = try! ModelContainer(for: schema, configurations: configuration)
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .modelContainer(container)
        }
    }
}

解決方法

let schema = Schema([
    MyList.self,    // 追加!
    MyItem.self,
    Category.self
])

バージョン管理時の例:

enum SchemaV2: VersionedSchema {
    static var versionIdentifier = Schema.Version(2, 0, 0)
    static var models: [any PersistentModel.Type] {
        [MyItem.self, Category.self, MyList.self]  // すべてのモデルを含める
    }
}

4. SwiftDataのインポート忘れ

モデルファイルとビューファイルの両方でimport SwiftDataが必要です。

解決方法

// モデルファイル(MyList.swift)
import SwiftData
import Foundation

@Model
class MyList {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

// ビューファイル(ContentView.swift)
import SwiftUI
import SwiftData  // ← 忘れずに

struct ContentView: View {
    @Query var lists: [MyList]
    
    var body: some View {
        List(lists, id: \.name) { list in
            Text(list.name)
        }
    }
}

5. データが一度も保存されていない

これは見落としがちな重要な原因です。 モデルの定義が正しくても、そのモデルのデータが一度も保存されていない場合、Core Dataのスキーマが生成されず、エラーが発生することがあります。

特に以下のタイミングで発生します:

  • アプリの初回起動時
  • シミュレータやアプリをリセットした直後
  • データベースファイルが削除された後

解決方法

初回起動時に自動的にデータを作成するか、空の状態を考慮したUIを作成します。

@main
struct MyApp: App {
    let container: ModelContainer
    
    init() {
        do {
            container = try ModelContainer(for: MyList.self)
            initializeDataIfNeeded(container: container)
        } catch {
            fatalError("ModelContainerの初期化に失敗: \(error)")
        }
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(container)
    }
    
    private func initializeDataIfNeeded(container: ModelContainer) {
        let context = container.mainContext
        let descriptor = FetchDescriptor<MyList>()
        
        do {
            let existingLists = try context.fetch(descriptor)
            
            if existingLists.isEmpty {
                let sampleList = MyList(name: "はじめてのリスト", items: [])
                context.insert(sampleList)
                try context.save()
            }
        } catch {
            print("データ確認エラー: \(error)")
        }
    }
}

空の状態を考慮したUI:

struct ContentView: View {
    @Query private var lists: [MyList]
    @Environment(\.modelContext) private var modelContext
    
    var body: some View {
        Group {
            if lists.isEmpty {
                VStack {
                    Text("リストがありません")
                        .font(.title2)
                        .foregroundColor(.secondary)
                    
                    Button("最初のリストを作成") {
                        createFirstList()
                    }
                    .buttonStyle(.borderedProminent)
                }
            } else {
                List(lists, id: \.name) { list in
                    Text(list.name)
                }
            }
        }
    }
    
    private func createFirstList() {
        let newList = MyList(name: "新しいリスト", items: [])
        modelContext.insert(newList)
        try? modelContext.save()
    }
}

6. Xcodeのビルドキャッシュの問題

Xcodeのビルドキャッシュが原因で、最新のモデル定義が反映されていない場合があります。

解決方法

以下の手順を順番に試してください。

  1. Clean Buildを実行
    • Xcodeのメニューから「Product」→「Clean Build Folder」(Shift + Command + K)
    • プロジェクトを再ビルド(Command + B)
  2. Derived Dataの削除
    • Xcodeのメニューから「Xcode」→「Settings」
    • 「Locations」タブを選択
    • 「Derived Data」の矢印アイコンをクリックしてフォルダを開く
    • 該当するプロジェクトのフォルダを削除
    • Xcodeを再起動して再ビルド
  3. シミュレータのリセット
    • シミュレータのメニューから「Device」→「Erase All Content and Settings」
  4. 実機の場合はアプリを削除して再インストール

7. モデルクラスの定義に問題がある

SwiftDataの@Modelマクロは、特定の条件を満たすクラスにのみ適用できます。

確認すべきポイント

  • クラスはclassとして定義(structは使用不可)
  • プロパティはvarで定義(letは使用不可)
  • サポートされている型のみを使用

解決方法

// ❌ 間違い:structを使用
@Model
struct MyList {
    var name: String
}

// ✅ 正しい:classを使用
@Model
class MyList {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

// ❌ 間違い:letで定義
@Model
class MyList {
    let name: String
    
    init(name: String) {
        self.name = name
    }
}

// ✅ 正しい:varで定義
@Model
class MyList {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

サポートされているプロパティ型

  • 基本型:String, Int, Double, Bool, Date
  • オプショナル型:String?, Int?など
  • コレクション:Array, Set
  • 他の@Modelクラスへの参照
  • Data, UUID
  • enum(RawRepresentableな型)

エラーハンドリングの実装

本番環境での予期せぬエラーに備えて、適切なエラーハンドリングを実装しましょう。

基本的なエラーハンドリング

struct ContentView: View {
    @Environment(\.modelContext) private var modelContext
    @Query private var lists: [MyList]
    @State private var errorMessage: String?
    
    var body: some View {
        Group {
            if let errorMessage = errorMessage {
                VStack {
                    Image(systemName: "exclamationmark.triangle")
                        .font(.largeTitle)
                        .foregroundColor(.red)
                    Text(errorMessage)
                        .padding()
                }
            } else {
                List(lists, id: \.name) { list in
                    Text(list.name)
                }
            }
        }
        .onAppear {
            validateModelSetup()
        }
    }
    
    func validateModelSetup() {
        do {
            let descriptor = FetchDescriptor<MyList>()
            let _ = try modelContext.fetch(descriptor)
        } catch {
            errorMessage = "データの読み込みに失敗しました"
            print("Error: \(error)")
        }
    }
}

ModelContainer初期化時のエラーハンドリング

@main
struct MyApp: App {
    @State private var container: ModelContainer?
    @State private var initError: Error?
    
    init() {
        setupModelContainer()
    }
    
    var body: some Scene {
        WindowGroup {
            if let container = container {
                ContentView()
                    .modelContainer(container)
            } else if let error = initError {
                ErrorView(error: error)
            } else {
                ProgressView("データを読み込み中...")
            }
        }
    }
    
    private func setupModelContainer() {
        do {
            container = try ModelContainer(for: MyList.self)
        } catch {
            initError = error
        }
    }
}

struct ErrorView: View {
    let error: Error
    
    var body: some View {
        VStack(spacing: 20) {
            Image(systemName: "exclamationmark.octagon.fill")
                .font(.system(size: 60))
                .foregroundColor(.red)
            
            Text("データベースの初期化に失敗しました")
                .font(.headline)
            
            Text(error.localizedDescription)
                .font(.caption)
                .foregroundColor(.secondary)
                .multilineTextAlignment(.center)
                .padding()
        }
        .padding()
    }
}

エラーを防ぐためのベストプラクティス

1. モデルの定義を一箇所にまとめる

MyApp/
├── Models/
│   ├── MyList.swift
│   ├── MyItem.swift
│   └── Category.swift
├── Views/
│   └── ContentView.swift
└── MyApp.swift

2. @Modelマクロの使用を徹底する

import SwiftData

@Model
final class MyList {
    var name: String
    var createdAt: Date
    var items: [MyItem]
    
    init(name: String, items: [MyItem] = []) {
        self.name = name
        self.createdAt = Date()
        self.items = items
    }
}

3. ModelContainerの設定を確実に行う

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: [MyList.self, MyItem.self, Category.self])
    }
}

4. プレビュー用のModelContainerを用意する

extension ModelContainer {
    static var preview: ModelContainer {
        let container = try! ModelContainer(
            for: MyList.self,
            configurations: ModelConfiguration(isStoredInMemoryOnly: true)
        )
        
        let sample1 = MyList(name: "買い物リスト", items: [])
        let sample2 = MyList(name: "TODO", items: [])
        container.mainContext.insert(sample1)
        container.mainContext.insert(sample2)
        
        return container
    }
}

#Preview {
    ContentView()
        .modelContainer(ModelContainer.preview)
}

5. ビルド設定を確認する

  • Deployment Target: iOS 17.0以上
  • Swift Language Version: Swift 5.9以上

トラブルシューティングチェックリスト

エラーが解決しない場合は、以下を順番に確認してください。

基本設定の確認

  • [ ] モデルクラスに@Modelマクロが付いているか
  • [ ] モデルクラスがclassとして定義されているか
  • [ ] すべてのプロパティがvarで定義されているか
  • [ ] import SwiftDataが記述されているか(モデルとビューの両方)
  • [ ] ModelContainerが正しく設定されているか
  • [ ] 使用するすべてのモデルがModelContainerに登録されているか
  • [ ] Schemaを使用している場合、すべてのモデルが含まれているか
  • [ ] iOS Deployment Targetが17.0以上に設定されているか

データの確認

  • [ ] アプリで一度もデータを保存していないか
  • [ ] 最近アプリやシミュレータをリセットしたか
  • [ ] 初回起動時にサンプルデータを作成する処理があるか
  • [ ] 空のデータ状態を想定したUI設計になっているか

ビルド・キャッシュの確認

  • [ ] Clean Build Folderを実行したか
  • [ ] Derived Dataを削除したか
  • [ ] Xcodeを再起動したか
  • [ ] シミュレータ/実機でアプリを削除して再インストールしたか

SwiftDataとCore Dataの違い

SwiftDataは内部的にCore Dataを使用していますが、より現代的で使いやすいAPIを提供しています。

項目Core DataSwiftData
データモデル定義.xcdatamodeldファイル@Modelマクロを使ったSwiftコード
最小iOSiOS 3.0〜iOS 17.0〜
マクロの使用なし@Model, @Queryなど
学習曲線やや急緩やか

このため、SwiftDataを使っていても、エラーメッセージにはCore Data由来の用語が表示されることがあります。これは正常な動作です。

まとめ

SwiftDataで「NSFetchRequest could not locate an NSEntityDescription for entity name」エラーに遭遇したら、以下のポイントを確認しましょう。

  1. @Modelマクロの確認 – すべてのモデルクラスに付いているか
  2. ModelContainerの設定 – アプリ起動時に正しく登録されているか
  3. Schemaの確認 – 明示的にSchemaを使用している場合、すべてのモデルが含まれているか
  4. import文の確認 – SwiftDataがインポートされているか
  5. データの存在確認 – 初回起動時やリセット後は、データが存在しないことが原因の可能性が高い
  6. ビルドキャッシュのクリア – Clean BuildやDerived Dataの削除
  7. モデルの定義 – classを使い、プロパティはvarで定義

特に見落としがちなのが以下の2点です:

Schemaを使用している場合: マイグレーションやバージョン管理でSchemaを明示的に定義していると、そこに含まれていないモデルはエンティティとして認識されません。

データが一度も保存されていない場合: モデルの定義が完璧でも、実際のデータが存在しなければエラーが発生します。初回起動時のサンプルデータ作成や、空の状態を考慮したUI設計を心がけましょう。

SwiftDataはiOS 17以降の新しい技術ですが、適切な設定を行えば、Core Dataよりもシンプルにデータ永続化を実装できます。エラーメッセージを恐れずに、一つずつ確認していけば必ず解決できます。

参考リンク

プログラミングの独学におすすめ
プログラミング言語の人気オンラインコース
独学でプログラミングを学習している方で、エラーなどが発生して効率よく勉強ができないと悩む方は多いはず。Udemyは、プロの講師が動画で実際のプログラムを動かしながら教えてくれるオンライン講座です。講座の価格は、セール期間中には専門書籍を1冊買うよりも安く済むことが多いです。新しく学びたいプログラミング言語がある方は、ぜひUdemyでオンライン講座を探してみてください。
目次