SwiftUI EnvironmentValues - SwiftUI.PropertyList

Updated
Sep 22, 2022 10:13 AM
Created
Sep 21, 2022 1:44 PM
Tags
SwiftSwiftUI.framework
Attributes

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")

  }

}

@usableFromInline
@frozen internal struct PropertyList : Swift.CustomStringConvertible {
  @usableFromInline
  internal var elements: SwiftUI.PropertyList.Element?
  @inlinable internal init() { elements = nil }
  @usableFromInline
  internal var description: Swift.String {
    get
  }
}
extension PropertyList {
  @usableFromInline
  internal class Tracker {
    @objc @usableFromInline
    deinit
  }
}
extension PropertyList {
  @usableFromInline
  internal class Element : Swift.CustomStringConvertible {
    @usableFromInline
    internal var description: Swift.String {
      get
    }
    @objc @usableFromInline
    deinit
  }
}

struct Book_1_DumpingEnv: View {
  var body: some View {
    DumpingEnvironment(content: Text("Hey"))
  }
}

struct DumpingEnvironment<V: View>: View {
  @Environment(\.self) var env
  let content: V
  var body: some View {
    dump(env)
    return content
  }
}

(lldb) po type(of: Mirror.init(reflecting: env).children.first { $0.label == "_plist" }!.value.self)
=> SwiftUI.PropertyList

▿ some : 6 elements
    - 0 : "keyType"
    - 1 : "before"
    - 2 : "after"
    - 3 : "length"
    - 4 : "keyFilter"
    - 5 : "id"

▿
  ▿ _plist:
   ▿ elements:
    ▿ some:
     ▿ super: SwiftUI.PropertyList.Element
      - keyType: #
      - before:
      ▿ after:
       ▿ some:
        ▿ super: SwiftUI.PropertyList.Element
         - keyType: #
         - before:
         ▿ after:
          ▿ some:
           ▿ super: SwiftUI.PropertyList.Element
            - keyType: #
            - before:
            ▿ after:
             ▿ some:
              ▿ super: SwiftUI.PropertyList.Element
               - keyType: #
               - before:
               ▿ after:
                ▿ some:
                 ▿ super: SwiftUI.PropertyList.Element
                  - keyType: #
                  - before:
                  ▿ after:
                   ▿ some:
                    ▿ super: SwiftUI.PropertyList.Element

image

  • EnvironmentValues
    • _plist: PropertyList
  • PropertyList
    • elements: TypedElement

Recently updated notes

📖
Notes

NameTagsUpdated
just happened
SwiftUI.frameworkSwift
Sep 22, 2022 6:13 PM
Rendering ascii utils
Swift
Sep 22, 2022 8:45 AM
SwiftUI EnvironmentValues - SwiftUI.PropertyList
SwiftSwiftUI.framework
Sep 22, 2022 10:13 AM
Regex example
Sep 21, 2022 1:27 PM
SwiftUI animation - Structural Identity
SwiftSwiftUI.framework
Sep 16, 2022 12:39 PM
Verge v9 migration note
Sep 15, 2022 3:49 AM
Merging Blob, Metaball
Sep 14, 2022 12:28 PM
SwiftUI Testing
Sep 12, 2022 6:33 PM
Swipe up or down prototyping
SwiftSwiftUI.framework
Sep 10, 2022 9:51 AM
Pick an emoji at random
SwiftMemoDevelopmentSwiftScripting
Sep 10, 2022 7:00 AM
flex-wrap in SwiftUI with Layout API
SwiftSwiftUI.framework
Sep 9, 2022 4:11 PM
SwiftUI backports
SwiftSwiftUI.framework
Sep 9, 2022 4:03 PM
Making Slides in SwiftUI
Swift
Sep 8, 2022 6:35 PM
CORS on Firebase
Sep 5, 2022 8:48 AM
swift-async-shell
SwiftBookmark
Sep 4, 2022 12:34 PM
Furniture shops
Bookmark
Sep 4, 2022 11:06 AM
New StateReader syntax
VergeGroup/VergeSwift
Sep 3, 2022 8:07 PM
Swift snippets for scripting
SwiftSwiftScripting
Sep 4, 2022 12:35 PM
Mixpanel note
mixpanel
Sep 2, 2022 3:23 PM
[scrapbook] Trampoline implementation EventEmitter - Verge
Swift
Sep 1, 2022 3:45 PM
apple/swift-book
SwiftBookmark
Aug 31, 2022 4:50 PM
Swift Concurrency チートシート
SwiftMemoBookmark
Aug 29, 2022 4:59 AM
Check if it is in Task
SwiftConcurrency
Aug 27, 2022 6:11 AM
Behaviors actor annotated type - Swift Concurrency
SwiftConcurrency
Aug 26, 2022 5:23 PM
Syntax Design in Swift
Swift
Aug 25, 2022 12:41 PM
[Syntax Design] .background(.blue)
SwiftSyntaxDesign
Aug 25, 2022 12:41 PM
WidgetKit note
iOSSwift
Sep 15, 2022 1:58 PM
ActivityKit note
iOSSwift
Sep 15, 2022 3:13 PM
Hides warnings coming from calling main actor isolated things
SwiftConcurrency
Aug 25, 2022 1:32 PM
Coffee Grinder - VARIA VS3 GRINDER
Coffee
Aug 29, 2022 6:14 AM
Logger template for iOS12+
Swift
Sep 13, 2022 12:35 PM
Like UIScrollView, how to get "rubber banding effect"
Sep 15, 2022 5:43 AM
🛠️
Valuable iOS development resources
UIKit.frameworkSwiftSwiftUI.frameworkObjective-CCocoaiOS
Aug 29, 2022 5:53 AM