Closed bachand closed 1 year ago
I found a related test in this repository.
The test leverages a custom view that does not conform to Inspectable
.
The test describes this view as a "blocker" view.
Hey @bachand , good question. Conformance to Inspectable
is an unfortunate requirement that I could not work around yet. Here is my understanding why this is impossible to go without it:
As you may already know, the library is relying on swift reflection for digging into the views. In reflection, you can see the names of the variables, you can extract their values, you can get a string representation of their types, but extracted value's type is always Any
. It does not give you the Type you can cast it to, only the name of the type.
Custom views provide the hierarchy in the computed variable body
, but reflection does not call computed variables, so we have to do it ourselves. Since the value of the extracted view is Any
, swift won't allow us to call body
on Any
, we need a way to assure swift that this method exists on the variable - so we need to either cast it to the exact type of the view, or to a protocol it conforms to. Here we end up with Inspectable
.
Casting to the exact type works in some cases, the library uses this approach in a few places, but in general, this is far more inconvenient than Inspectable
conformance: the factual view type might be super cumbersome due to the heavy use of generics in SwiftUI, this makes the tests super fragile, as even a tiny tweak in the view hierarchy changes the resolved view type.
I hope this sheds some light on the problem.
Hey @nalexn. Thank you so much for your thoughtful response. I appreciate you taking the time to write this out.
Custom views provide the hierarchy in the computed variable
body
, but reflection does not call computed variables, so we have to do it ourselves. Since the value of the extracted view isAny
, swift won't allow us to callbody
onAny
, we need a way to assure swift that this method exists on the variable - so we need to either cast it to the exact type of the view, or to a protocol it conforms to. Here we end up withInspectable
.
When I look at the Inspectable
protocol I see two members, an entity
property and an extractContent(…)
method. https://github.com/nalexn/ViewInspector/blob/ac7df67c4e0593470eda90a550f8493a81609745/Sources/ViewInspector/BaseTypes.swift#L5-L9
I see what you are saying about not being able to call the body
computed property on an instance of type Any
. I am not following how the Inspectable
protocol addresses this issue since it does not define body
property.
Can you point me to a code example in the library where we are using Inspectable
to invoke body
? I'm asking to learn. I think seeing the code may help me connect the dots.
My goal is to continue to understand the internal mechanics of this library more deeply so that I can figure out the best way to integrate it into a large codebase with many custom views.
I see what you are saying about not being able to call the body computed property on an instance of type
Any
. I am not following how theInspectable
protocol addresses this issue since it does not define body property.
It does call body
in an extension, see extension Inspectable where Self: View
and similar for ViewModifier, etc.
You cannot cast to a View
protocol for calling body
because it has an associated type.
I see what you are saying about not being able to call the body computed property on an instance of type
Any
. I am not following how theInspectable
protocol addresses this issue since it does not define body property.It does call
body
in an extension, seeextension Inspectable where Self: View
and similar for ViewModifier, etc.You cannot cast to a
View
protocol for callingbody
because it has an associated type.
Ah that makes sense @nalexn . With Swift 5.7 it's now possible to cast to the View
protocol and invoke body
on the existential! I've made removed the need for an Inspectable
conformance when performing content extraction in https://github.com/nalexn/ViewInspector/pull/216.
My goal is to remove the need for a developer to conform their views to Inspectable
when using this library. Relaxing this restriction should make this library easier to adopt, especially in a large codebase with many views.
After https://github.com/nalexn/ViewInspector/pull/216 the Inspectable
protocol is a "marker" protocol, as it has no property or method requirements. I tried going further to relax the requirement that views conform to Inspectable
. When I did so I encountered this code that I had trouble wrapping my head around.
The value content.isCustomView
is determined by whether the view conforms to Inspectable
.
@nalexn can you help me understand the unwrappedModifiedContent()
and the above child(…)
method?
If it's not a requirement that views conform to Inspectable
I am not sure when we would want to call content.extractCustomView()
vs. content.unwrappedModifiedContent()
.
We were able to remove the requirement that views conform to Inspectable
in https://github.com/nalexn/ViewInspector/pull/216 and through subsequent changes added to the https://github.com/nalexn/ViewInspector/commits/0.9.3 release. Thanks for your help in making this happen @nalexn !
This is an amazing library. Wow!
I am learning how this library works and I have a question about the
Inspectable
protocol.Let's say I have a toy SwiftUI view setup like this:
And then let's say I want to verify that my screen shows the string "Hello world" somewhere at any level of the hierarchy. I'd write that test like this:
I find that for this to to pass I need to conform both
MyScreen
andMyTextWrapperView
toInspectable
.When building infrastructure for a large codebase it can be challenging to ensure that all of the expected views conform to
Inspectable
. I was hoping that ViewInspector'sfind(text:)
would work out of the box without any view needing to be conformed toInspectable
. I don't mind needing to conformMyScreen
toInspectable
; the part that is more difficult to reason about is the need to conform child view types, likeMyTextWrapperView
, used within the hierarchy.I am trying to understand the reason why the
Inspectable
conformance is necessary. I see that theInspectable
protocol has one property and one method, and that there's a default implementation for each whenSelf: View
. However I haven't been able to find any documentation as to why theInspectable
protocol is necessary.Thanks again for filling this huge gap in the SwiftUI ecosystem which such a thoughtfully built library đŸ˜„