-
Notifications
You must be signed in to change notification settings - Fork 153
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
Inspection of nested SwiftUI Views using @State properties not possible #231
Comments
I’m experiencing a similar issue. In my case, I’m updating a I’ve tried to make this work using Approach #2 and the proposal here to avoid having the extra boilerplate code for |
@lo1tuma I tried a similar approach as the the one described in the link you shared with similar results: the View is not updated probably because the I even tried running the exact test in the example which fails probably because import Combine
import XCTest
import SwiftUI
import ViewInspector
internal final class Inspection<V> {
let notice = PassthroughSubject<UInt, Never>()
var callbacks: [UInt: (V) -> Void] = [:]
func visit(_ view: V, _ line: UInt) {
if let callback = callbacks.removeValue(forKey: line) {
callback(view)
}
}
}
extension Inspection: InspectionEmissary {}
public let TEST_WRAPPED_ID: String = "wrapped"
struct TestWrapperView<Wrapped: View> : View{
internal let inspection = Inspection<Self>()
var wrapped: Wrapped
init( wrapped: Wrapped ){
self.wrapped = wrapped
}
var body: some View {
wrapped
.id(TEST_WRAPPED_ID)
.onReceive(inspection.notice) {
self.inspection.visit(self, $0)
}
}
}
struct ContentViewFromInternet: View {
@State var numClicks:Int = 0
var body: some View {
VStack{
Button("Click me"){
numClicks += 1
}.id("Button1")
Text("\(numClicks)")
.id("Text1")
.padding()
}
}
}
final class InternetTestCase: XCTestCase {
func testContentViewFromInternet() throws{
let sut = TestWrapperView(wrapped: ContentViewFromInternet())
let exp = sut.inspection.inspect { view in
let wrapped = try view.find(viewWithId: TEST_WRAPPED_ID)
let button = try wrapped.find(viewWithId: "Button1").button()
try button.tap()
let numClicks = try wrapped
.view(ContentViewFromInternet.self)
.actualView()
.numClicks
XCTAssertEqual(numClicks, 1)
let text = try wrapped.find(viewWithId: "Text1").text()
let value = try text.string()
XCTAssertEqual(value, "1")
}
ViewHosting.host(view: sut)
wait(for: [exp], timeout: 1)
}
} |
Thanks for writing this up in such detail @rafael-assis . Ideally the "storage" for all |
Right, this is a known limitation of the library. Your investigation led in the right direction, ultimately, the |
Thank you @nalexn for taking the time to review the issue and providing your thoughts! |
@nalexn we're facing the same issue. Do you think it will technically be feasible on library-side? Or is it a SwiftUI limitation somehow? I didn't get that from the link you provided. As @rafael-assis, we wanted to use a wrapper to prevent polluting our production code with inspection stuff. And it's working great for @ObservedObjects and stuff, but not at all for @States |
Yeah, that's a limitation of SwiftUI. If you use objects for state management, they share the state and always contain the actual values, but @State does not |
Any updates on this? |
Context
In an attempt to inspect SwiftUI Views that use
@State
Properties, I found out that these properties don't change their value, hence making it impossible to inspect a view after an action (a button tap for example) happened in order to validate its new state.I tried the Approach 1 listed in the guide section that covers workarounds to inspect
@State
properties.That approach seems to work only for the root level SwiftUI View. Other nested SwiftUI Views used in its
body
computed property woudn't have their state changed even though the find and tap functions will execute successfully throughout the child SwiftUI Views in the hierarchy.Code example
As a code example of the issue, consider the SwiftUI Views defined below:
Running a sample SwiftUI application, the following output was generated:
I noticed that
@State
properties that didn't work had their_location
property set to nil. This is a good indication of the cause of it not working.Another curious fact I found out about is that
@State
properties only had a non-nil_location
property in the context of their containing SwiftUI View'sbody
computed property or in the context of theonAppear
event handler which coincides with the statement in the aforementioned documentation:The inspection will be fully functional inside the didAppear callback.
Notice that in the output above the
_location
of the_childStatus
in the context of theParentView.onAppear
is nil.Test failure
The test below illustrates the issue as it fails with the following message:
testChildView(): XCTAssertNotEqual failed: ("childStatus: Initial") is equal to ("childStatus: Initial")
The text was updated successfully, but these errors were encountered: