SwiftUI - how to include a second filter to retrieve CoreData

33 Views Asked by At

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?

1

There are 1 best solutions below

0
sugarakis On

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.