Passing Data in SwiftUI

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

Lesson 02: Implementing Data Passing Techniques

Demo: Simplifying Data Management with SwiftUI Environment

Episode complete

Play next episode

Next
Transcript

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.

  1. Locate the FinancialEntryRow struct.
  2. Introduce two new properties to inject Environment values:
struct FinancialEntryRow: View {
  // ...

  @Environment(\.expenseTextColor)
  var expenseTextColor: Color
  @Environment(\.incomeTextColor)
  var incomeTextColor: Color

  // ...
}
  1. Update the foregroundColor modifier for the Text view displaying the entry amount to replace the hard-coded colors with expenseTextColor and incomeTextColor:
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:

  1. Build and run the app.
  2. Verify that the text colors for “Income” and “Expense” entries adapt based on the custom environment values: .green for incomes and .red for 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.

See forum comments
Cinema mode Download course materials from Github
Previous: Introducing the SwiftUI Environment Next: Conclusion