I’m new to SwiftUI and trying to start using it in a complex, existing UIKit app. The app has a theming system, and I’m not sure how to get the SwiftUI view to respond to theme change events.
Our theme objects look like
class ThemeService {
static var textColor: UIColor { get }
}
struct ViewTheme: {
private(set) var textColor = { ThemeService.textColor }
}
where the value returned ThemeService.textColor
changes when the user changes the app’s theme. In the UIKit portions of the app, views observe a "themeChanged"
Notification
and re-read the value of the textColor
property from their theme structs.
I’m not sure how to manage this for SwiftUI. Since ViewTheme
isn’t an object, I can’t use @ObservableObject
, but its textColor
property also doesn’t change when the theme changes; just the value returned by calling it changes.
Is there a way to somehow get SwiftUI to re-render the view hierarchy from an external event, rather than from a change in a value that the view sees? Or should I be approaching this differently?
2
Answers
I was able to get this working by cheating with the theming system a little and changing the
ViewTheme
to an object:Now the view can mark it with
@ObservedObject
and will be re-generated when the"themeChanged"
notification is fired.This seems to be working great, but I'm not sure if there are non-obvious problems that I'm missing with this solution.
Your answer works perfectly well, but it requires adoption of ObservableObject. Here is an alternative answer which uses your existing NotificationCenter notifications to update a SwiftUI view.
This requires a @State variable to hold the theme’s current data, but it’s correct because a SwiftUI view is really just a snapshot of what the view should currently display. It ideally should not reference data that is arbitrarily changed because it leads to data-sync problems like the question you asked. So in a SwiftUI view, it is problematic to write
ThemeService.textColor
directly within the body unless you are certain an update will always occur after it the theme gets changed.