Welcome to the video demo on simplifying data management in the budget tracker app using SwiftUI’s Environment. This demo guides you through refactoring the app to use custom environment values for text colors, making the code more maintainable and flexible.
Before diving into code, please open the Starter Xcode project located at 02-implementing-data-passing-techniques/05-demo/Starter/MyBudget.xcodeproj.
The code for this demo all resides in the BudgetTrackerApp.swift file for simplicity. In a real app, you typically would organize your code into separate files for each view.
Your goal in this demo is to leverage SwiftUI’s Environment to efficiently centralize the expense and income text color values.
Step 1: Define Custom Environment Keys
In this step, you’ll define custom Environment keys for managing text colors in the SwiftUI Environment. Follow these detailed instructions:
Step 1.1: Create ExpenseTextColorKey
First, create an Environment key for the expense text color:
At the end of the file, define the ExpenseTextColorKey struct, which conforms to the EnvironmentKey protocol:
struct ExpenseTextColorKey: EnvironmentKey {
static let defaultValue: Color = .red
}
This struct specifies a default color value for expenses, which is red.
Step 1.2: Create IncomeTextColorKey
Next, define an Environment key for the income text color:
Below the ExpenseTextColorKey, define the IncomeTextColorKey struct:
struct IncomeTextColorKey: EnvironmentKey {
static let defaultValue: Color = .green
}
This struct specifies a default color value for income, which is green.
Step 1.3: Add expenseTextColor to EnvironmentValues
Now, extend EnvironmentValues to include the property for the expense text color:
Below the IncomeTextColorKey struct, extend EnvironmentValues and add the expenseTextColor property:
extension EnvironmentValues {
var expenseTextColor: Color {
get { self[ExpenseTextColorKey.self] }
set { self[ExpenseTextColorKey.self] = newValue }
}
}
This computed property uses the ExpenseTextColorKey to get and set the expense text color in the Environment.
Step 1.4: Add incomeTextColor to EnvironmentValues
Next, add the property for the income text color to the same extension:
In the EnvironmentValues extension, below the expenseTextColor property, add the incomeTextColor property:
extension EnvironmentValues {
// ...
var incomeTextColor: Color {
get { self[IncomeTextColorKey.self] }
set { self[IncomeTextColorKey.self] = newValue }
}
}
This computed property uses the IncomeTextColorKey to get and set the income text color in the Environment.
Step 2: Apply Custom Environment Values
Now, adjust the FinancialEntryRow view to use the newly created Environment values instead of hard-coded colors.
-
Locate the
FinancialEntryRowstruct. - Introduce two new properties to inject Environment values:
struct FinancialEntryRow: View {
// ...
@Environment(\.expenseTextColor)
var expenseTextColor: Color
@Environment(\.incomeTextColor)
var incomeTextColor: Color
// ...
}
-
Update the
foregroundColormodifier for theTextview displaying the entry amount to replace the hard-coded colors withexpenseTextColorandincomeTextColor:
struct FinancialEntryRow: View {
// ...
var body: some View {
HStack {
// ...
Text("$\(entry.amount, specifier: "%.2f")")
.foregroundColor(entry.isExpense ?
expenseTextColor : incomeTextColor)
}
}
}
With these changes, FinancialEntryRow now dynamically adapts its text color based on the Environment, significantly simplifying color management.
Step 3: Testing the Changes
It’s time to test the modifications:
- Build and run the app.
-
Verify that the text colors for “Income” and “Expense” entries adapt based on the custom environment values:
.greenfor incomes and.redfor expenses.
Step 4: Overriding Environment Values in SwiftUI
In the previous steps, you defined custom Environment values in the budget tracker app. Now, explore how to override these values for specific views. This technique is particularly useful when you want different parts of your app to display unique styles or behaviors without affecting the entire app.
Navigate to the ContentView Struct
Still in the same file, in ContentView, there’s a list that displays financial entries. Each entry is represented by a FinancialEntryRow. You’re going to override the Environment values specifically for these rows.
Overriding the Expense Text Color
Find the ForEach loop in the List that iterates over the entries. Here’s the code snippet where FinancialEntryRow is instantiated for each entry:
ForEach(entries) { entry in
FinancialEntryRow(entry: entry)
}
Add an Environment modifier right after FinancialEntryRow(entry: entry). This overrides the expenseTextColor for this specific instance of FinancialEntryRow.
ForEach(entries) { entry in
FinancialEntryRow(entry: entry)
.environment(\.expenseTextColor, .orange)
}
By adding this line, you instruct SwiftUI to apply an orange color to the expense text only for entries displayed in this list. It’s a powerful way to customize parts of your UI on the fly.
Reflecting on the Code Changes
With this addition, you’ve customized the appearance of your financial entries without changing the global Environment color value for expense text. This local override affects only the FinancialEntryRows in this particular list, demonstrating the flexibility of SwiftUI’s Environment system.
Test the Changes
Build and run the app to see your changes. You should see that the text color for expenses now appears in orange, differentiating it from other areas of the app where the default red color might still be used.
Conclusion of the Override Segment
This step has shown you how to effectively use Environment overrides to achieve local customizations in your SwiftUI applications. By using the .environment modifier, you can specify different styles and behaviors for specific components, enhancing your UI’s flexibility and maintainability.
Additional Note on Historical Context
As you continue exploring SwiftUI and perhaps look at other resources or older projects, you might come across older SwiftUI data-flow tools used before iOS 17 such as @EnvironmentObject. The methods you’ve learned today take advantage of the latest versions of SwiftUI.
Wrapping Up the Video Demo
Congratulations! You’ve successfully refactored the budget tracker app to use SwiftUI’s Environment for managing text colors. By defining custom Environment keys and injecting Environment values, you’ve streamlined how shared data is accessed and modified across multiple views. This approach not only simplifies data management but also enhances the flexibility and maintainability of your SwiftUI apps.
Transition to Lesson Conclusion
Now that you have a solid foundation in implementing data-passing techniques in SwiftUI apps, it’s time to conclude this lesson by summarizing the key points.