Codementor Events

SwiftUI app theme switch

Published Jan 29, 2022
SwiftUI app theme switch

This short article will explain how to change your app theme when app is running and all the needed stuff to make it work at runtime.
So first things first, lets create our Xcode project and assets that we will use on one simple view that will demonstrate switch between dark/light theme and also implement the check if system is using dark/light theme.
Open Xcode and create new project like File-> New -> Project. Choose iOS as template.
Screenshot 2022-01-29 at 14.18.08.png

Next, create your identifier and choose SwiftUI as interface and Swift as language.
Screenshot 2022-01-29 at 14.18.34.png

After we have created our project lets organise views into folders and add some colour assets that will distinguish our light from dark mode.
So first create folder Views in root of our project and move ContentView.swift to it and create new HomeView.swift inside same folder.
Screenshot 2022-01-29 at 14.28.22.png

Next lets create our colour Assets. Click on Assets and on navigation bar Right click -> New Colour Set.
Screenshot 2022-01-29 at 14.29.12.png

Lets name our new colour set “Accent”. Lets add three more colour sets and name them:

  • BackgroundColor
  • BackgroundColorList
  • ColorPrimary
  • TextColorPrimary
  • TextColorSecondary
    Screenshot 2022-01-29 at 14.38.38.png

After we have created our set of colours we must create Extension to use our custom colours and modifiers. Create folder Extensions and add Color.swift extension.
Screenshot 2022-01-29 at 14.54.57.png

extension Color {
 static var theme: Color  {
   return Color("theme")
 }
 static var BackgroundColor: Color  {
   return Color("BackgroundColor")
 }
 static var BackgroundColorList: Color  {
   return Color("BackgroundColorList")
 }
 static var ColorPrimary: Color  {
   return Color("ColorPrimary")
 }
 static var Accent: Color  {
   return Color("AccentColor")
 }
 static var TextColorPrimary: Color  {
   return Color("TextColorPrimary")
 }
 static var TextColorSecondary: Color  {
   return Color("TextColorSecondary")
 }
}

Next lets create new folder called Fonts and copy/paste our fancy fonts to new folder.
Screenshot 2022-01-29 at 14.43.39.png

Also create new folder called Utils and add Constants.swift class and UserDefaultsUtils.swift class that will be our singleton for setting/getting our theme of app.

Constants.swift

import Foundation
class Constants {
  public static let DARK_MODE = “DARK_MODE”
  public static let LIGHT_MODE = “LIGHT_MODE”
}

UserDefaultsutils.swift

import Foundation
class UserDefaultsUtils {
static var shared = UserDefaultsUtils()
 func setDarkMode(enable: Bool) {
   let defaults = UserDefaults.standard
   defaults.set(enable, forKey: Constants.DARK_MODE)
 }
 func getDarkMode() -> Bool {
  let defaults = UserDefaults.standard
  return defaults.bool(forKey: Constants.DARK_MODE)
 }
}

So lets add some code to our HomeView.swift. First we are going to add Environment variable that will represent system colour scheme that is currently used if we decide to use same theme as current system theme. Second we will use state property isDarkModeOn that will keep our theme state of our window, and which will be saved to UserDefaults for later use.

@Environment(\.colorScheme) private var colorScheme: ColorScheme
@State private var isDarkModeOn = false

Next lets add our function for setting theme of our app when we open and function for changing theme on Toggle.

func setAppTheme(){
  //MARK: use saved device theme from toggle
  isDarkModeOn = UserDefaultsUtils.shared.getDarkMode()
  changeDarkMode(state: isDarkModeOn)
  //MARK: or use device theme
  /*if (colorScheme == .dark)
  {
    isDarkModeOn = true
  }
  else{
    isDarkModeOn = false
  }
  changeDarkMode(state: isDarkModeOn)*/
}
func changeDarkMode(state: Bool){
  (UIApplication.shared.connectedScenes.first as? 
  IWindowScene)?.windows.first!.overrideUserInterfaceStyle = state ?   .dark : .light
  UserDefaultsUtils.shared.setDarkMode(enable: state)
}

And finally our HomeView.swift body.

var ToggleThemeView: some View {
Toggle("Dark Mode", isOn: $isDarkModeOn).onChange(of: isDarkModeOn) { (state)  in
changeDarkMode(state: state)
}.labelsHidden()
}
var body: some View {
  NavigationView {
   ZStack {
    Color.BackgroundColorList.edgesIgnoringSafeArea(.all)
    VStack(alignment: .leading) {
      Text("Switch   theme").foregroundColor(Color.TextColorSecondary).padding(10).font(F  ont.custom("Baloo-Regular", size: 15))
      ToggleThemeView
 }
 .background(Color.BackgroundColorList)
 .font(Font.custom("Baloo-Regular", size: 20))
 .navigationBarTitle("", displayMode: .inline)
 .navigationBarItems(
 leading:
   Text("DarkModeSwitch").font(Font.custom("Baloo-Regular", size: 20)))
   .navigationBarBackButtonHidden(true)
   foregroundColor(Color.TextColorPrimary)
  }
 }
  .background(Color.BackgroundColorList)
  .navigationViewStyle(StackNavigationViewStyle())
  .onAppear(perform: {
  setAppTheme()
 })
}

So if we open our app first theme will be light. We can toggle it to dark, or use default device theme.
Simulator Screen Shot - iPhone 13 - 2022-01-29 at 16.57.01.png
Simulator Screen Shot - iPhone 13 - 2022-01-29 at 16.57.04.png

That is it. Code is available on GitHub repository.
GitHub

Discover and read more posts from Kenan Begić
get started