How to apply a global theme for an App using TCA? #1178
-
Hi there, We are developing an app using TCA. In the app, there are multiple SwiftUi views and its corresponding At his point, we are trying to introduce a theme state which contains info like colors, fonts, etc. and will be applied to all SwiftUI views in the app. Whenever the theme changes, the new theme will be propagated to all view To do this, we have a theme state which acts like a global theme for the whole app in the top layer of the One downside of this approach is that there are repetitivenesses of the Is there a better solution? What is the best practice to handle this bases on TCA? Thanks. |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 1 reply
-
Hello, public struct Theme: Equatable {}
enum ThemeKey: EnvironmentKey {
static var defaultValue: Theme { Theme() }
}
extension EnvironmentValues {
public var theme: Theme {
get { self[ThemeKey.self] }
set { self[ThemeKey.self] = newValue }
}
} In any view you want to use the theme, you can expose a @Environment(\.theme) var theme
var body: some View {
Text("Hello!")
.background(theme.backgroundColor)
} If struct ThemeApplicator: ViewModifier {
var store: Store<Theme, Never>
func body(content: Content) -> some View {
WithViewStore(store) { viewStore in
content.environment(\.theme, viewStore.state)
}
}
} You can activate this modifier using: var body: some View {
NavigationView {
…
}
.modifier(
ThemeApplicator(store: store.scope(state: \.theme).actionless)
)
} That's how I would do if the Alternatively, it is also possible that extracting the If your struct ThemeWrapper: Equatable {
let theme: Theme
let token: UUID = UUID()
static func == (a: Self, b: Self) -> Bool { a.token == b.token }
} You modify your modifier to accept a …
case .themeEditor:
state.themeWrapper = ThemeWrapper(theme: state.theme)
return .none Alternatively, if your theme storage offers a publisher of some sort, you can rely on that. |
Beta Was this translation helpful? Give feedback.
-
In addition to what @tgrapperon said, I'd also like to add that if the theme data is not needed in your business logic, then it does not need to be in state. It is completely fine to have view-specific data that is not visible to the reducers, and to feed that data throughout your views via SwiftUI's environment. |
Beta Was this translation helpful? Give feedback.
-
Thanks both of you, @tgrapperon and @mbrandonw. Your proposal and suggestion perfectly solve our problem. |
Beta Was this translation helpful? Give feedback.
Hello,
You can probably use SwiftUI's
Environment
to handle the propagation for you. You could probably do something like this:In any view you want to use the theme, you can expose a
theme
value:If
var theme: Theme
is a property storing your theme in your root state, and assuming it isEquatable
, you can scope into this value an…