Update (6/13/2019): The original version of this post featured the wrong colors in the ColorCompatibility enum at the end of the post. This has now been fixed.

Update (9/21/2019): The original version of this post included systemBrown, a color which was removed in the Xcode 11 GM. It also contained unnecessary availability checks in the code at the bottom (system colors like red, blue etc have been available since iOS 7). The code has been updated, and the tables in this post still list all system and element colors - even those which have been available for a while, since they're different in dark mode vs light mode. I also updated a reference to Mac Catalyst.

Update (10/5/2019): A previous version of this post included the wrong default colors in the ColorCompatibility code at the bottom of the post. These have been updated - the defaults are now the light mode colors which are being used in production in Trestle and CIFilter.io.

Update (10/5/2019): In order to better support bug reporting on ColorCompatibility, I've released it as a library. The code now lives in this GitHub repo. You can read more about it here.

Update (10/17/2019): A previous version of this post included the wrong color values in one of the example code blocks. This has now been fixed, but I'd recommend looking at the ColorCompatibility library if you're interested in using it in your own projects.

At WWDC 2019, Apple announced that Dark Mode would be supported on iOS 13. There are some significant changes to UIKit in order to support this - many of them are detailed in the talk Implementing Dark Mode on iOS which I'd highly recommend watching.

One of the changes that makes adopting Dark Mode so easy is the new system colors API from UIColor . On iOS 12 and older, you might have a label you want to make black, and it would work just fine - in fact, black was the default color for UILabel s.

label. textColor = UIColor. black

But in Dark Mode, the background will also be black, which means the text won't be visible. In iOS 13+, it's better to use the new system color which will respect the user's color scheme preference:

label. textColor = UIColor. label

label is only one example: there are 24 new color scheme agnostic UIColor s available in iOS 13+.

Colors are organized into two groups:

Element Colors (e.g. label ) Standard Colors ( systemIndigo , systemGray3 , etc).

iOS 13 has 23 new element colors ( label etc) and one new standard color ( systemIndigo ). However, even system colors that have been around for a while (like systemRed ) have become dynamic in iOS 13 - they might actually be different colors in dark mode vs light mode.

