-
-
Notifications
You must be signed in to change notification settings - Fork 112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Replace ViewDeferredToRenderer
, fix renderer tests
#408
Conversation
ViewDeferredToRenderer
, fix renderer tests
@@ -30,7 +30,7 @@ final class MountedApp<R: Renderer>: MountedCompositeElement<R> { | |||
// They also have no parents, so the `parent` argument is discarded as well. | |||
let childBody = reconciler.render(mountedApp: self) | |||
|
|||
let child: MountedElement<R> = mountChild(childBody) | |||
let child: MountedElement<R> = mountChild(reconciler.renderer, childBody) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
renderer
instances now need to be explicitly passed through in a lot more places. This allows mounted elements to call back to the renderer to determine whether a given view is primitive for this renderer.
render(compositeElement: compositeView, body: \.view.view, result: \.view.bodyClosure) | ||
let view = body(of: compositeView, keyPath: \.view.view) | ||
|
||
guard let renderedBody = renderer.body(for: view) else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've only refactored ViewDeferredToRenderer
for now. SceneDeferredToRenderer
is kept as is to keep the PR small. And we didn't have problems with scenes (yet).
bodyClosure = { AnyView(($0 as! V).body) } | ||
} | ||
// swiftlint:disable:next force_cast | ||
bodyClosure = { AnyView(($0 as! V).body) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So happy this mess in AnyView
can be finally simplified!
@@ -48,7 +48,7 @@ public struct Button<Label>: View where Label: View { | |||
} | |||
} | |||
|
|||
public struct _Button<Label>: PrimitiveView where Label: View { | |||
public struct _Button<Label>: _PrimitiveView where Label: View { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PrimitiveView
is not meant to be re-exported by any renderer modules, making that explicit here with underscore naming.
@@ -18,6 +18,149 @@ | |||
import TokamakStaticHTML | |||
import XCTest | |||
|
|||
private let expectedHTML = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to use https://github.com/pointfreeco/swift-snapshot-testing here instead, but we're running these tests in the Wasm environment, and file access is tricky there. Hardcoding the test fixture here for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, excited to get some good testing going.
Sources/TokamakCore/Renderer.swift
Outdated
/** Returns a body of a given pritimive view, or `nil` if `view` is not a primitive view for | ||
this renderer. | ||
*/ | ||
func body(for view: Any) -> AnyView? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense for this to be called primitiveBody(for:)
? Then it’s more clear that it only returns the body of a primitive view?
This allows writing tests for
TokamakStaticHTML
,TokamakDOM
, andTokamakGTK
targets.The issue was caused by conflicting
ViewDeferredToRenderer
conformances declared in different modules, including theTokamakTestRenderer
module.This works around a general limitation in Swift, which was discussed at length on Swift Forums previously. When multiple conflicting conformances to the same protocol (
ViewDeferredToRenderer
in our case) exist in different modules, only one of them is available in a given binary (even a test binary). Also, only of them can be loaded and used. Which one exactly is loaded can't be known at compile-time, which is hard to debug and leads to breaking tests that cover code in different renderers. We had to disableTokamakStaticHTMLTests
for this reason.The workaround is to declare two new functions in the
Renderer
protocol:Now each renderer can declare their own protocols for their primitive views, i.e.
HTMLPrimitive
,DOMPrimitive
,GTKPrimitive
etc, delegating to them from the implementations ofbody(for view:)
andisPrimitiveView(_:)
. Conformances to these protocols can't conflict across different modules. Also, these protocols can haveinternal
visibility, as opposed toViewDeferredToRenderer
, which had to be declared aspublic
inTokamakCore
to be visible in renderer modules.