State Management in SwiftUI

Jun 20 2024 · Swift 5.9, iOS 17.2, Xcode 15.2

Lesson 02: Managing Local View State with @State & @Binding

Building Financial Entry Form Using @State & @Binding Demo

Episode complete

Play next episode

Next

Heads up... You’re accessing parts of this content for free, with some sections shown as efkuhcegyw text.

Heads up... You’re accessing parts of this content for free, with some sections shown as uzwuhxumup text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

00:01Welcome to this video demo on building a financial entry form for a budget-tracking app using @State and @Binding. This session is designed to extend your understanding of form controls, modal presentation, and data flow within SwiftUI.

00:48In this first part of the demo, you’ll build the controls for users to enter a new entry in the budget-tracking app. By incorporating state management techniques, you’ll ensure the form is reactive and updates dynamically with user interactions.

01:18First, you’ll implement the amount TextField for entering the monetary value of the transaction. This control allows users to specify the amount involved in each financial entry.

01:28Above AddFinancialEntryView’s body, define a new amount state property to store the numeric value entered by the user:

@State private var amount: Double = 0
TextField("Amount", value: $amount, format: .number)
  .keyboardType(.decimalPad)

02:09Run the preview to check the functionality of the amount field. Notice the reactive behavior of the text field as it updates with each keystroke.

02:17Next, you’ll implement the category TextField, which allows users to organize their financial entries by category.

02:23Below the amount state property, introduce a new category state property to capture the text input from the user:

@State private var category: String = ""
TextField("Category", text: $category)

02:53Run the preview and ensure the category text field updates reactively with each keystroke.

02:59Finally, implement a toggle to allow users to denote if the entry is an expense.

03:04Below the category state property, add a state variable to store whether the new entry is an expense:

@State private var isExpense = true
Toggle(isOn: $isExpense) {
  Text("Is Expense")
}

03:38Run the preview. Confirm that the toggle switches states effectively and updates the user interface as expected.

03:53In this part of the demo, you’ll present the financial entry form view using a SwiftUI sheet.

03:58In SwiftUI, sheets are used to present new content modally over existing content. To manage the presentation of a sheet, you need a binding to a Boolean state variable. This state controls whether the sheet is presented or not. By toggling this Boolean value, you can show or hide the sheet dynamically.

04:60First, modify AddFinancialEntryView to include a @Binding property. This property will allow the form to control its presentation state directly.

@Binding var showingAddView: Bool

05:34Next, add a toolbar button to handle cancellation — to dismiss the form without saving the new entry.

ToolbarItem(placement: .cancellationAction) {
  Button("Cancel") {
    showingAddView.toggle()
  }
}

06:04Before running the preview to see the cancel button, you need to make some changes to your preview code to accommodate the new @Binding property and sheet presentation logic.

@State private var showingAddView = true
AddFinancialEntryView(showingAddView: $showingAddView)

06:36First, open BudgetTrackerApp.swift to configure the main user interface for presenting the form. This step involves preparing ContentView to manage the visibility of the form with a state variable.

06:49In ContentView under the entries state property, add a new state property to track whether the form should be shown. This Boolean state acts as a control for the modal presentation of the sheet:

@State private var showingAddView = false

07:14Modify the plus navigation bar item to present the sheet when pressed. Replace the bar button’s action with:

self.showingAddView = true

07:28Attach the sheet view modifier to List in ContentView after the .navigationBarItems view modifier:

.sheet(isPresented: $showingAddView) {
    AddFinancialEntryView(showingAddView: $showingAddView)
}

07:54Run the preview to see the implementation in action. Click the add button to present the form and observe it overlaying the current content. Test the Cancel button within the form

08:32The next step is enabling the saving of new entries. This involves passing data between views using bindings.

08:39First, you’ll add a @Binding property to AddFinancialEntryView to directly access and modify the main list of financial entries. Next, you’ll implement a save button within the form’s toolbar, which will save new entries to this array and dismiss the form. Finally, you’ll pass the ContentView’s entries state property to AddFinancialEntryView as a binding to keep all data perfectly in sync.

09:05First, modify AddFinancialEntryView to include a @Binding property for the array of financial entries. This lets the form modify the array directly.

@Binding var financialEntries: [FinancialEntry]
@State private var financialEntries: [FinancialEntry] = []
AddFinancialEntryView(showingAddView: $showingAddView, financialEntries: $financialEntries)

09:58Next, add a toolbar item for the save button under the cancel toolbar item inside the .toolbar view modifier.

ToolbarItem(placement: .confirmationAction) {
  Button("Save") {
    let newEntry = FinancialEntry(id: UUID(), amount: amount, category: category, isExpense: isExpense)
    financialEntries.append(newEntry)
    showingAddView.toggle()
  }
}

10:23Finally, ensure that the ContentView passes the financialEntries array as a binding when presenting AddFinancialEntryView.

.sheet(isPresented: $showingAddView) {
  AddFinancialEntryView(showingAddView: $showingAddView, financialEntries: $entries)
}

10:58Run the preview to try out adding a new entry and seeing it show up in the list.

11:11You’ve now successfully built the budget-tracking app’s entry form!

See forum comments
Cinema mode Download course materials from Github
Previous: Passing Mutable @State using @Binding Next: Conclusion