iOS 18 SwiftData ModelContext reset

Since the iOS 18 and Xcode 16, I've been getting some really strange SwiftData errors when passing @Model classes around.

The error I'm seeing is the following:

SwiftData/BackingData.swift:409: Fatal error: This model instance was destroyed by calling ModelContext.reset and is no longer usable. 

PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(url: x-coredata://34EE9059-A7B5-4484-96A0-D10786AC9FB0/TestApp/p2), implementation: SwiftData.PersistentIdentifierImplementation)

The same issue also happens when I try to retrieve a model from the ModelContext using its PersistentIdentifier and try to do anything with it. I have no idea what could be causing this.

I'm guessing this is just a bug in the iOS 18 Beta, since I couldn't find a single discussion about this on Google, I figured I'd mention it.

if someone has a workaround or something, that would be much appreciated.

Facing the same issue, does anyone know how to solve this issue?

Submitted Bug to Apple with ID: FB14089213

Anyone facing similar issue please reference this in your own report.

I got the exact same error message and resolved it by changing the way I create and keep my ModelContainer.

Cause:

Before, I created it in ContentView like this:

NavigationStackView()
    .modelContainer(MyModelContainer.create())

and accessed it in all other views via

@Environment(\.modelContext) private var modelContext

MyModelContainer.swift looked like this:

actor MyModelContainer
{
    @MainActor static func create() -> ModelContainer
    {
        let schema = Schema([MyModel.self])
        let configuration = ModelConfiguration("foo", url: FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!.appendingPathComponent("foo.store"))
        let container = try ModelContainer(for: schema, configurations: [configuration])
        // ... fill it ...
        SomeSingleton.shared.modelContext = container.mainContext // Other classes need model access too
        return container
    }
}

I have 2 models, but I've ruled that out as a cause. I also ruled out that @Environment causes it, because the crash happened even if only my SomeSingleton accessed the modelContext.

Solution:

For me, the solution was to make container a member of MyModelContainer, which I turned into a Singleton:

@MainActor class MyModelContainer
{
    static let shared = MyModelContainer()
    let container: ModelContainer

    private init()
    {
        let schema = Schema([MyModel.self])
        let configuration = ModelConfiguration("foo", url: FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!.appendingPathComponent("foo.store"))
        container = try ModelContainer(for: schema, configurations: [configuration])
        // ... fill it ...
    }
}

In other places (another Swift class, and SwiftUI), I access it via MyModelContainer.shared.container.mainContext. In SwiftUI, I removed all .modelContainer and @Environment lines. I suppose that these modifications might have also improved the thread safety and performance of my code, Apple probably had a reason for this change.

I'll also file a bug report as soon as I find the time to build a minimal example project.

Hope that helps 🙂

I filed FB14130653 with a minimal example project, and referenced the FB that @ashinthetray reported already.

Having thought about this some more, I retract my previous statement about my solution having improved the my code. I realized that now, without using @Environment, my code doesn't make use of SwiftData's simple model access in SwiftUI at all. This looks like a regression in the iOS 18 beta round (and visionOS 2, tvOS 18, macOS 15), and not like an intentional change.

NavigationStackView() .modelContainer(MyModelContainer.create()) this creates a new ModelContainer for the view, every time the view is created. Which probably isn't what you actually want. A singleton would be preferable. The view modifiers don't outlive the View instance, so this container should go away when the view does. It was a bug that it didn't on iOS 17.

The view instance in the tab controller in the sample should still be alive, however, and that is very weird. We're investigating.

The SwiftUI team strongly recommends moving the container creation into an @State variable:

@State var container = ModelContainer.create()

Facing the same issue.

Here is some code from my app for context:


In View

private func saveSocialPlatform() {
        guard !socialTitle.isEmpty else { return }
        
        let maxChars = Int(postTextMaxChars) ?? Int.max
        
        Task {
            let actor = SocialsModelActor(modelContainer: modelContext.container, mainActor: true)
            
            do {
                try await actor.saveSocial(socialTitle: socialTitle, hashtags: hashtags, maxCharsOption: maxChars, usesHashtags: usesHashtags, bestPractices: "")
                
                withAnimation {
                    show = false
                }
            } catch {
                print("Error saving a manual social platform: \(error.localizedDescription)")
            }
        }
    }

The ModelActor


actor SocialsModelActor: ModelActor {
    let modelContainer: ModelContainer
    let modelExecutor: any ModelExecutor
    
    init(modelContainer: ModelContainer) {
        let context = ModelContext(modelContainer)
        self.modelContainer = modelContainer
        modelExecutor = DefaultSerialModelExecutor(modelContext: context)
    }
    
    func saveSocial(
        socialTitle: String,
        hashtags: [String],
        maxCharsOption: Int,
        usesHashtags: Bool,
        bestPractices: String
    ) throws {
        let newHashtags = hashtags.map { Hashtag(title: $0) }
        try saveSocial(socialTitle: socialTitle, hashtags: newHashtags, maxCharsOption: maxCharsOption, usesHashtags: usesHashtags, bestPractices: bestPractices)
    }
    
    func saveSocial(
        socialTitle: String,
        hashtags: [Hashtag],
        maxCharsOption: Int,
        usesHashtags: Bool,
        bestPractices: String
    ) throws {
        try saveHashtags(hashtags: hashtags)
        
        let newSocial = SocialsPlatform(
            title: socialTitle,
            hashtags: hashtags,
            postTextMaxChars: maxCharsOption,
            usesHashtags: usesHashtags,
            bestPractices: bestPractices
        )
        
        modelContext.insert(newSocial)
        try modelContext.save()
    }
    
    func saveHashtags(
        hashtags: [Hashtag]
    ) throws {
        for hashtag in hashtags {
            modelContext.insert(hashtag)
        }
        try modelContext.save()
    }
    
    func saveHashtag(
        hashtag: Hashtag
    ) throws {
        modelContext.insert(hashtag)
        try modelContext.save()
    }
}

On top of the error discussed in the thread, using ModelActors will also trigger an old bug from over a year ago, where the UI won't refresh when an update was performed.

Has anyone found a solution yet?

I have an app all running fine and all of a sudden, I run into this error.
I was pretty sure that it was working 1 second ago so all I did was to delete the derived data(File> project setting to find out the path for the folder) of this app and build the app again. And it magically start working again...
Hope this helps!

Same issue here. iOS 18 beta 3

I'm running into the same problem in my app. The proposed solution of using a @State variable isn't easily possible for me, as while I am using SwiftUI for the front-end of the app, I've isolated the SwiftData utility into an external package.

I'm getting a crash only in the iOS 18 betas, on device and in the simulator.

This model instance was destroyed by calling ModelContext.reset and is no longer usable. PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(url: x-coredata://F5001085-8312-4E76-9730-25A81E049FB0/SentPostcard/p1), implementation: SwiftData.PersistentIdentifierImplementation)

Since an Apple engineer said we shouldn't create multiple contexts and use @State variables, etc.:

My model container is created in the App and never recreated anywhere else. I do use a ModelActor though, which is initialized from the context provided by the environment whenever it's needed.

I believe I now know exactly what's causing this. I've filed FB14344675 with a detailed description of the problem.

When an @ModelActor is created and later released (for example dropped at the end of a function scope), the model instances fetched by its associated model context can't be meaningfully used anymore.

This behavior is new in iOS 18. I don't know whether iOS 18 now actually resets the background context on deinit, whether iOS 17 somehow kept it alive until all instances were gone, or whether it's something entirely different that caused this regression.

Anyway, the solution is as follows:

  • Do NOT fetch model instances using a ModelActor-conforming actor, if the actor doesn't outlive the fetched instances.
  • Instead, keep the actor around. I'm doing this using a @State variable, but I suppose it could also be done by making the actor a singleton.

In the case that the crash is actually indented behavior and model instances aren't supposed to outlive their model context, I'd be very interested in hearing about a proper architectural solution to this problem.

The singleton solution seems the simplest, but it makes two assumptions that I don't like at all: The model context has to be well-known and static.

In my case, this simply doesn't work, because I have two model containers: One for previews with in-memory storage only, and one for development with proper on-disk storage.

Since the "correct" model container is only known at runtime, my actor can't be a singleton. It has to be instantiated with a reference to the model context.

This, however, causes the following problem: The actor can't be created on view creation, because the model container isn't (yet) accessible. I don't see a good solution to this.

This has been my solution so far:

struct MyView: View {
  @Environment(\.modelContainer) private var container
  @State private var myActor: MyModelActor?

  // Use this whenever you need the actor
  func getModelActor() -> MyModelActor {
    guard let myActor else {
      let myActor = MyModelActor(modelContainer: container)
      self.myActor = myActor
      return myActor
    }
    return myActor
  }

  var body: some View {
    SomeContent()
  }
}

That's a super ugly solution in my opinion, and although it does work, I really wouldn't wanna use this if there's a better way.

iOS 18 SwiftData ModelContext reset
 
 
Q