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