From 3cb344cad96e9d67924fe4b156977c490495daa1 Mon Sep 17 00:00:00 2001 From: Yusuke Hosonuma Date: Sun, 19 Jun 2022 13:44:40 +0900 Subject: [PATCH] hotfix: infinity-loop in @Published property --- Sources/Core/PrettyDescriber.swift | 42 ++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/Sources/Core/PrettyDescriber.swift b/Sources/Core/PrettyDescriber.swift index 4073b1e..baba6f3 100644 --- a/Sources/Core/PrettyDescriber.swift +++ b/Sources/Core/PrettyDescriber.swift @@ -196,7 +196,6 @@ struct PrettyDescriber { let typeName = String(describing: target.self) let propertyWrappers: [(String, String)] = [ - ("Published", "currentValue"), ("StateObject", "wrappedValue"), ("ObservedObject", "wrappedValue"), ("EnvironmentObject", "_store"), @@ -209,7 +208,7 @@ struct PrettyDescriber { for (type, key) in propertyWrappers { if typeName.hasPrefix("\(type)<"), let value = lookup(key, from: target) { if debug { - // e.g. `@Published(42)` + // e.g. `@State(42)` return "@\(type)(\(__string(value)))" } else { return __string(value) @@ -227,6 +226,22 @@ struct PrettyDescriber { return formatter.objectString(typeName: "@\(name)", fields: objectFields(target, debug: debug)) } + // + // @Published + // + // Note: + // Direct lookups because some data structures infinity-loop with circular references. + // + if typeName.hasPrefix("Published") { + let value = lookup(keyPath: ["storage", "publisher", "subject", "currentValue"], from: target) + if debug { + // e.g. `@Published(42)` + return "@Published\(__string(value)))" + } else { + return __string(value) + } + } + // // @Namespace // @@ -472,6 +487,29 @@ struct PrettyDescriber { return nil } + private func lookup(keyPath: [String], from: Any) -> Any? { + var target: Any = from + + for key in keyPath { + if let value = lookup(key: key, from: target) { + target = value + } else { + return nil + } + } + + return target + } + + private func lookup(key: String, from target: Any) -> Any? { + for child in Mirror(reflecting: target).children { + if let label = child.label, label == key { + return child.value + } + } + return nil + } + private func handleError(_ f: () throws -> String) -> String { do { return try f()