@EnvironmentObject & the Environment

Passing a shared model through every view gets tedious fast. SwiftUI's environment lets you inject an object once near the top and read it anywhere below with @EnvironmentObject . You'll also meet @Environment for built-in values like dark mode and the dismiss action.

Learn @EnvironmentObject & the Environment in our free Swift course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a…

Part of the free Swift course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

Building iOS Apps with SwiftUI • Intermediate

What You'll Learn in This Lesson

1️⃣ Injecting with .environmentObject(_)

Create your shared model (an ObservableObject ) and own it with @StateObject . Then attach .environmentObject(_) to an ancestor view. Everything below it can now reach that instance.

2️⃣ Reading with @EnvironmentObject

A descendant declares @EnvironmentObject var session: Session and SwiftUI supplies the injected instance — even if many views sit between them. Intermediate views never have to forward it.

3️⃣ @Environment — Built-In Values

@Environment(\\.key) reads built-in keyed values rather than your own objects: the current colorScheme (light/dark), the locale , the dismiss action, and more.

Your turn. Fill in the blanks to inject an object and read it in a descendant.

Create a Theme: ObservableObject with a @Published var isDark = false . Inject it at the root, then build two unrelated child views several levels deep: one with a toggle button that flips isDark , and another that displays the current theme. Confirm both react without passing the object through any intermediate view.

Practice quiz

What problem does @EnvironmentObject solve?

  • Storing data on disk
  • Passing a shared object deep down without threading it through every view
  • Drawing animations
  • Networking

Answer: Passing a shared object deep down without threading it through every view. @EnvironmentObject injects a shared model into the environment so deep descendants read it directly.

How do you inject an object into the environment?

  • With the .environmentObject(_) modifier on a parent view
  • By writing @State
  • With a global variable
  • It is automatic for all objects

Answer: With the .environmentObject(_) modifier on a parent view. Call .environmentObject(myObject) on an ancestor view to place it in the environment for descendants.

How does a descendant view read an injected object?

  • @State var model
  • @Binding var model
  • @EnvironmentObject var model: MyModel
  • import model

Answer: @EnvironmentObject var model: MyModel. Declare @EnvironmentObject var model: MyModel and SwiftUI supplies the injected instance.

What must the injected type conform to (pre-iOS 17)?

  • View
  • ObservableObject
  • Codable
  • Hashable

Answer: ObservableObject. An @EnvironmentObject is an ObservableObject, so its @Published changes refresh observing views.

What happens if you read an @EnvironmentObject that was never injected?

  • It returns nil
  • The app crashes at runtime
  • It returns a default
  • It compiles to an empty view

Answer: The app crashes at runtime. Forgetting to inject the object crashes at runtime because there is nothing to supply.

What does @Environment(\.colorScheme) read?

  • A custom model
  • A built-in environment value (light or dark mode)
  • The view's frame
  • User defaults

Answer: A built-in environment value (light or dark mode). @Environment reads built-in environment values like colorScheme, locale, or dismiss.

What is the difference between @EnvironmentObject and @Environment?

  • They are identical
  • @EnvironmentObject reads an injected ObservableObject; @Environment reads keyed environment values
  • @Environment is for networking
  • @EnvironmentObject is deprecated

Answer: @EnvironmentObject reads an injected ObservableObject; @Environment reads keyed environment values. @EnvironmentObject pulls a shared object; @Environment(\.key) reads a specific keyed value.

Which value would you read with @Environment(\.dismiss)?

  • A color
  • An action to close the current screen
  • The device name
  • A binding to text

Answer: An action to close the current screen. @Environment(\.dismiss) gives a dismiss action you call to close a presented view.

Why is @EnvironmentObject convenient for app-wide state?

  • It is faster than a database
  • Any descendant can read it without manual passing through intermediaries
  • It encrypts data
  • It avoids using classes

Answer: Any descendant can read it without manual passing through intermediaries. It removes the need to pass the object through every intermediate view's initializer.

Where do you typically inject app-wide objects?

  • In a leaf view
  • Inside a Button
  • In a Text view
  • Near the root, e.g. on the top-level view in the App

Answer: Near the root, e.g. on the top-level view in the App. Inject near the root so the whole view tree below it can access the shared object.