Implementing themes and custom color schemes in SwiftUI.
SwiftUI’s @AppStorage
is useful for storing simple user preferences, but it only supports basic data types such as String, Int, and Bool , making it less suitable for complex data structures.
We can declare a custom enum to represent different themes, such as light, dark, and system. And conform it to the String
or Int
protocol that supported by @AppStorage
. This allows us to store the user’s theme preference in UserDefaults and apply it throughout the app.
Here is the code.
1enum Theme:String, CaseIterable {
2 case light
3 case dark
4 case system
5 var colorScheme: ColorScheme? {
6 switch self {
7 case .light:
8 return .light
9 case .dark:
10 return .dark
11 case .system:
12 return nil
13 }
14 }
15}
And example for tint color.
1enum TintColor: String,CaseIterable {
2 case blue, green, red, yellow, purple, pink, orange, brown, gray
3
4 var color:Color {
5 switch self {
6 case .blue:
7 return Color.blue
8 case .green:
9 return Color.green
10 case .red:
11 return Color.red
12 case .yellow:
13 return Color.yellow
14 case .purple:
15 return Color.purple
16 case .pink:
17 return Color.pink
18 case .orange:
19 return Color.orange
20 case .brown:
21 return Color.brown
22 case .gray:
23 return Color.gray
24 }
25 }
26}
Since the enum conforms to CaseIterable
, we can easily generate a list of available themes and colors for users to choose from in the settings view. You can then bind the selected theme and tint color to the settings view using @AppStorage
, like this:
1struct SettingView: View {
2 @AppStorage("theme") private var theme: Theme = .system
3 @AppStorage("tintColor") private var tintColor: TintColor = .blue
4 var body: some View {
5 Form{
6 Picker("Theme", selection: $theme) {
7 ForEach(Theme.allCases, id: .self) { theme in
8 Text(theme.rawValue.capitalized).tag(theme)
9 }
10 }
11 Picker("Tint Color", selection: $tintColor) {
12 ForEach(TintColor.allCases, id: .self) { color in
13 Text(color.rawValue.capitalized).tag(color)
14 }
15 }
16 }
17 }
18}
Now you can apply the theme and tint color to your main view using preferredColorScheme
and tint
modifiers.
1@main
2struct NavigationDemoApp: App {
3 @AppStorage("theme") private var theme: Theme = .system
4 @AppStorage("tintColor") private var tintColor: TintColor = .blue
5 var body: some Scene {
6 WindowGroup {
7 ContentView()
8 .preferredColorScheme(theme.colorScheme)
9 .tint(tintColor.color)
10 }
11 }
12}
The code is available in this repository
Just noticed the color change doesn’t apply immediately on the picker. I've submitted feedback to Apple about this issue.
Last Update: Jul.2025