``` title = "Theming iOS Apps is No Longer Hard" tags = ["swift"] date = "2023-10-02 22:37:42 -0400" short_desc = "" slug = "custom-traits" ``` Well, at least not for the [same reasons](/2023/theming-ios-apps/). I figured I'd write a brief follow-up post, but unless you've been living under a rock, you'll have heard that UIKit gained support for custom traits in `UITraitCollection` with iOS 17. They work very similarly to SwiftUI's environment and are exactly what I wanted. To create a custom trait, you define a type conforming to `UITraitDefinition` which serves as the key for your trait: ```swift struct PureBlackDarkModeTrait: UITraitDefinition { static let defaultValue = true static let affectsColorAppearance = true } ``` The default value is, well, the default value. That is, what will be read when the trait's not explicitly defined in the trait environment. The trait definition also specifies whether this trait can effect the appearance of colors, meaning whether dynamic `UIColor`s should be reëvaluted when the trait's value changes. Then, you can define an extension on `UITraitCollection` which provides more idiomatic access to the trait (rather than always explicitly looking it up with the type): ```swift extension UITraitCollection { var pureBlackDarkMode: Bool { self[PureBlackDarkModeTrait.self] } } ``` One place where UIKit differs slightly from SwiftUI is that trait setters are defined on a separate type: `UIMutableTraits`. So, one more extension: ```swift extension UIMutableTraits { var pureBlackDarkMode: Bool { get { self[PureBlackDarkModeTrait.self] } set { self[PureBlackDarkModeTrait.self] = newValue } } } ``` And with that, I can continue using my custom trait just as I was prior to iOS 16, but without any of the pile of hacks. It's a pretty minor thing, objectively speaking, but this is a strong contender for my favorite feature of iOS 17.