<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">SGTM!<div class=""><br class=""></div><div class="">-Chris</div><div class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Jan 17, 2018, at 3:14 PM, Connor Wakamo &lt;<a href="mailto:cwakamo@apple.com" class="">cwakamo@apple.com</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html; charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><br class=""><div class=""><br class=""><blockquote type="cite" class=""><div class="">On Jan 12, 2018, at 11:02 PM, Chris Lattner &lt;<a href="mailto:clattner@nondot.org" class="">clattner@nondot.org</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><blockquote type="cite" class=""><div class="">On Jan 12, 2018, at 6:22 PM, Connor Wakamo &lt;<a href="mailto:cwakamo@apple.com" class="">cwakamo@apple.com</a>&gt; wrote:</div><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class=""><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class=""><div class="">The second case, and the one I’m more worried about, is subclassing. If, for instance, you have the following:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>class FooView: UIView, CustomPlaygroundRepresentable {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>var playgroundRepresentation: Any {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                        </span>return “A string describing this view"</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>}</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>class BarView: FooView {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>override var playgroundRepresentation: Any {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                        </span>// BarView instances wanted to be logged as themselves, but there’s no way to express that</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                        </span>return ???</div><div class=""><span class="Apple-tab-span" style="white-space:pre">                </span>}</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div class=""><br class=""></div><div class="">There’s nothing that BarView can do to ensure it gets logged like a view because FooView declared a conformance to CustomPlaygroundRepresentable.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">I really don’t understand this. &nbsp;FooView declares that it conforms, and it provides a “playgroundRepresentation” member. &nbsp;Cool for FooView.</div><div class=""><br class=""></div><div class="">BarView comes around and overrides FooView’s implementation. &nbsp;They don’t have to conform, because it *isa* FooView, so of course it conforms. &nbsp;If it wants to customize its presentation, it overrides its&nbsp;&nbsp;“playgroundRepresentation” method and it… just works. &nbsp;If it conditionally wants the FooView representation for some reason, it can even call “super.playgroundRepresentation”. &nbsp;What’s the problem? &nbsp;This seems ideal to me.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">The issue is that there’s no way for `BarView` to recover the default playground representation which `UIView` and its subclasses get. </div></div></div></div></blockquote><div class=""><br class=""></div>I get it now, thanks.</div><div class=""><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class=""><div class="">For a `UIView`, the PlaygroundLogger library renders an image of the view and packages that up in a log entry. If `BarView` wants that instead of `FooView`’s custom representation, there’s no way for it to request it.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">I can think of two different ways to solve this problem:</div><div class=""><br class=""></div><div class="">1) Go with your suggestion upthread, where a custom enum is used: implementations do:</div><div class=""><br class=""></div><div class="">&nbsp; if predicate() {</div><div class="">&nbsp; &nbsp; return .custom(“my representation)</div><div class="">&nbsp; } else {</div><div class="">&nbsp; &nbsp; return .default</div><div class="">&nbsp; }</div><div class=""><br class=""></div><div class="">The problem with this is that you’re forcing implementations of this to deal with the enum just because of the rare case. &nbsp;Not a showstopper, but doesn’t feel right.</div><div class=""><br class=""></div><div class="">2) Go with the simple approach of returning Any, and add a new function to the Playground API somewhere that produces the default representation that they can call, the unusual case above would look like this:</div><div class=""><br class=""></div><div class=""><div class="">&nbsp; if predicate() {</div><div class="">&nbsp; &nbsp; return “my representation</div><div class="">&nbsp; } else {</div><div class="">&nbsp; &nbsp; return SomePlaygroundAPI.getDefaultPlaygroundRepresentation(self)</div><div class="">&nbsp; }</div><div class=""><br class=""></div><div class="">This seems like the best of both worlds to me.</div></div></div></div></div></blockquote><div class=""><br class=""></div><div class="">Yes, agreed. I’m not going to propose adding this to PlaygroundSupport as part of this proposal (under the assumption that this is an extreme edge case to begin with), but I’ll include a reference to it in the “alternatives considered” section so it can be referenced in the future should this be important enough that it warrants support.</div><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><div class="">IMO, every time you choose to privilege “well known” types in the standard library with special code in Xcode (or some other high level system loosely integrated with <a href="http://swift.org/" class="">swift.org</a>) you are introducing technical debt into the Swift ecosystem. &nbsp;This is because you are violating the basic principles on which Swift was established, which is that users can [re]define primitives to build their own abstractions and carve out new domains beyond what was originally envisioned when you’re writing the UI code.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">So given that, I think it’s not unreasonable for a platform/toolchain’s playground logger to know about that platform/toolchain’s important types so it can produce the appropriate opaque log entries. If there’s some approach that I’m overlooking — or if I’m dismissing the suite of protocols case too quickly — I would love to hear it, because I’m open to suggestions.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">Yes, you are completely right: Unless you are willing to make the structured schema public, there is no point to doing what I suggest.</div><div class=""><br class=""></div><div class="">I do think that there is a schema which would make sense to take public (similar to the abstract format used in treeviews like the navigator and UI debugger for variables, strikingly similar to what JSON can express with a fancier terminal than strings), but there is no rush to do that, and it is best to take time to consider it deliberately.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">Likewise, agreed — I don’t want to try to figure out what that would look like with the time pressure imposed by trying to get this in Swift 4.1 prior to ABI stability. And even in a world where this is exposed in a way to allow custom implementations at that low level, I think that a `CustomPlaygroundConvertible`-style API is still useful as the simple way of customizing display for most clients.</div><div class=""><br class=""></div><div class="">Connor</div><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><blockquote type="cite" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class=""><div class="">Is the problem the “one” case? &nbsp;If that is the problem, then it might be better to take a completely different approach where you embrace the fact that you have structured data producing 2D results that need to be displayed. &nbsp;Each result could either return an atomic result or a result that wraps some other recursive 2D presentation.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">No, the issue is that the playground logger would see that `Foo.two` conforms to `CustomPlaygroundConvertible`, and would therefore call `playgroundDescription` another time, which would return another `Foo.two`, and this would continue endlessly. I plan to guard against that in PlaygroundLogger with a limit on chaining, but this protocol should not require a failsafe as a feature.</div></div></div></blockquote><div class=""><br class=""></div>Right, it seems like the best way to solve this is to provide explicit access to the default presentation.</div><div class=""><br class=""></div><div class="">-Chris</div><br class=""></div></div></blockquote></div><br class=""></div></div></blockquote></div><br class=""></div></body></html>