← All shorts
03iOS 18+2 min read

@Entry: Custom Environment Values in One Line

The @Entry macro turns a multi-step EnvironmentKey into one line for custom environment and focus values.

Adding one custom value to EnvironmentValues used to mean defining a private key type conforming to EnvironmentKey, giving it a static defaultValue, then extending EnvironmentValues with a computed property that reads and writes through that key. Every custom value repeated the same three-part dance.

#The old way

import SwiftUI

private struct AccentThemeKey: EnvironmentKey {
    static let defaultValue: Color = .blue
}

extension EnvironmentValues {
    var accentTheme: Color {
        get { self[AccentThemeKey.self] }
        set { self[AccentThemeKey.self] = newValue }
    }
}

#The new way

import SwiftUI

extension EnvironmentValues {
    @Entry var accentTheme: Color = .blue
}

struct ThemedBadge: View {
    @Environment(\.accentTheme) private var accentTheme

    var body: some View {
        Text("New")
            .padding(6)
            .background(accentTheme, in: Capsule())
    }
}

struct ContentView: View {
    var body: some View {
        ThemedBadge()
            .environment(\.accentTheme, .orange)
    }
}

#Why it matters

  • One line replaces the key struct plus the computed get/set property, for every custom value
  • The same macro works for FocusedValues and Transaction keys, not only EnvironmentValues
  • The default value comes straight from the property's own initializer, no separate defaultValue declaration to keep in sync
  • Fewer moving parts means fewer chances to typo a key name and silently read the wrong default elsewhere

#Gotcha

@Entry requires the default value written right there in the declaration — @Entry var accentTheme: Color = .blue — it can't pull a default from a separate initializer or from logic elsewhere in the file. And it's iOS 18 / macOS 15 and later only: if your app still supports iOS 17, any environment value that needs to work there still needs the old EnvironmentKey boilerplate, since you can't conditionally compile a stored property's declaration style based on OS version at the call site.

swiftuiios18environment

Related shorts