Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does EnvironmentKey have a defaultValue property?

Tags:

swift

swiftui

Here's the definition of EnvironmentKey:

public protocol EnvironmentKey {

    associatedtype Value

    static var defaultValue: Self.Value { get }
}

I'm confused as to the purpose of the defaultValue property. Or perhaps I'm confused about the intended use of @Environment. Let me explain...

Based on my testing, I know that the defaultValue is being accessed. If you, for example, define the defaultValue as a getter:

protocol MyInjector: EnvironmentKey {
  static var defaultValue: Foo {
    print("get default value!")
    return Foo()
  }
}

you'll find that SwiftUI frequently calls this property (because of this, in practice I define my defaultValue as static let).

When you add a breakpoint on the print statement and move down the stack it stops on the arrow below:

extension EnvironmentValues {
  var myInjector: Foo {
    get { self[MyInjector.self] }  <-----------------
    set { self[MyInjector.self] = newValue }
  }
}

Further down in the stack is the line in my component's init where I access the following variable:

@Environment(\.myInjector) var foo: Foo

That is, it seems to access defaultValue any time a SwiftUI view with an @Environment variable is re-rendered.

I think that the internal implementation of @Environment needs to set this default value before it determines the actual value as a way to avoid making the variable declaration optional.

Finally, I also experimented with adding @Environment to an ObservableObject in the hopes that it would have access to the same "environment" as the SwiftUI view tree.

class Bar: ObservableObject {
  @Environmenet(\.myInjector var foo: Foo
}

Unfortunately, that was not the case and the instance that Bar received was different than the instance I had injected via View.environment(\.myInjector, Foo()).

I also found, btw that I could use @Environment to inject a global singleton by using the shared variable pattern:

protocol MyInjector: EnvironmentKey {
  static let defaultValue = Foo()
}

and the same instance was available to SwiftUI views and any other class, but I'm not sure how that could be useful in any way.

So what is the purpose of defaultValue? Is it simply, as I suspect, so that the internal implementation can assign an arbitrary value to the variable before the true value is determined, or is there something else going on?

like image 552
Gil Birman Avatar asked Nov 06 '22 10:11

Gil Birman


1 Answers

IMO there are two reasons to have defaultValue in this pattern (and I assume this was the intention):

1) to specify corresponding value type
2) to make environment value always valid (in usage of @Environment)

In SwiftUI: Set Status Bar Color For a Specific View post you can find example of usage of this defaultValue feature to easily connect via environment different parts of application.

like image 109
Asperi Avatar answered Dec 04 '22 23:12

Asperi