-
Notifications
You must be signed in to change notification settings - Fork 156
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
CustomInspectable view support #288
CustomInspectable view support #288
Conversation
c844e4f
to
80db9d7
Compare
@nalexn this is what it looks like in our codebase, to give you a better idea of how this can be used: extension CollectionViewRepresentable: CustomInspectable {
@ViewBuilder
public var inspectableRepresentation: some View {
ForEach(sections) { section in
ForEach(Array(section.supplementaries.enumerated()), id: \.offset) { _, supplementary in
supplementary._opaqueViewForTesting
}
ForEach(section.items, id: \.id) { item in
item._opaqueViewForTesting
}
}
}
} |
80db9d7
to
e772575
Compare
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.
Thank you for adding this powerful functionality to ViewInspector, @bryankeller!
@nalexn we appreciate your thoughts on the approach we're taking.
@@ -127,7 +139,11 @@ public struct Content { | |||
let medium: Medium | |||
|
|||
internal init(_ view: Any, medium: Medium = .empty) { | |||
self.view = view | |||
if let customInspectable = view as? any CustomInspectable { | |||
self.view = customInspectable.customInspectableContent |
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.
👌
/// A protocol for views that need to expose a custom view to `ViewInspector` that differs from its default inspectable | ||
/// representation. | ||
/// | ||
/// A use case for this might be a `UIViewRepresentable`that contains some hosted SwiftUI views (consider a wrapped |
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.
👍
@bryankeller thanks for the PR! Just to clarify, there is an API for accessing That gives you access to the UIKit views, but I haven't tested if this is possible to further dig up embedded SwiftUI views from UIKit hierarchy. I assume that should be possible by means of UIKit API (recursive traverse of subviews). Could you confirm this approach doesn't allow you to achieve what you want? |
Hi @nalexn, I can provide some additional context here. @bryankeller please feel free to add more info if you spot anything I missed. Our internal use case is that we need to use UIKit types to accomplish things that SwiftUI still falls short of our requirements (layout/scrolling capabilities, performance, etc). The key internal implementation detail is that we use the UICollectionViewCell's We basically have a SwiftUI View that wraps a The key motivation behind the It's just more ergonomic and transparent for the testing code if the parent SwiftUI View to expose the children SwiftUI Views in the cells as its content directly (instead of using We also have some other SwiftUI Views that implement the same "SwiftUI -> UIKit -> SwiftUI" wrapping pattern that are just implemented differently from the key implementation detail I just mentioned above. So the |
Thanks @rafael-assis - that description is spot on. One thing I'd add is that for our use case, we could technically get the underlying collection view via |
Thanks for elaborating on the topic, I've added this use case to the documentation and merged the PR. |
Hey @nalexn ,
At Airbnb, we've recently discovered that
ViewInspector
doesn't work with some of our new SwiftUI infrastructure. We're having trouble getting a customUIViewRepresentable
that contains SwiftUI subviews to properly work. Internally, we've wrappedUICollectionView
in aUIViewRepresentable
, and under the hood, it usesUIHostingConfiguration
s to show SwiftUI views in cells.The API for this
UIViewRepresentable
is pretty declarative - we pass it some data, and under the hood, it diffs the data and updates the collection view.The issue is that
ViewInspector
doesn't really know how to handle aUIViewRepresentable
that contains SwiftUI views. After some discussion internally, we realized that one way around this limitation is to simply expose an alternative SwiftUI-native representation of theUIViewRepresentable
toViewInspector
, based on the current set of data. For example, if we have aUIViewRepresentable
wrapping a collection view, and its current cell data looks like this:We could expose this to
ViewInspector
using a custom view-building property:Internally,
ViewInspector
would use this custom representation, rather than theUIViewRepresentable
. TheinspectableRepresentation
view would likely be aForEach
, rather than a hard-codedVStack
like in my simple example above.The full approach taken is to introduce a protocol called
CustomInspectable
. Any SwiftUI view can conform to this, and if it does,ViewInspector
will use the custom view it provides for inspection, rather than treating it as aUIViewRepresentable
or whatever type it originally was.Other use cases might be:
This approach is a purely additive change, and requires very little additional code in the library. I'd love to get your thoughts on it :)