SwiftData - error: Error: Persistent History (2) has to be truncated due to the following entities being removed: ( A )

I'm having trouble resolving the error.

If I save data in view A and open view B, I get an error. If I open only view A, or only view B, there is no problem, but when I open a second view (A to B, B to A, etc.) I get an error.

Attached is the code that reproduces the problem. We would appreciate your cooperation.

No simulator is used. Xcode: Version 15.4 (15F31d) iPone11: 17.5.1

Thank you.

Error

error: Error: Persistent History (2) has to be truncated due to the following entities being removed: (
    A
)
warning: Warning: Dropping Indexes for Persistent History
warning: Warning: Dropping Transactions prior to 2 for Persistent History
warning: Warning: Dropping Changes prior to TransactionID 2 for Persistent History

Code

import SwiftUI
import SwiftData
@main
struct issueApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .modelContainer(for: [A.self, B.self])
        }
    }
}
import SwiftUI
import SwiftData
struct ContentView: View {
    @State var showAView: Bool = false
    @State var showBView: Bool = false
    var body: some View {
        VStack {
            Button("show A"){
                showAView.toggle()
            }
            .sheet(isPresented: $showAView){
                AView()
            }
            Button("show B"){
                showBView.toggle()
            }
            .sheet(isPresented: $showBView){
                BView()
            }
        }
    }
}
struct AView: View {
    @Environment(\.modelContext) var context
    @Query private var list: [A]
    let query = QueryData<A>()
    var body: some View {
        VStack {
            if list.count > 0 {
                VStack {
                    List {
                        ForEach(list, id: \.number){ item in
                            VStack {
                                Text(item.number)
                            }
                        }
                    }
                    Button("clear"){
                        try? context.delete(model: A.self)
                    }
                }
            } else {
                Button("set"){
                    query.set(data: A(number: "1"))
                }
            }
        }
    }
}
struct BView: View {
    @Environment(\.modelContext) var context
    @Query private var list: [B]
    let query = QueryData<B>()
    var body: some View {
        VStack {
            if list.count > 0 {
                VStack {
                    List {
                        ForEach(list, id: \.number){ item in
                            VStack {
                                Text(item.number)
                            }
                        }
                    }
                    Button("clear"){
                        try? context.delete(model: B.self)
                    }
                }
            } else {
                Button("set"){
                    query.set(data: B(number: "1"))
                }
            }
        }
    }
}
class QueryData<T: PersistentModel> {
    private var container: ModelContainer?
    init() {
        self.container = try? ModelContainer(for: T.self)
    }
    func set(data: T) {
        guard let container = self.container else { return }
        let context = ModelContext(container)
        do {
            try context.delete(model: T.self)
            context.insert(data)
            try context.save()
        } catch {
            print("error: \(error)")
        }
    }
}
@Model
final class A {
    var number: String
    init(number: String) {
        self.number = number
    }
}
@Model
final class B {
    var number: String
    init(number: String) {
        self.number = number
    }
}
Answered by DTS Engineer in 796060022

When you set data in view A and open view B, your code does the following:

  1. Create a model container with model A.
  2. Insert an object of model A to the SwiftData store, which generates the history in the store.
  3. Create a model container with model B.

At step 3, the model schema only contains model B, which means the container knows nothing about model A, and so SwiftData cleans everything related to model A, including the history.

If you don't do step 2, no history is generated, and hence no need to truncate the history at step 3.

The error tells you that the history was truncated. Unless you need to find the history related to model A at step 3, which isn't quite right because the container's schema only contains B, the error won't matter, and you can safely ignore it.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

When you set data in view A and open view B, your code does the following:

  1. Create a model container with model A.
  2. Insert an object of model A to the SwiftData store, which generates the history in the store.
  3. Create a model container with model B.

At step 3, the model schema only contains model B, which means the container knows nothing about model A, and so SwiftData cleans everything related to model A, including the history.

If you don't do step 2, no history is generated, and hence no need to truncate the history at step 3.

The error tells you that the history was truncated. Unless you need to find the history related to model A at step 3, which isn't quite right because the container's schema only contains B, the error won't matter, and you can safely ignore it.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Accepted Answer

If this error occurs, any saved data will be lost. For example, if you save data in A and open B, an error will occur and the data in A will be deleted. It doesn't seem to be a problem, but it would be a problem if the data disappeared. This is because it is used for data persistence purposes.

Yeah, that's again because the container at step 3 doesn't know model A at all, given that you use only model B as the container schema.

Is there any reason why you create a model container with only model A at step 1 and only model B at step 3? Given that you would retain the data, it seems to me that you'd create the container with both model A and B at both step 1 and 3:

let container = try? ModelContainer(for: A.self, B.self)

If you do need to change the schema at some point (by removing model A in this example), consider creating a migration plan (SchemaMigrationPlan) and using it to create a model container so that the existing data is migrated to the new schema.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

SwiftData - error: Error: Persistent History (2) has to be truncated due to the following entities being removed: ( A )
 
 
Q