Investigation

a stuff displaying env value and custom env-key as an indicator for this investigation

private struct MyEnvironmentKey: EnvironmentKey {
  static let defaultValue = "Default"
}

extension EnvironmentValues {
  var myEnvironmentKey: String {
    get { self[MyEnvironmentKey.self] }
    set { self[MyEnvironmentKey.self] = newValue }
  }
}

struct EnvironemtDisplay<V>: View {

  @Environment var value: V

  init(keyPath: KeyPath<EnvironmentValues, V>) {
    self._value = .init(keyPath)
  }

  var body: some View {
    Text("\\(String(describing: value))")
  }
}

creating deep nested view, and setting env-value for each level

struct Book_1_DumpingEnv: View {
  var body: some View {

    Group {
      EnvironemtDisplay(keyPath: \\.myEnvironmentKey)
      Group {
        EnvironemtDisplay(keyPath: \\.myEnvironmentKey)
        Group {
          EnvironemtDisplay(keyPath: \\.myEnvironmentKey)
          Group {
            EnvironemtDisplay(keyPath: \\.myEnvironmentKey)
            Group {
              EnvironemtDisplay(keyPath: \\.myEnvironmentKey)
              Group {
                EnvironemtDisplay(keyPath: \\.myEnvironmentKey)
              }
              .environment(\\.myEnvironmentKey, "6")
            }
            .environment(\\.myEnvironmentKey, "5")
          }
          .environment(\\.myEnvironmentKey, "4")
        }
        .environment(\\.myEnvironmentKey, "3")
      }
      .dumpEnv(label: "🌏2")
      .environment(\\.myEnvironmentKey, "2")
    }
    .dumpEnv(label: "🌏1")
    .environment(\\.myEnvironmentKey, "1")

  }
}

see what’s happening on level-2 we could see the previous value for MyEnvironmentKey that was specified on the up level.

2022-09-22 19:07:46.517859+0900 SwiftUIBook[92260:5625305] [environment] EnvironmentValues: 🌏2
{
  "_0_id" : 7212,
  "_1_keyTypeName" : "MyEnvironmentKey",
  "_2_valueRepresentation" : "2",
  "_4_after" : {
    "_0_id" : 7211,
    "_1_keyTypeName" : "MyEnvironmentKey",
    "_2_valueRepresentation" : "1",
    "_4_after" : {
      "_0_id" : 7210,
      "_1_keyTypeName" : "IsContentScrollViewManaged",
      "_2_valueRepresentation" : "true",
      "_4_after" : {
        "_0_id" : 7209,

Tools

extension View {

  public func dumpEnv(label: String = "🌏") -> some View {
    DumpingEnvironment(label: label, content: self)
  }

}

private struct DumpingEnvironment<V: View>: View {
  @Environment(\\.self) var env
  let label: String
  let content: V
  var body: some View {
    parse(env: env, label: label)
    return content
  }
}

private final class ProjectedTypedElement: Encodable {

  var _0_id: Int
  var _1_keyTypeName: String
  var _2_valueRepresentation: String
  var _3_before: ProjectedTypedElement?
  var _4_after: ProjectedTypedElement?

  init(mirror: Mirror) {

    let typeName = String(describing: mirror.subjectType)
      .replacingOccurrences(of: "TypedElement<EnvironmentPropertyKey<", with: "")
      .replacingOccurrences(of: ">", with: "")

    self._1_keyTypeName = typeName

    self._2_valueRepresentation = String(describing: mirror["value"]!)

    let superMirror = mirror.superclassMirror!

    self._0_id = superMirror.next("id")!["value"] as! Int
    self._4_after = superMirror.next("after")?.next("some").map { .init(mirror: $0) }
    self._3_before = superMirror.next("before")?.next("some").map { .init(mirror: $0) }

  }

  func jsonString() -> String {

    let encoder = JSONEncoder()
    encoder.outputFormatting = [.prettyPrinted, .sortedKeys]

    let data = try! encoder.encode(self)

    return String(data: data, encoding: .utf8)!

  }

  func renderFlatten() -> String {

    var values: [(String, String)] = []

    func visit(element: ProjectedTypedElement) {

      values.append((element._1_keyTypeName, element._2_valueRepresentation))

      if let before = element._3_before {
        visit(element: before)
      }

      if let after = element._4_after {
        visit(element: after)
      }

    }

    visit(element: self)

    return values.map {
      "\\($0.0) => \\($0.1)"
    }
    .joined(separator: "\\n")
  }

  func render() -> String {

    var string = ""

    string += _render(indent: 2)

    return string
  }

  private func _render(indent: Int) -> String {
    var string = ""

    let before = _3_before?._render(indent: indent)
    let after = _4_after?._render(indent: indent)

    string = #"""
      "id": "\\#(_0_id)",
      "key": "\\#(_1_keyTypeName)",
      "value": "\\#(_2_valueRepresentation)",
      "before": \\#(before ?? "null"),
      "after": \\#(after ?? "null")
      """#

    return """
      {
      \\(string.indented(indent))
      }
      """

  }

}

private func parse(env: EnvironmentValues, label: String) {

  let mirror = Mirror(reflecting: env)

  guard let propertyList = mirror.next("_plist") else {
    return
  }

  let elements = propertyList.next("elements")!.next("some")!

  let t = ProjectedTypedElement(mirror: elements)

  Log.debug(
    .environment,
    """
    EnvironmentValues: \\(label)
    \\(t.jsonString())
    """
  )

}

extension Mirror {

  fileprivate var labels: [String] {
    children.map { $0.label ?? "" }
  }

  fileprivate subscript(_ label: String) -> Any? {
    children.first(where: { $0.label == label }).map { $0.value }
  }

  fileprivate func next(_ label: String) -> Mirror? {
    children.first(where: { $0.label == label }).map { Mirror(reflecting: $0.value) }
  }

}

extension String {

  fileprivate static func spacing(_ length: Int) -> String {
    return Array(repeating: " ", count: length).joined()
  }

  fileprivate func indented(_ size: Int) -> String {

    let spaces = Array(repeating: " ", count: size).joined(separator: "")

    return split(separator: "\\n")
      .map { spaces + $0 }
      .joined(separator: "\\n")

  }

}