I’m working on Testing a SwiftUI project using SwiftData where I need to add Book objects to my model context from both the ContentView and a background task. I want to ensure that both operations use the same ModelContainer for consistency.
I’ve created a simple project to illustrate my setup, but I’m unsure if I’m approaching this correctly. Could someone provide guidance on how to properly add Book objects from both the ContentView and the background task while using the same context?
import SwiftUI
import SwiftData
@main
struct testQueryApp: App {
var sharedModelContainer: ModelContainer = {
let schema = Schema([
Book.self,
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
var body: some Scene {
WindowGroup {
ContentView(modelContainer: sharedModelContainer)
}
.modelContainer(sharedModelContainer)
}
}
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@Query private var items: [Book]
@StateObject private var viewModel: NetworkingManager
init(modelContainer: ModelContainer) {
_viewModel = StateObject(wrappedValue: NetworkingManager(modelContainer: modelContainer))
}
var body: some View {
NavigationSplitView {
List {
ForEach(items) { item in
NavigationLink {
Text("Item at \(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))")
} label: {
Text("Book: \(item.title)")
}
}
.onDelete(perform: deleteItems)
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
ToolbarItem {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
ToolbarItem(placement: .topBarLeading) {
Button {
Task {
await viewModel.parser.addBook()
}
} label: {
Text("Return")
}
}
}
} detail: {
Text("Select an item")
}
}
private func addItem() {
withAnimation {
let newItem = Book(timestamp: Date(), title: "Book01")
modelContext.insert(newItem)
}
}
private func deleteItems(offsets: IndexSet) {
withAnimation {
for index in offsets {
modelContext.delete(items[index])
}
}
}
}
import SwiftData
import SwiftUI
import Foundation
@Observable
class NetworkingManager: ObservableObject {
let parser: BackgroundParser
init(modelContainer: ModelContainer) {
self.parser = BackgroundParser(modelContainer: modelContainer)
}
}
actor BackgroundParser: ObservableObject {
let executor: any ModelExecutor
let context: ModelContext
init(modelContainer: ModelContainer) {
context = ModelContext(modelContainer)
executor = DefaultSerialModelExecutor(modelContext: context)
}
func addBook() {
let newItem = Book(timestamp: Date(), title: "Background Book")
context.insert(newItem)
}
}
import Foundation
import SwiftData
@Model
final class Book {
var timestamp: Date
var title : String
init(timestamp: Date, title: String) {
self.timestamp = timestamp
self.title = title
}
}
I appreciate any insights or suggestions on how to properly manage adding items from both the ContentView and background tasks using the same context.
Thanks in advance!
@Observable
andObservableObject
, use one or the other, not both. If you useObservableObject
then you should not nest them, egBackgroundParser
insideNetworkingManager
.