The Entry macro reduces the boilerplate when customizing the SwiftUI environment.
Creating New Environment Values – A Recap
Adding our own values to the SwiftUI environment is a common operation but it requires some boilerplate code each time. See SwiftUI Custom Environment Values for an example but hereโs a quick recap:
-
Create the environment key with a default value:
private struct CaptionColorKey: EnvironmentKey { static let defaultValue = Color(.secondarySystemBackground) }
-
Extend the environment adding a getter/setter for our key:
extension EnvironmentValues { var captionBackgroundColor: Color { get { self[CaptionColorKey.self] } set { self[CaptionColorKey.self] = newValue } } }
At this point we can already use our custom environment value:
ContentView() .environment(\.captionBackgroundColor, .yellow)
Then in the
ContentView
:struct ContentView: View { @Environment(\.captionBackgroundColor) var captionBackgroundColor var body: some View { Text("Hello, world!") .background(captionBackgroundColor) } }
-
An optional third step adds a view modifier to allow a more compact syntax:
extension View { func captionBackgroundColor(_ color: Color) -> some View { environment(\.captionBackgroundColor, color) } }
That allows us to write:
ContentView() .captionBackgroundColor(.yellow)
The Entry
macro helps us with the first two steps.
Using the Entry Macro
The Entry
macro allows us to replace the first two steps, directly extending the environment:
extension EnvironmentValues {
@Entry var captionBackgroundColor: Color =
Color(.secondarySystemBackground)
}
Expanding the macro shows us the generated code:
extension EnvironmentValues {
{
get {
self[__Key_captionBackgroundColor.self]
}
set {
self[__Key_captionBackgroundColor.self] = newValue
}
}
private struct __Key_captionBackgroundColor: SwiftUICore.EnvironmentKey {
typealias Value = Color
static var defaultValue: Value { Color(.secondarySystemBackground) }
}
}
The Entry macro doesnโt help with the final optional step of adding a view modifier but it does remove the rest of the boilerplate. It also works for adding Transaction, ContainerValues, and FocusedValues, and works back to iOS 13 so thereโs no need to wait to use it.