iOS
Dark Mode
iOS Development
App Design
SwiftUI

How to check for Dark Mode in iOS?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Detecting dark mode on iOS is straightforward on iOS 13+, but implementation details matter when supporting older versions, handling runtime theme changes, and keeping UIKit and SwiftUI behavior consistent. Many bugs come from checking appearance once at launch and never reacting to trait changes. Another common issue is using manual color branching everywhere instead of semantic system colors. This article shows robust dark-mode detection and response patterns for UIKit and SwiftUI applications.

Core Sections

1. Basic UIKit check for current interface style

For iOS 13 and later:

swift
1if #available(iOS 13.0, *) {
2    let isDark = traitCollection.userInterfaceStyle == .dark
3    print("Dark mode:", isDark)
4}

If supporting iOS 12 and below, default to light behavior or custom theme system.

2. React to theme changes at runtime

Users can switch appearance while app is running. In UIKit, override trait-change callback:

swift
1override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
2    super.traitCollectionDidChange(previousTraitCollection)
3
4    if #available(iOS 13.0, *) {
5        if previousTraitCollection?.userInterfaceStyle != traitCollection.userInterfaceStyle {
6            updateTheme()
7        }
8    }
9}

This keeps UI synchronized when appearance changes.

3. Prefer semantic colors over manual branching

Instead of hardcoding black/white values, use semantic system colors:

swift
label.textColor = .label
view.backgroundColor = .systemBackground

These adapt automatically across light/dark mode and reduce conditional styling logic.

4. App-wide appearance override when needed

If product requirements force a mode, set window override:

swift
if #available(iOS 13.0, *) {
    window?.overrideUserInterfaceStyle = .dark // or .light / .unspecified
}

Use this carefully, because it overrides user preference and may affect accessibility expectations.

5. SwiftUI pattern

In SwiftUI, read color scheme from environment and react declaratively.

swift
1struct ContentView: View {
2    @Environment(\.colorScheme) var colorScheme
3
4    var body: some View {
5        Text(colorScheme == .dark ? "Dark" : "Light")
6            .padding()
7            .background(Color(.systemBackground))
8    }
9}

Avoid duplicated style logic by centralizing design tokens.

6. Testing and QA strategy

Test both modes in simulator and physical devices, including transitions while app is foregrounded. Snapshot tests in both schemes are valuable for catching unreadable text or low-contrast icons.

Validation and production readiness

A reliable solution should include explicit validation and observability, not just a working snippet. Add representative test inputs for normal flow, malformed input, and boundary values so behavior is stable under change. Where timing or throughput matters, keep a small benchmark scenario and run it after refactors to catch accidental slowdowns early. If external systems are involved, include retry, timeout, and failure-path tests to verify the system degrades gracefully rather than hanging or failing silently.

Operationally, document assumptions close to the implementation: dependency versions, environment requirements, timezone or locale expectations, and any platform-specific behavior. Add structured logs for key decision points and failures so production incidents are diagnosable without reproducing every condition locally. For teams, define a minimal rollout checklist that covers backward compatibility, monitoring alerts, and rollback steps. These checks reduce incidents caused by integration gaps, which are more common than syntax errors in real deployments.

Common Pitfalls

  • Checking dark mode once at startup and ignoring runtime changes.
  • Hardcoding colors instead of semantic adaptive colors.
  • Forcing app-wide style without clear product or accessibility justification.
  • Forgetting iOS version guards around userInterfaceStyle APIs.
  • Mixing UIKit and SwiftUI theming rules inconsistently across screens.

Summary

Dark mode detection in iOS is simple, but robust support requires runtime trait handling and adaptive design primitives. Use userInterfaceStyle or SwiftUI colorScheme for detection, rely on semantic colors for most UI elements, and test both themes thoroughly. With these patterns, your app remains visually consistent and maintainable across iOS versions and appearance changes.

In practice, documenting this pattern in team standards and validating it in CI prevents recurring regressions and keeps behavior consistent across environments, contributors, and release cycles.


Course illustration
Course illustration

All Rights Reserved.