I am developing an iOS app using SwiftUI. In the ContentView after it loads it displays the SelectAccountView for the user to select an account. When the process returns back to ContentView, I want to use the selectedAccount value to filter the CoreData.
Below is the ContentView
import SwiftUI
import CoreData
struct ContentView: View
{
@State private var finalAmount: Float = 0
@State private var selectedEntry: CFTEntry? // Move selectedEntry to ContentView
@State private var isPresentingEntryEditView = false
@State private var isPresentingSettingsView = false
@State private var isPresentingDeleteView = false
@State private var isPresentingAccountsView = true
@State private var selectedAccount: CFTAccounts? // Added selectedAccount
@FetchRequest(entity: CFTEntry.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \CFTEntry.cftDate, ascending: false)])
var entries: FetchedResults<CFTEntry>
var body: some View
{
NavigationView
{
VStack
{
HStack
{
Image("Wallet") // Add your desired icon system name
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 40, height: 40)
.padding(.leading, 20)
Spacer()
Text("Cash Flow Tracker")
.font(.title)
.fontWeight(.bold)
// .padding(.top, 1)
Spacer()
}
HStack
{
if let account = selectedAccount
{ // Show selected account name
Text("Account: \(account.cftAccount ?? "")")
.font(.headline)
// .fontWeight(.bold)
// .padding(.top, 1)
}
else
{
Text("Please, select an Account") // Display message if no account is selected
.foregroundColor(.red)
// .padding(.top, 1)
}
}
HStack(spacing: 50)
{
// Descriptions
Button(action: {
// Show EntrySettingsView
isPresentingSettingsView = true
}) {
Image(systemName: "info.circle") //.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25, height: 25)
}
// Add New Entry
Button(action: {
isPresentingEntryEditView = true
selectedEntry = nil
})
{
VStack
{
Image(systemName: "plus.circle") //.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25, height: 25)
// Text("Add Entry")
// .font(.system(size: 14))
}
}
// Delete Entries
Button(action: {
isPresentingDeleteView = true
})
{
VStack
{
Image(systemName: "trash.circle") //.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25, height: 25)
}
}
// Accounts
Button(action: {
isPresentingAccountsView = true
})
{
VStack
{
Image(systemName: "building.columns.circle") //.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25, height: 25)
}
}
}
EntryListView(entries: entries, selectedEntry: $selectedEntry, bindTotalAmount: $finalAmount, updateTotalAmount: calculateTotalAmount)
{ totalAmount in
// Update the totalAmount in the ContentView
finalAmount = totalAmount
}
.onAppear
{
// Calculate totalAmount when the view appears initially
finalAmount = calculateTotalAmount()
}
.onReceive(NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave)) { _ in
// Calculate totalAmount when the managed object context is saved (i.e., an entry is added or edited)
finalAmount = calculateTotalAmount()
}
.onTapGesture
{
isPresentingEntryEditView = true
}
//.padding(.top, 20)
//Display the grand total at the bottom of the screen
HStack
{
//"$\(entry.cftAmount, specifier: "%.2f")"
Text("Projected Total $\(finalAmount, specifier: "%.2f")")
.font(.title3)
.fontWeight(.regular)
.padding(.trailing, 10)
}
}
.sheet(isPresented: $isPresentingEntryEditView)
{
EntryEditView(selectedEntry: $selectedEntry, bindTotalAmount: $finalAmount, selectedAccount: $selectedAccount)
}
.sheet(isPresented: $isPresentingSettingsView)
{
EntrySettingsView()
}
.sheet(isPresented: $isPresentingDeleteView)
{
DeleteEntriesView()
}
.sheet(isPresented: $isPresentingAccountsView)
{
// Pass selectedAccount as binding
SelectAccountView(selectedAccount: $selectedAccount)
}
}
}
func calculateTotalAmount() -> Float
{
var calculatedTotalAmount: Float = 0.0
let filteredEntries: [CFTEntry]
if let selectedAccount = selectedAccount
{
filteredEntries = entries.filter { $0.cftAccount == selectedAccount.cftAccount }
}
else
{
filteredEntries = entries.map { $0 }
}
for entry in filteredEntries
{
if entry.cftCategory == "Income"
{
calculatedTotalAmount += entry.cftAmount
}
else
{
calculatedTotalAmount -= entry.cftAmount
}
}
return calculatedTotalAmount
}
}
The Core data entity is CFTEntry and it contains an attribute cftAccount. Every time the user displays the SelectAccountView to select an account, the account name is retrieved successfully in the code
Text("Account: \(account.cftAccount ?? "")")
Within the ContentView, I pass the entries variable to EntryListView, where it displays a list of CFTEntry records. How can I filter by the selectedAccount?
I solved my problem by rearranging the filtering process. No need to @FetchRequest in the ContentView, because it is more cumbersome to deal with the back-and-forth between ContentView and SelectAccountView. Since ContentView already captures the selectedAccount value, I pass it as argument to the EntryListView and carry out the filtering inside.