Give your simulator superpowers

RocketSim: An Essential Developer Tool
as recommended by Apple

Exclusive Pre-Launch Offer: Get 20% Off atgoing-indie.com

@Entry macro: Creating custom environment values in SwiftUI

The @Entry macro in SwiftUI allows you to define custom environment values without writing boilerplate code. While introduced in Xcode 16, you can use it from iOS 13 and up since it’s a Swift Macro that generates backward-compatible code.

The new macro can be used for environment values, as well as for transaction, container, and focused values. In this article, we’ll be focusing on environment values only.

What are Environment Values?

Environment values are a way to share data across the view hierarchy in SwiftUI. They are essentially key-value pairs that provide views with access to data that is common throughout the app or specific to a part of the view hierarchy.

This mechanism helps maintain a clean and efficient data flow without explicitly passing data through multiple layers of views. You can access an environment value using the @Environment or @EnvironmentObject property wrapper for which I wrote dedicated articles:

Stay updated with the latest in SwiftUI

Join 19,814 Swift developers in our exclusive newsletter for the latest insights, tips, and updates. Don't miss out – join today!

You can always unsubscribe, no hard feelings.

Using the @Entry macro to create custom environment values

The @Entry macro is a so-called attached macro and can be used as follows:

extension EnvironmentValues {
    @Entry var primaryTheme: Theme = .init(primaryColor: .accentColor)
}

It’s called ‘entry’ since you create ‘an entry to a value’ in a given environment. In our case, we’re generating an entry to an environment value. However, you can also use it to create Transaction, ContainerValues, or FocusedValues entries.

The example allows you to define a custom theme for your app, accessible throughout the view hierarchy. It’s a simple theme definition for the sake of this example:

@Observable
final class Theme {
    var primaryColor: Color

    init(primaryColor: Color) {
        self.primaryColor = primaryColor
    }
}

The macro generates code behind the scenes that looks as follows:

The @Entry macro generates code behind the scenes to support environment values.
The @Entry macro generates code behind the scenes to support environment values.

In other words, without using the macro we had to write the following code to make the same piece work:

extension EnvironmentValues {
    var primaryTheme: Theme {
        get {
            self[PrimaryThemeKey.self]
        }
        set {
            self[PrimaryThemeKey.self] = newValue
        }
    }
}

struct PrimaryThemeKey: EnvironmentKey {
    static var defaultValue: Theme { .init(primaryColor: .accentColor) }
}

While writing this code might be fine for a single key, it quickly becomes a lot when you have to write many custom environment values. Replacing this boilerplate with the macro is an excellent code improvement.

Using the @Entry generated code in SwiftUI

Once you’ve added a custom entry using the @Entry macro, you can start reference its value as follows:

struct ThemedView: View {
    
    @Environment(\.primaryTheme) private var primaryTheme: Theme
    
    var body: some View {
        Text("This text is using the theme's primary color")
            .foregroundStyle(primaryTheme.primaryColor)
    }
}

Since we defined our Theme as observable, our view will automatically update when the primary color changes. Environment values are a great way to access common app data without manually passing it through the view hierarchy.

Conclusion

The @Entry macro is a great example of the added value macros can deliver. It’s great to see a new Xcode version adding backward-compatible features that reduce the code we have to write manually.

If you want to improve your SwiftUI knowledge, even more, check out the SwiftUI category page. Feel free to contact me or tweet me on Twitter if you have any additional tips or feedback.

Thanks!

 
Antoine van der Lee

Written by

Antoine van der Lee

iOS Developer since 2010, former Staff iOS Engineer at WeTransfer and currently full-time Indie Developer & Founder at SwiftLee. Writing a new blog post every week related to Swift, iOS and Xcode. Regular speaker and workshop host.