Environment is a very powerful way to pass data from parent down to children.

It is like a global singleton.

Don’t cringe, because that’s the direction SwiftUI is taking. So embrace it.

1. Custom Environment

The steps involve creating a key, injecting into EnvironmentValues, and then passing it (from parent to children).

/// 1. Declare a key with the type, and a default
struct MyEnvironmentKey: EnvironmentKey {
    static var defaultValue: CGFloat { 0 }

/// 2. Provide extension to EnvironmentValues
extension EnvironmentValues {
    var foo: CGFloat {
        get { self[MyEnvironmentKey.self] }
        set { self[MyEnvironmentKey.self] = newValue }

/// 3. Pass to children using `environment`
  .environment(\.foo, 1.23)

/// 4. Children declare and use
@Environment(\.foo) var foo

2. Custom EnvironmentObject

The difference is that EnvironmentObject does NOT require a key.

Because it simply uses the type as the “key”.

You start by creating your custom type, which has to implement ObservableObject. Yup, that’s the same view model you’ll have.

/// 1. Declare the custom type
class MyModel: ObservableObject {
    @Published var foo: String = "a"

struct ChildView: View {

    /// 2. Use `@EnvironmentObject` with the custom type
    @EnvironmentObject var model: MyModel

    var body: some View {

/// 3. Parent to pass down the model
struct ParentView: View {
    @ObservedObject var model = MyModel()

    var body: some View { = "b" // eg. Parent could update it
        return VStack {

NOTE: You must create your EnvironmentObject and set it from somewhere. If it is never set and the child view uses it, the app will crash.

PITFALL: Using in modal sheet

Environment is passed down from a parent to all it’s descendant.

Typically you pass on the root view.

However, if you use a sheet, it will be a sibling to the root (NOT a descendant). So you have to set the environment in the sheet.

.sheet(isPresented: $presentDetailsView) {

PITFALL: Xcode preview crashed

Cannot preview in this file - may have crashed

This is because you didn’t pass an environment for the preview. Simply pass it in your PreviewProvider.




Back to Home