Welcome to the video demo for “Implementing Data Passing Techniques in SwiftUI”. In this demo, you’ll build the version of the budget tracking app you saw in Lesson 1’s video demo.
Before starting to write code, open the Starter Xcode project found in 02-implementing-data-passing-techniques/03-demo/Starter/MyBudget.xcodeproj.
The code for this demo all sits in the BudgetTrackerApp.swift file for simplicity. In a real app, you typically would organize your code into separate files for each view.
Step 1: Create the ContentView
First, you’ll create a new SwiftUI view called ContentView. This view will display our list of financial entries.
Below the BudgetTrackerApp struct, start by defining a new struct called ContentView that conforms to the View protocol with a placeholder Text body.
struct ContentView: View {
var body: some View {
Text("Placeholder")
}
}
Next, add let entries: [FinancialEntry] at the top of the ContentView struct to hold the financial entries.
struct ContentView: View {
let entries: [FinancialEntry]
var body: some View {
Text("Placeholder")
}
}
You’ll pass the array of entries into this content view to display them in a list. For now, the content view just displays a placeholder Text view. You’ll replace this with the List shortly.
Step 2: Display ContentView in the App
Next, add the ContentView to the app’s WindowGroup in the body property of the BudgetTrackerApp struct. To do this, add ContentView(entries: entries) inside the WindowGroup braces, passing the entries array to the ContentView.
@main
struct BudgetTrackerApp: App {
// ...
var body: some Scene {
WindowGroup {
ContentView(entries: entries)
}
}
}
Here, you pass the entries array from BudgetTrackerApp to the ContentView.
Step 3: Add a List to ContentView
Now, you’ll replace the placeholder Text view in ContentView with a List that iterates over the entries array. To do this, inside the body property of ContentView, replace the Text("Placeholder") with a List taking the entries array and an empty trailing closure as parameters, just like the example below. This creates a List that iterates over the entries array.
struct ContentView: View {
// ...
var body: some View {
List(entries) { entry in
}
}
}
Next, in the trailing closure of the List constructor, add a Text view to display the amount of each financial entry: Text("$\(entry.amount, specifier: "%.2f")").
struct ContentView: View {
// ...
var body: some View {
List(entries) { entry in
Text("$\(entry.amount, specifier: "%.2f")")
}
}
}
This List now displays the amount of each financial entry. Build and run the app to see the list in action.
Step 4: Add a NavigationView
To give the app a more polished look, add a NavigationView and a navigation title. To do this, wrap the List in NavigationView { } and then add .navigationTitle("Budget Tracker") to the List.
struct ContentView: View {
// ...
var body: some View {
NavigationView {
List(entries) { entry in
Text("$\(entry.amount, specifier: "%.2f")")
}
.navigationTitle("Budget Tracker")
}
}
}
Build and run the app again to see the navigation bar with the title Budget Tracker.
Step 5: Create a Custom Row View
To display more information about each entry, create a custom row view called FinancialEntryRow.
First, below the ContentView struct, define a new struct called FinancialEntryRow. Then, add a let entry: FinancialEntry property to hold a single financial entry. You’ll pass an individual FinancialEntry into this custom row view to display its details.
struct FinancialEntryRow: View {
let entry: FinancialEntry
}
You’ll see a compiler error because you didn’t implement the body property of the view. You’ll do that next. Add the body property at the bottom of the FinancialEntryRow. Inside it, create an HStack to arrange the views horizontally:
struct FinancialEntryRow: View {
// ...
var body: some View {
HStack {
}
}
}
Now, add the first Text view inside the HStack to display whether the entry is an expense or income: Text(entry.isExpense ? "Expense" : "Income").
struct FinancialEntryRow: View {
// ...
var body: some View {
HStack {
Text(entry.isExpense ? "Expense" : "Income")
}
}
}
Then, add a Spacer() to push the Text to the left side of the HStack.
struct FinancialEntryRow: View {
// ...
var body: some View {
HStack {
Text(entry.isExpense ? "Expense" : "Income")
Spacer()
}
}
}
Finally, add the second Text view at the end of the HStack to display the amount together with a view modifier to change its color based on whether the entry is an expense or income.
struct FinancialEntryRow: View {
// ...
var body: some View {
HStack {
Text(entry.isExpense ? "Expense" : "Income")
Spacer()
Text("$\(entry.amount, specifier: "%.2f")")
.foregroundColor(entry.isExpense ? .red : .green)
}
}
}
Step 6: Use FinancialEntryRow in the List
Finally, use FinancialEntryRow in the List in ContentView to display each entry. To do this, replace Text("$\(entry.amount, specifier: "%.2f")") in the List with FinancialEntryRow(entry: entry).
struct ContentView: View {
// ...
var body: some View {
NavigationView {
List(entries) { entry in
FinancialEntryRow(entry: entry)
}
// ...
}
}
}
Here, you pass each entry from the List to the FinancialEntryRow, allowing it to display the entry’s details.
Build and run the app one last time. You should now see a list of financial entries with each labeled as “Income” or “Expense” and the amount colored accordingly.
This setup illustrates how data is passed down the view hierarchy, from the BudgetTrackerApp to ContentView, and then to each FinancialEntryRow. Each view uses the data it receives to render its content, creating a data-driven user interface.
Wrapping Up
This lesson has provided the following key learnings:
- Implemented data-passing techniques in SwiftUI, from creating custom views to passing data down the view hierarchy.
- Demonstrated the use of initializers to pass data between views and render content dynamically based on received data.
-
Utilized SwiftUI constructs like
Listto iterate over data and display it in a user-friendly manner.
However, as you’ve seen, passing data through each level of the view hierarchy can become cumbersome, especially for data shared across multiple views. In the next section, you’ll explore how to use the SwiftUI Environment to pass shared data easily throughout an app’s view hierarchy.