Good day, please tell me, is it possible to somehow change the behavior of Computed Property?
What is happening here: we show a list of transactions that are dynamically grouped by dates
import SwiftUI
import SwiftData
struct TransactionsList: View {
@Query(sort: [
SortDescriptor(\Transaction.dateTransaction, order: .reverse)
]) var transactions: [Transaction]
var groupedTransactionByDate: [Date : [Transaction]] {
print("Grouping transaction")
return Dictionary(grouping: transactions, by: { $0.dateTransaction })
}
var body: some View {
NavigationStack {
List {
ForEach(groupedTransactionByDate.keys.sorted(by: >), id: \.self) { date in
Section(header: Text(date, style: .date).font(.headline)) {
ForEach(groupedTransactionByDate[date]!) { transaction in
TransactionRow(transaction: transaction)
}
}
}
}
}
}
}
Here the log is printed several thousand times
Here in such a code, if we have 5k transactions in the database, when obtaining the MAP value (27 line), we recalculate the value of the
groupedTransactionbyDate
variable and allot the memory of 2 GB, which is why oom Killer kills the application
I used to think that the calculated properties count and update only when changing variables that are involved in the calculation, and it turns out to be at every mention of this variable
As if it begs to return the two values of Key and Value in the 25th line so that Value can no longer recalculate to each mention, but I did not find a way to do it
I can do when initializing the Fetchdescriptor and fill out the groupedTransactionByDate
variable in advance, but then it will not be updated when data changes in the database in background
I tried to make the simplest model with two fields and try to group it also, which excludes the change in this model from the outside, but this has led to the fact that for each receipt of the computed properties we recalculate
When the screen is opened, once we take data from the database (when the data changes in the database, we will recalculate the entire screen)
Once a variable groupedTransactionByDate
will be counted
Once we will draw the screen until it changes/add/delete at least one transaction in the database, after which 1, 2 and 3 points are again counted
One solution is to store the grouped transactions in a local variable in body
. This guarantees that the computation is done exactly once each time SwiftUI asks TransactionsList
for its body
.
struct TransactionsList: View {
@Query(sort: [
SortDescriptor(\Transaction.dateTransaction, order: .reverse)
]) var transactions: [Transaction]
var body: some View {
let groupedTransactionByDate = Dictionary(
grouping: transactions,
by: { $0.dateTransaction }
)
NavigationStack {
List {
ForEach(groupedTransactionByDate.keys.sorted(by: >), id: \.self) { date in
Section(header: Text(date, style: .date).font(.headline)) {
ForEach(groupedTransactionByDate[date]!) { transaction in
TransactionRow(transaction: transaction)
}
}
}
}
}
}
}