I have a structure to display a list of bank / ATM withdrawal entries, and another structure to create a new entry for display in the list. When I press the save button after entering a new entry, operation is returning to the list structure and displaying all entries but the current entry before the method addNewWithdrawal() has had a chance to save the new entry (checked with a breakpoint).
So I have a timing issue occurring at the bottom of the saveButton() function--the question is how to fix it. It appears that I need to separate the two operations by placing things on separate threads and delaying the main thread until the save operation completes. But both threads are tied to views requiring the main thread.
Let me know if you would like to see the list view logic.
struct WdModel: Codable, Identifiable, Hashable {
var id = UUID()
var wdDate: Date // bank withdrawal data
var wdCode: String
var wdBank: String
var wdAmtL: Double
var wdAmtH: Double
var wdCity: String
var wdState: String
var wdCountry: String
}
class Withdrawal: ObservableObject {
@Published var wdArray: [WdModel] {
didSet {
// save withdrawal entries
if let encoded = try? JSONEncoder().encode(wdArray) { // save withdrawal entries
UserDefaults.standard.set(encoded, forKey: StorageKeys.wdBank.rawValue)
}
}
}
init() {
if let wdArray = UserDefaults.standard.data(forKey: StorageKeys.wdBank.rawValue) {
if let decoded = try? JSONDecoder().decode([WdModel].self, from: wdArray) {
self.wdArray = decoded
return
}
}
self.wdArray = []
if let encoded = try? JSONEncoder().encode(wdArray) {
UserDefaults.standard.set(encoded, forKey: StorageKeys.wdBank.rawValue)
}
}
// save new withdrawal data
func addNewWithdrawal(wdDate: Date, wdCode: String, wdBank: String, wdAmtL: Double, wdAmtH: Double, wdCity: String, wdState: String, wdCountry: String) {
self.withdrawalTotal += wdAmtH
let item = WdModel(wdDate: wdDate, wdCode: wdCode, wdBank: wdBank, wdAmtL: wdAmtL, wdAmtH: wdAmtH, wdCity: wdCity, wdState: wdState, wdCountry: wdCountry)
self.wdArray.append(item)
}
Below is the code to save the newly created entry
// the save button has been pressed
func saveButton() {
// get coordinates and address
// convert amount as string to double
let moneyD = (wdAmtL as NSString).doubleValue
let wdCode = currencies.curItem[userData.entryCur].curCode
// get the local currency equivalent
let rate = currencies.curItem[userData.entryCur].curRate
// get local currency amount: use inverse rate
wdAmtH = moneyD * (1 / rate)
**** Problem is occurring here when dismiss() and return to the list view happens before the data is saved (wdvm.addNewWithdrawal()
// save entry to user defaults
wdvm.addNewWithdrawal(wdDate: wdDate, wdCode: wdCode, wdBank: wdBank, wdAmtL: moneyD, wdAmtH: wdAmtH, wdCity: wdCity, wdState: wdState, wdCountry: wdCountry)
}
dismiss()
}
struct WithdrawalView: View {
@StateObject var wdvm = Withdrawal()
var uniqueBankDates: [String] {
Array(Set(wdvm.wdArray)) // This will remove duplicates, but WdModel needs to be Hashable
.sorted { $0.wdDate < $1.wdDate } // Compare dates
.compactMap {
$0.wdDate.formatted(date: .abbreviated, time: .omitted) // Return an array of formatted the dates
}
}
// filters entries for the given date
func bankEntries(for date: String) -> [WdModel] {
return wdvm.wdArray.filter { $0.wdDate.formatted(date: .abbreviated, time: .omitted) == date }
}
var body: some View {
GeometryReader { g in
VStack (alignment: .leading) {
WDTitleView(g: g)
List {
if wdvm.wdArray.isEmpty {
NoItemsView()
} else {
// outer ForEach with unique dates
ForEach(uniqueBankDates, id: \.self) { dateItem in // change this to sort by date
Section {
// inner ForEach with items of this date
ForEach(bankEntries(for: dateItem)) { item in
wdRow(g: g, item: item)
}
} header: {
Text("\(dateItem)")
}
}.onDelete(perform: deleteItem)
}
}
.navigationBarTitle("Bank Withdrawals", displayMode: .inline)
.environment(\.defaultMinListRowHeight, g.size.height > g.size.width ? 9 : 5)
.navigationBarItems(trailing: NavigationLink (destination: AddWithdrawalView()) {
Image(systemName: "plus")
.resizable()
.frame(width: 18, height: 18)
})
}
}
if !wdvm.wdArray.isEmpty {
ShowWdTotal()
}
}
// may delete entries with swipe left
func deleteItem(at offsets: IndexSet) {
var money: Double = 0
offsets.forEach { singleOffset in
money = wdvm.wdArray[singleOffset].wdAmtH
wdvm.withdrawalTotal -= money
wdvm.wdArray.remove(atOffsets: offsets)
}
}
}