When changing a property of a SwiftData Model from a ModelActor the memory needed slightly increases. Once you do that more often, you can see that the usage is linearly increasing. I modified the Swiftdata template as little as possible. This is the least code I need to reproduce the problem:
Changes In the @main struct
:
ContentView(modelContainer: sharedModelContainer)
ContentView:
struct ContentView: View {
@Query private var items: [Item]
let dataHanndler: DataHandler
@State var timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { t in })
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(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))
}
}
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
ToolbarItem {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
ToolbarItem {
Button {
timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { t in
Task {
await dataHanndler.updateRandom()
// Obviously this makes little sense but I need to update a lot of entities in my actual app. This is the simplest way to demonstrate that. updateRandom() could also be a function of a view but that doesn't make a difference
}
}
} label: {
Label("Do a lot of writing", systemImage: "gauge.with.dots.needle.100percent")
}
}
ToolbarItem {
Button {
timer.invalidate()
} label: {
Label("Invalidate", systemImage: "stop.circle")
}
}
}
} detail: {
Text("Select an item")
}
}
private func addItem() {
Task {
await dataHanndler.insert(timestamp: Date.now)
}
}
init(modelContainer: ModelContainer) {
self.dataHanndler = DataHandler(modelContainer: modelContainer)
}
}
ModelActor:
@ModelActor
actor DataHandler {
public func update<T>(_ persistentIdentifier: PersistentIdentifier, keypath: ReferenceWritableKeyPath<Item, T>, to value: T) throws {
let model = modelContext.model(for: persistentIdentifier) as! Item
model[keyPath: keypath] = value
}
public func insert(timestamp: Date) {
let item = Item(timestamp: timestamp)
modelContext.insert(item)
}
public func updateRandom() {
let count = try! modelContext.fetchCount(FetchDescriptor<Item>())
var descriptor = FetchDescriptor<Item>()
descriptor.fetchOffset = Int.random(in: 0..<count)
descriptor.fetchLimit = 1
let model = try! modelContext.fetch(descriptor)
model.first!.timestamp = Date.now
}
}
I filed a bug report FB14876920 but I am looking for other ideas to solve this before it will be fixed in a future update. The modelContext I use is created and managed by the @ModelActor
macro.
Happy to hear ideas