(Skip to the bottom if you'd like to see a list of the new colors.)

Compatibility

These new colors are all well and good, but most of us with existing apps will still be supporting devices with iOS 12 or lower, at least for a while. This means we'll probably be doing a lot of things like this, using Swift's #available syntax:

if #available ( iOS 13 , * ) { label. textColor = . label } else { label. textColor = . black }

It's a workable solution, but it necessitates changing a lot of code - an if statement for every custom label or background color! For CIFilter.io, I wondered if there was a better way. What if, instead of the if #available , there was a way to abstract the color choice down one level, so we could do something like this?

label. textColor = ColorCompatibility. label

Generating system colors

I wrote a small app (with SwiftUI, no less!) which displays all the system colors in the current color scheme.

Light mode Dark mode

This app collects all the UIColor objects - once we have those, we can use their red/green/blue/alpha components to generate the implementation of ColorCompatibility that we want:

enum ColorCompatibility { static var label: UIColor { if #available ( iOS 13 , * ) { return . label } return UIColor ( red : 0.0 , green : 0.0 , blue : 0.0 , alpha : 1.0 ) } static var secondaryLabel: UIColor { if #available ( iOS 13 , * ) { return . secondaryLabel } return UIColor ( red : 0.23529411764705882 , green : 0.23529411764705882 , blue : 0.2627450980392157 , alpha : 0.6 ) } // ... 21 more definitions: full code in the link at the bottom }

We can then use ColorCompatibility it to set any colors we need.

Note: we can't use @available for these checks, since it doesn't provide a way to check if the current environment is less than a specific app version.

Conclusion

This approach has some great advantages:

This code will compile on iOS 13+, iOS 12 and earlier, and Catalyst ✅ Since everything is a computed var , we never pre-store colors, which means that when the user switches color scheme, our app will automatically adapt as the trait collection changes 👍 When we drop iOS 12 support, cleaning this up will be as simple as replacing every instance of ColorCompatibility with UIColor 🎉

Hopefully this makes your app's transition to dark mode easier!

If you're interested in dark mode and new UIColor apis, you can follow me on Twitter as I continue to play around with these new iOS technologies.

Table of system colors

For those interested in iOS 13+ system colors but not wanting (or not able) to compile the sample app, here's a list of the system colors in light and dark mode, with their hex codes and RGBA values:

Light Mode Name Color Hex String RGBA label #000000ff rgba(0.0, 0.0, 0.0, 1.0) secondaryLabel #3c3c4399 rgba(60.0, 60.0, 67.0, 0.6) tertiaryLabel #3c3c434c rgba(60.0, 60.0, 67.0, 0.3) quaternaryLabel #3c3c432d rgba(60.0, 60.0, 67.0, 0.18) systemFill #78788033 rgba(120.0, 120.0, 128.0, 0.2) secondarySystemFill #78788028 rgba(120.0, 120.0, 128.0, 0.16) tertiarySystemFill #7676801e rgba(118.0, 118.0, 128.0, 0.12) quaternarySystemFill #74748014 rgba(116.0, 116.0, 128.0, 0.08) placeholderText #3c3c434c rgba(60.0, 60.0, 67.0, 0.3) systemBackground #ffffffff rgba(255.0, 255.0, 255.0, 1.0) secondarySystemBackground #f2f2f7ff rgba(242.0, 242.0, 247.0, 1.0) tertiarySystemBackground #ffffffff rgba(255.0, 255.0, 255.0, 1.0) systemGroupedBackground #f2f2f7ff rgba(242.0, 242.0, 247.0, 1.0) secondarySystemGroupedBackground #ffffffff rgba(255.0, 255.0, 255.0, 1.0) tertiarySystemGroupedBackground #f2f2f7ff rgba(242.0, 242.0, 247.0, 1.0) separator #3c3c4349 rgba(60.0, 60.0, 67.0, 0.29) opaqueSeparator #c6c6c8ff rgba(198.0, 198.0, 200.0, 1.0) link #007affff rgba(0.0, 122.0, 255.0, 1.0) darkText #000000ff rgba(0.0, 0.0, 0.0, 1.0) lightText #ffffff99 rgba(255.0, 255.0, 255.0, 0.6) systemBlue #007affff rgba(0.0, 122.0, 255.0, 1.0) systemGreen #34c759ff rgba(52.0, 199.0, 89.0, 1.0) systemIndigo #5856d6ff rgba(88.0, 86.0, 214.0, 1.0) systemOrange #ff9500ff rgba(255.0, 149.0, 0.0, 1.0) systemPink #ff2d55ff rgba(255.0, 45.0, 85.0, 1.0) systemPurple #af52deff rgba(175.0, 82.0, 222.0, 1.0) systemRed #ff3b30ff rgba(255.0, 59.0, 48.0, 1.0) systemTeal #5ac8faff rgba(90.0, 200.0, 250.0, 1.0) systemYellow #ffcc00ff rgba(255.0, 204.0, 0.0, 1.0) systemGray #8e8e93ff rgba(142.0, 142.0, 147.0, 1.0) systemGray2 #aeaeb2ff rgba(174.0, 174.0, 178.0, 1.0) systemGray3 #c7c7ccff rgba(199.0, 199.0, 204.0, 1.0) systemGray4 #d1d1d6ff rgba(209.0, 209.0, 214.0, 1.0) systemGray5 #e5e5eaff rgba(229.0, 229.0, 234.0, 1.0) systemGray6 #f2f2f7ff rgba(242.0, 242.0, 247.0, 1.0) Dark Mode Name Color Hex String RGBA label #ffffffff rgba(255.0, 255.0, 255.0, 1.0) secondaryLabel #ebebf599 rgba(235.0, 235.0, 245.0, 0.6) tertiaryLabel #ebebf54c rgba(235.0, 235.0, 245.0, 0.3) quaternaryLabel #ebebf52d rgba(235.0, 235.0, 245.0, 0.18) systemFill #7878805b rgba(120.0, 120.0, 128.0, 0.36) secondarySystemFill #78788051 rgba(120.0, 120.0, 128.0, 0.32) tertiarySystemFill #7676803d rgba(118.0, 118.0, 128.0, 0.24) quaternarySystemFill #7676802d rgba(118.0, 118.0, 128.0, 0.18) placeholderText #ebebf54c rgba(235.0, 235.0, 245.0, 0.3) systemBackground #000000ff rgba(0.0, 0.0, 0.0, 1.0) secondarySystemBackground #1c1c1eff rgba(28.0, 28.0, 30.0, 1.0) tertiarySystemBackground #2c2c2eff rgba(44.0, 44.0, 46.0, 1.0) systemGroupedBackground #000000ff rgba(0.0, 0.0, 0.0, 1.0) secondarySystemGroupedBackground #1c1c1eff rgba(28.0, 28.0, 30.0, 1.0) tertiarySystemGroupedBackground #2c2c2eff rgba(44.0, 44.0, 46.0, 1.0) separator #54545899 rgba(84.0, 84.0, 88.0, 0.6) opaqueSeparator #38383aff rgba(56.0, 56.0, 58.0, 1.0) link #0984ffff rgba(9.0, 132.0, 255.0, 1.0) darkText #000000ff rgba(0.0, 0.0, 0.0, 1.0) lightText #ffffff99 rgba(255.0, 255.0, 255.0, 0.6) systemBlue #0a84ffff rgba(10.0, 132.0, 255.0, 1.0) systemGreen #30d158ff rgba(48.0, 209.0, 88.0, 1.0) systemIndigo #5e5ce6ff rgba(94.0, 92.0, 230.0, 1.0) systemOrange #ff9f0aff rgba(255.0, 159.0, 10.0, 1.0) systemPink #ff375fff rgba(255.0, 55.0, 95.0, 1.0) systemPurple #bf5af2ff rgba(191.0, 90.0, 242.0, 1.0) systemRed #ff453aff rgba(255.0, 69.0, 58.0, 1.0) systemTeal #64d2ffff rgba(100.0, 210.0, 255.0, 1.0) systemYellow #ffd60aff rgba(255.0, 214.0, 10.0, 1.0) systemGray #8e8e93ff rgba(142.0, 142.0, 147.0, 1.0) systemGray2 #636366ff rgba(99.0, 99.0, 102.0, 1.0) systemGray3 #48484aff rgba(72.0, 72.0, 74.0, 1.0) systemGray4 #3a3a3cff rgba(58.0, 58.0, 60.0, 1.0) systemGray5 #2c2c2eff rgba(44.0, 44.0, 46.0, 1.0) systemGray6 #1c1c1eff rgba(28.0, 28.0, 30.0, 1.0)

ColorCompatibility full code