The "@Environment" keyword in SwiftUI

·

3 min read

Today, I will show what the @Environment keyword is and how it works.

Overview

@Environment is a property wrapper in SwiftUI. It is used to access and read (but it is not allowed to write) environment values.

To dive deeper, let me first explain about environment values.

About environment values

Apps made with SwiftUI display views based on a view hierarchy structure, which consists of views as parents and children. These views have their own underlying data. In SwiftUI, some of data, such as locale and color scheme, are predefined and you can read them. Note that they are read-only and you cannot directly write to them.

About @Environment

The @Environment property wrapper is a way to access environment values. Here is an example:

struct ExampleView: some View {
    @Environment(\.colorScheme) var colorScheme: ColorScheme
    if colorScheme == .dark {
        DarkContent()
    else {
        LightContent()
    }
}

In this way, you can instantiate a variable with @Environment and the key path. If the current value of the colorScheme is dark, the ExampleView displays the DarkContent and if the current value is not dark it displays the LightContent.

To be detailed, the property wrapper @Environment generates a computed property with a getter that retrieves the value using the specified key path (\.colorScheme in this case). The wrappedValue of the Environment struct is used to access the actual value. Therefore, the code above is equivalent to the following:

struct ExampleView: View {
    // @Environment(\.colorScheme) var colorScheme: ColorScheme
    var colorScheme: ColorScheme {
        get {
            return Environment(\.colorScheme).wrappedValue
        }
    }

    if colorScheme == .dark {
        DarkContent()
    else {
        LightContent()
    }
}

List of environment values

Here is a list of important and common environment values:

  • colorScheme: The current color scheme (light or dark) of the environment.

  • locale: The current locale of the environment.

  • calendar: The current calendar of the environment.

  • timeZone:The current time zone of the environment.

  • horizontalSizeClass: The current horizontal size class of the environment (regular or compact).

  • verticalSizeClass: The current vertical size class of the environment (regular or compact).

  • layoutDirection: The current layout direction of the environment (left-to-right or right-to-left).

  • accessibilityEnabled: A Boolean value indicating whether accessibility features are enabled.

  • editMode: The edit mode of the current view (active or inactive).

  • isSearching: A Boolean value indicating whether a search is currently active.

How to create a custom environment value

In addition to the built-in environment values, you can create custom environment values. Here is an example:

struct MyCustomEnvironmentKey: EnvironmentKey {
    static var defaultValue: String = "default value"
}

extension EnvironmentValues {
    var myCustomValue: String {
        get { self[MyCustomEnvironmentKey.self] }
        set { self[MyCustomEnvironmentKey.self] = newValue }
    }
}

As shown in the example above, you first have to define a struct that conform to the EnvironmentKey protocol for the key to of the environment value. Then, you have to extend the EnvironmentValues struct, where all the environment values are defined, to add the new environment value.

Now, you can access the new custom environment value using the @Environment property wrapper:

struct MyView: View {
    @Environment(\.myCustomValue) var customValue
    var body: some View {
        Text("Custom Value: \(customValue)")
    }
}

struct ParentView: View {
    var body: some View {
        MyView()
            .environment(\.myCustomValue, "Custom Value") // assign a value that is different from default value
    }
}