- Published on
Inspecting all SwiftUI Environment Values
- Authors
- Name
- Mick MacCallum
- @0x7fs
When debugging SwiftUI views, understanding what environment values are available at different points in your view hierarchy can be incredibly helpful. SwiftUI's environment system passes values down through the view tree, but sometimes you need to inspect what's actually being used at a specific level. This article shows you how to efficiently examine and debug environment values for better development workflow.
Understanding SwiftUI Environment
SwiftUI uses the environment to pass values down the view hierarchy. You can access these values using the @Environment property wrapper:
struct ContentView: View {
@Environment(\.colorScheme) var colorScheme
@Environment(\.horizontalSizeClass) var horizontalSizeClass
@Environment(\.scenePhase) var scenePhase
var body: some View {
Text("Current color scheme: \(colorScheme == .dark ? "Dark" : "Light")")
}
}
You can also create custom environment values and inject them at different levels:
private struct ThemeColorKey: EnvironmentKey {
static let defaultValue = Color.blue
}
extension EnvironmentValues {
var themeColor: Color {
get { self[ThemeColorKey.self] }
set { self[ThemeColorKey.self] = newValue }
}
}
Simple Environment Value Inspection
For debugging a single environment value, you can use the transformEnvironment modifier combined with Swift's dump function:
Text("Hello, World!")
.transformEnvironment(\.themeColor) { color in
dump(color)
}
This will print the color value to the console in a readable format:
▿ blue
▿ provider: SwiftUI.(unknown context at $7fff5dc005b0).ColorBox<SwiftUI.SystemColorType> #0
- super: SwiftUI.AnyColorBox
- super: SwiftUI.AnyShapeStyleBox
- base: SwiftUI.SystemColorType.blue
Examining All Environment Values
When you need to inspect all environment values at once, you can create a wrapper view that captures the entire environment:
struct EnvironmentInspector<Content: View>: View {
@Environment(\.self) var environment
let content: Content
init(@ViewBuilder content: () -> Content) {
self.content = content()
}
var body: some View {
content
.onAppear {
print("--- Environment Values - BEGIN ---")
inspectEnvironmentValues(environment)
print("--- Environment Values - END ---")
}
}
}
However, using dump directly produces an overwhelming amount of output (over 1000 lines!). Instead, we need a more efficient approach to filter and format the environment values.
Efficient Environment Inspection
Here's a more practical solution that filters out duplicate entries and provides cleaner output:
func inspectEnvironmentValues(_ environment: EnvironmentValues) {
let mirror = Mirror(reflecting: environment)
for child in mirror.children {
guard let label = child.label else { continue }
// Extract the key name from the label
let keyName = String(label)
.replacingOccurrences(of: "EnvironmentPropertyKey<", with: "")
.replacingOccurrences(of: ">", with: "")
// Get a readable value description
let valueDescription = String(describing: child.value)
print("\(keyName) = \(valueDescription)")
}
}
You can use this with a simple view modifier:
extension View {
func inspectEnvironment() -> some View {
self.modifier(EnvironmentInspectModifier())
}
}
struct EnvironmentInspectModifier: ViewModifier {
@Environment(\.self) var environment
func body(content: Content) -> some View {
content
.onAppear {
print("--- Environment Values - BEGIN ---")
inspectEnvironmentValues(environment)
print("--- Environment Values - END ---")
}
}
}
Practical Usage Examples
Debugging Custom Environment Values
When working with custom environment values, you can easily see what's being passed down:
struct MyView: View {
var body: some View {
VStack {
Text("Theme Color Test")
.foregroundColor(.themeColor)
.inspectEnvironment()
}
.environment(\.themeColor, .green)
}
}
This will show you the current theme color value along with all other environment values.
Inspecting Different Hierarchy Levels
You can examine environment values at different levels to understand how they propagate:
struct HierarchyExample: View {
var body: some View {
VStack {
Text("Root Level")
.inspectEnvironment()
VStack {
Text("Nested Level")
.inspectEnvironment()
}
.environment(\.themeColor, .red)
}
.environment(\.themeColor, .blue)
}
}
Conditional Debugging
You can make environment inspection conditional based on build configuration:
extension View {
func debugEnvironment() -> some View {
#if DEBUG
return self.inspectEnvironment()
#else
return self
#endif
}
}
Filtered Environment Inspection
For more targeted debugging, you can create a function that only shows specific environment values:
func inspectSpecificEnvironmentValues(_ environment: EnvironmentValues, keys: [String]) {
let mirror = Mirror(reflecting: environment)
for child in mirror.children {
guard let label = child.label else { continue }
let keyName = String(label)
.replacingOccurrences(of: "EnvironmentPropertyKey<", with: "")
.replacingOccurrences(of: ">", with: "")
if keys.contains(keyName) {
let valueDescription = String(describing: child.value)
print("\(keyName) = \(valueDescription)")
}
}
}
// Usage
Text("Debug View")
.onAppear {
inspectSpecificEnvironmentValues(environment, keys: ["ColorSchemeKey", "HorizontalSizeClassKey"])
}
Best Practices
Use in Debug Builds Only: Always wrap environment inspection in
#if DEBUG
to avoid performance impact in release builds.Target Specific Views: Only inspect environment values for views you're actively debugging to avoid console spam.
Filter Output: Use the filtered approach when you only need specific environment values.
Environment value inspection is a powerful debugging tool that can help you understand how SwiftUI's environment system works and troubleshoot issues with custom environment values. By using these efficient inspection techniques, you can quickly identify what values are available at any point in your view hierarchy.