Search code examples
swiftdata

Failed to find any currently loaded container for Q


Now I am working to make a Question and Answer App. for medical student using Swift Data. I am tryin to make a class for Question type and another class Q for item in Question type. Then I added the @Model macro the make a structure. This app class and says "Failed to find any currently loaded container for Q I made the class q codable but it also clash And says

"SwiftData/ModelContainer.swift:144: Fatal error: failed to find a currently active container for Q Failed to find any currently loaded container for Q)"

I have made a simple code to recreate the crush like below

import SwiftUI
import SwiftData

@main

struct MyApp: App {
    
    let modelContainer: ModelContainer
    @State private var appData = ApplicationData()
    
    init() {
        do {
            modelContainer = try ModelContainer(for: Question.self)
        } catch {
            fatalError("Could not initialize ModelContainer")
        }
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(modelContainer)
    }
}


import SwiftUI
import Observation
import SwiftData

@Observable class ApplicationData {
    var viewPath = NavigationPath()
    var userData: [Question]
    
           init() {
               userData =
               [
                Question(qid: "230272", year: "2023",  q: [Q(qid:"1", q: "1.대인관계치료"), Q(qid:"2", q: "2. 사회기술재활"), Q(qid:"3", q: "3. 습관역전훈련"), Q(qid:"4",q: "4.변증법적행동치료"), Q(qid:"5", q: "5.안구운동탈민감재처리")]),
                   
                Question(qid: "230273", year: "2023",  q: [Q(qid:"1", q: "1.홍수법"), Q(qid:"2", q: "2.수면제한"), Q (qid: "3", q: "3.동기강화치료"), Q(qid:"4", q: "4.체계적 탈감작"), Q( qid: "5", q: "5.노출과 반응차단")])
               ]
                              
           }
    
}

@Model
class Question: Identifiable {
    @Attribute(.unique)  var idid: UUID = UUID()
    var qid : String = ""
  var year: String = ""
    var q: [Q?] = []
 
    init( qid: String, year: String, q: [Q?]) {
           self.qid = qid
            self.year = year
           self.q = q
        }
  
}

@Model
 class Q:  Identifiable, Codable {
    
    enum CodingKeys: CodingKey {
        case  qid, q
    }
  
 @Attribute(.unique)  var id = UUID()
    var qid: String? = ""
    var q: String? = ""
    
     init( qid: String?, q: String?) {
        // self.id = UUID().uuidString
         self.qid = qid
        self.q = q
    }
     
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.qid = try container.decode(String.self, forKey: .qid)
        self.q = try container.decode(String.self, forKey: .q)
    }
    
    func encode(to encoder: Encoder) throws {
       var container = encoder.container(keyedBy: CodingKeys.self)
       try container.encode(qid, forKey: .qid)
       try container.encode(q, forKey: .q)
     }
 
}

import SwiftUI
import SwiftData

struct ContentView: View {
    @Environment(ApplicationData.self) private var appData
    @Environment(\.modelContext) var dbContext
    @Query var listQuestions: [Question]
    
    
    var body: some View {
        NavigationStack(path: Bindable(appData).viewPath) {
            List(listQuestions) { question in
                CellQuestion(question: question)
            }
            .listStyle(.plain)
            .navigationTitle("Title")
            .toolbarTitleDisplayMode(.inline)
            
        }
    }
}
    
struct CellQuestion: View {
        let question: Question
        var body: some View {
            Text(question.year)
            
        }
    }    

Solution

  • There are a few errors in your code.

    1. The init in ApplicationData will be executed to early, before the ModelContainer and ModelContext has been created and that is why you get the error Q isn't loaded.

    Solution here is to move the code inside the init into a separate function and call that function from an onAppear

    1. You are not inserting the objects you create into the model context so the query you have in your view will not fetch anything.

    Pass the model context as a parameter to the function in 1.

    1. You don't properly add ApplicationData as an environment value.

    add .environment(appData) to WindowGroup in main

    Here is my simplified implementation of ApplicationData

    @Observable class ApplicationData {
        var viewPath = NavigationPath()
    
        init() {}
    
        func load(modelContext: ModelContext) {
            let q1 = Question(qid: "230272", year: "2023",  q: [QuestItem(qid:"1", q: "1.대인관계치료"), QuestItem(qid:"2", q: "2. 사회기술재활"), QuestItem(qid:"3", q: "3. 습관역전훈련"), QuestItem(qid:"4",q: "4.변증법적행동치료"), QuestItem(qid:"5", q: "5.안구운동탈민감재처리")])
    
            modelContext.insert(q1)
            let q2 = Question(qid: "230273", year: "2023",  q: [QuestItem(qid:"1", q: "1.홍수법"), QuestItem(qid:"2", q: "2.수면제한"), QuestItem (qid: "3", q: "3.동기강화치료"), QuestItem(qid:"4", q: "4.체계적 탈감작"), QuestItem( qid: "5", q: "5.노출과 반응차단")])
    
            modelContext.insert(q2)
        }
    }
    

    And the ContentView where the call to load(...) should be assigned to a button rather then being executed everytime but this view code only serves as an example,

    struct ContentView: View {
        @Environment(ApplicationData.self) private var appData
        @Environment(\.modelContext) var dbContext
        @Query var listQuestions: [Question]
    
        var body: some View {
            NavigationStack(path: Bindable(appData).viewPath) {
                List(listQuestions) { question in
                    CellQuestion(question: question)
                }
                .onAppear {
                    appData.load(modelContext: dbContext)
                }
            }
        }
    }