ThemeManager @EnvironmentObject injection
App-wide theming via a single ObservableObject injected at the root — theme changes propagate instantly to all views.
Problem
Passing theme colors through view initializers creates deep prop-drilling chains. Storing colors in static globals makes runtime theme switching impossible. Neither approach scales when theme changes need to be reflected immediately across unrelated views.
Solution
Create a ThemeManager: ObservableObject that holds a ThemeColors struct with semantic color properties (e.g., background, accent, text). Inject it at the app root with .environmentObject(themeManager). Any view that declares @EnvironmentObject var themeManager: ThemeManager automatically receives updates when the active theme changes.
In FortuneTeller, this drives unlockable themes — when a user earns a new theme through achievements, ThemeManager.setTheme() triggers an immediate full-app re-render.
Usage
Apply when the app has more than one theme, or when colors need to be swapped at runtime. Use semantic color names in ThemeColors (not blue1, darkGray) so designers can adjust palettes without hunting call sites. Prefer this over @AppStorage for multi-property theme bundles — @AppStorage works better for single-flag toggles like sound on/off.
Trade-offs
- Pro: One source of truth for all colors; theme switches are instant and require no view-layer changes; semantic naming makes palette adjustments trivial.
- Con: Views become implicitly dependent on the environment — missing the injection at the root crashes at runtime with an unhelpful error; harder to test in isolation without injecting a stub
ThemeManager.
Context
ThemeColors struct with semantic color properties.
Reusability
- Score: 3/5
- Used in: FortuneTeller
Source
Synced from ops-core learning data. Original source: FortuneTeller