Welcome to the first Passing Data in SwiftUI demo. In this demo, you’ll explore the essentials of data flow in SwiftUI using a simple budget tracking app as an example. Time to dive in!
In SwiftUI, understanding data flow is crucial for building dynamic and responsive apps. As you learned in the previous section, data flow refers to how data moves through your app’s view hierarchy, ensuring the UI stays consistent with the underlying data. This includes passing data from one view to another and updating the UI when data changes.
The example budget tracking app for this demo allows users to view their income and expenses. Go ahead and open the MyBudget.xcodeproj
Xcode final project for this lesson and open the BudgetTrackerApp.swift
file. To see what the app looks like, look at the right side at the live preview. You can see the app’s user interface rendered in the Xcode canvas. The app displays a list of income and expense items.
Now that you’ve seen what the app looks like, it’s time to explore the code to see how data flows through the app’s view hierarchy to render the list of budget entries. For this demo, all the app’s code is in the BudgetTrackerApp.swift
file for demonstration purposes only. This makes it easy for you to see how data flows through the whole app. In practice, you would create new files for each view.
Ok, to see how data flows through the app, you’ll walk through all the stages SwiftUI goes through to display the app’s initial UI. First, SwiftUI starts with the entry point of the app. Towards the top of the BudgetTrackerApp.swift
file, find the BudgetTrackerApp
struct:
@main
struct BudgetTrackerApp: App {
let entries = [
FinancialEntry(id: UUID(), amount: 3000, category: "Income", isExpense: false),
FinancialEntry(id: UUID(), amount: 120, category: "Groceries", isExpense: true),
FinancialEntry(id: UUID(), amount: 500, category: "Technology", isExpense: true),
FinancialEntry(id: UUID(), amount: 10, category: "Subscription", isExpense: true)
]
var body: some Scene {
WindowGroup {
ContentView(entries: entries)
}
}
}
The BudgetTrackerApp
struct is the app’s entry point. Here, the app defines an array of financial entries called entries
, each representing an income or expense item. These entries flow through the app’s view hierarchy to display the list of entries starting from here.
The entries
array is passed to the ContentView
, which is responsible for displaying the entries list. This demonstrates the first aspect of data flow in SwiftUI: data passing.
Below BudgetTrackerApp
, you’ll find the ContentView
struct:
struct ContentView: View {
let entries: [FinancialEntry]
var body: some View {
NavigationView {
List {
Section(header: Text("Entries")) {
ForEach(entries) { entry in
FinancialEntryRow(entry: entry)
}
}
}
.navigationTitle("Budget Tracker")
}
}
}
The ContentView
struct uses a List
to display each financial entry by iterating over the entries array and passing each entry into a FinancialEntryRow
intializer, creating a FinancialEntryRow
view for each entry.
FinancialEntryRow
is declared next in the file as follows:
struct FinancialEntryRow: View {
let entry: FinancialEntry
var body: some View {
HStack {
Text(entry.isExpense ? "Expense" : "Income")
Spacer()
Text("$\(entry.amount, specifier: "%.2f")")
.foregroundColor(entry.isExpense ? .red : .green)
}
}
}
The FinancialEntryRow
view initializer takes a single FinancialEntry
as a parameter. This view uses an HStack
to display the entry’s category and amount, with conditional formatting based on whether it’s an expense or income. Notice how the isExpense
and amount
properties from the entry
passed into this view are used to decide which String
s are passed into each Text
view. For example, take isExpense
: If the entry is an expense, "Expense"
is passed into the first Text
view; otherwise, "Income"
is passed into the Text
view.
As noted before, Swift autogenerates intializers for structs. This is how you can initialize FinancialEntryRow
and ContentView
, even though they have no initializers defined.
Putting it all together, this setup illustrates how data is passed down the view hierarchy, via initializers, 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. This is how SwiftUI renders the initial UI of the app. With this code so far, once the app displays the list of entries, the app can’t change the UI. All the app can do is show the list of entries hard-coded in the app entry point. In Module 2, you’ll improve the example to allow for UI updates like adding new entries.
This concludes the demo. Next, you’ll wrap up the lesson.