### Chaining Publisher Inspections Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide.md Example of configuring multiple inspections for sequential values from a publisher, using `dropFirst()` for subsequent values. ```swift let exp1 = sut.inspection.inspect(onReceive: publisher) { view in // First value received } let exp2 = sut.inspection.inspect(onReceive: publisher.dropFirst()) { view in // Second value received } ``` -------------------------------- ### ContentView with Inspection Setup Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide.md Extend your SwiftUI View to include the `inspection` property and the `.onReceive` modifier to facilitate view inspection. ```swift struct ContentView: View { @State var flag: Bool = false internal let inspection = Inspection() // 1. var body: some View { Button(action: { self.flag.toggle() }, label: { Text(flag ? "True" : "False") }) .onReceive(inspection.notice) { self.inspection.visit(self, $0) } // 2. } } ``` -------------------------------- ### Implement Custom ProgressViewStyle Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide_styles.md Create a custom ProgressViewStyle conforming to the ProgressViewStyle protocol. This example includes label, currentValueLabel, and completion percentage. ```swift struct CustomProgressViewStyle: ProgressViewStyle { func makeBody(configuration: Configuration) -> some View { VStack { configuration.label .brightness(3) configuration.currentValueLabel .blur(radius: 5) Text("Completed: \(Int(configuration.fractionCompleted.flatMap { $0 * 100 } ?? 0))%") } } } ``` -------------------------------- ### Invoke LongPressGesture Updating Callback Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide_gestures.md This example demonstrates how to test the updating callback of a LongPressGesture, including verifying initial state and simulating updates. ```Swift struct TestGestureView5: View { @GestureState var isDetectingLongPress = false internal let inspection = Inspection() internal let publisher = PassthroughSubject() var body: some View { let press = LongPressGesture(minimumDuration: 1) .updating($isDetectingLongPress) { currentState, gestureState, transaction in gestureState = currentState } return Circle() .fill(isDetectingLongPress ? Color.yellow : Color.green) .frame(width: 100, height: 100, alignment: .center) .gesture(press) .onReceive(inspection.notice) { self.inspection.visit(self, $0) } .onReceive(publisher) { } } } ``` ```Swift func testTestGestureUpdating() throws { let sut = TestGestureView5() let exp1 = sut.inspection.inspect { XCTAssertEqual(try view.actualView().isDetectingLongPress, false) XCTAssertEqual(try view.shape(0).fillShapeStyle(Color.self), Color.green) let gesture = try view.shape(0).gesture(LongPressGesture.self) let value = LongPressGesture.Value(finished: true) var state: Bool = false var transaction = Transaction() try gesture.callUpdating(value: value, state: &state, transaction: &transaction) sut.publisher.send() } let exp2 = sut.inspection.inspect(onReceive: sut.publisher) { XCTAssertEqual(try view.shape(0).fillShapeStyle(Color.self), Color.green) } ViewHosting.host(view: sut) wait(for: [exp1, exp2], timeout: 0.1) } ``` -------------------------------- ### SwiftUI Alert Inspection Test Example Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide_popups.md Example test case demonstrating how to inspect an Alert presented using the custom `alert2` modifier. Asserts title, message, and button styles. ```swift func testAlertExample() throws { let binding = Binding(wrappedValue: true) let sut = EmptyView().alert2(isPresented: binding) { Alert(title: Text("Title"), message: Text("Message"), primaryButton: .destructive(Text("Delete")), secondaryButton: .cancel(Text("Cancel"))) } let alert = try sut.inspect().emptyView().alert() XCTAssertEqual(try alert.title().string(), "Title") XCTAssertEqual(try alert.message().string(), "Message") XCTAssertEqual(try alert.primaryButton().style(), .destructive) try sut.inspect().find(ViewType.AlertButton.self, containing: "Cancel").tap() } ``` -------------------------------- ### SwiftUI NavigationLink Body Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide.md Example of a SwiftUI NavigationView containing a VStack with a NavigationLink. ```swift var body: some View { NavigationView { VStack { // ...Various subviews... NavigationLink(destination: MyView(parameter: "Screen 1") { Text("Continue") } } } } ``` -------------------------------- ### Inspect Composed Gestures in SwiftUI Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide_gestures.md This example shows how to inspect a complex composed gesture, specifically a simultaneous gesture containing magnification, rotation, and drag gestures. It demonstrates accessing nested gestures within the composition. ```Swift func testComposedGestureComplex() throws { let sut = TestGestureView12() let exp = sut.inspection.inspect { view in let outerSimultaneousGesture = try view .vStack() .image(0) .gesture(SimultaneousGesture, DragGesture>.self) let innerSimultaneousGesture = try outerSimultaneousGesture.first(SimultaneousGesture.self) XCTAssertNoThrow(try innerSimultaneousGesture.first(MagnificationGesture.self)) } ViewHosting.host(view: sut) wait(for: [exp], timeout: 0.1) } ``` -------------------------------- ### Install ViewInspector via Swift Package Manager Source: https://context7.com/nalexn/viewinspector/llms.txt Add ViewInspector to your test target dependencies in Package.swift. Ensure it's only included in test targets. ```swift // Package.swift – add to test target only dependencies: [ .package(url: "https://github.com/nalexn/ViewInspector", from: "0.10.3") ], targets: [ .testTarget( name: "MyAppTests", dependencies: ["ViewInspector"]) ] ``` -------------------------------- ### Concrete Style Definition Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide_styles.md Define a concrete style conforming to a protocol. This example shows a `RedOutlineHelloWorldStyle` with a `StyleBody` that includes an `onAppear` callback. ```Swift struct RedOutlineHelloWorldStyle: HelloWorldStyle { func makeBody(configuration: HelloWorldStyleConfiguration) -> some View { StyleBody(configuration: configuration)) } struct StyleBody: View { let configuration: HelloWorldStyleConfiguration internal var didAppear: ((Self) -> Void)? var body: some View { ZStack { Rectangle() .strokeBorder(Color.red, lineWidth: 3, antialiased: true) } .onAppear { self.didAppear?(self) } } } } ``` -------------------------------- ### Get View Path to Root Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide.md Read the `pathToRoot` value to verify the library found the correct view. This can be printed in code or using LLDB. ```swift let view = try sut.inspect().find(viewWithId: 42) // print(view.pathToRoot) in the code or // lldb: po view.pathToRoot ``` -------------------------------- ### Synchronous Inspection with .inspect() Source: https://context7.com/nalexn/viewinspector/llms.txt Use the .inspect() extension on View to unwrap the view hierarchy for testing. Annotate tests with 'throws' as the call can fail. This example shows inspecting Text and Button labels within a VStack. ```swift import XCTest import ViewInspector @testable import MyApp struct ContentView: View { var body: some View { VStack { Text("Hello, world!") Button("Tap me") { } } } } final class ContentViewTests: XCTestCase { func testTextContent() throws { let sut = ContentView() // Mirror the body structure with chained calls let text = try sut.inspect().implicitAnyView().vStack().text(0) XCTAssertEqual(try text.string(), "Hello, world!") } func testButtonLabel() throws { let sut = ContentView() let label = try sut.inspect().implicitAnyView().vStack().button(1).labelView().text() XCTAssertEqual(try label.string(), "Tap me") } } ``` -------------------------------- ### Environment Object Injection Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide.md Demonstrates how to inject environment objects before hosting the view for inspection. ```swift ViewHosting.host(view: sut.environmentObject(...)) ``` -------------------------------- ### Build and Test Commands for ViewInspector Source: https://github.com/nalexn/viewinspector/blob/0.10.4/CLAUDE.md Standard commands for building the Swift package, running all tests, filtering tests by class or method, and listing available tests. ```bash swift build ``` ```bash swift test ``` ```bash swift test --filter ViewInspectorTests.TextTests ``` ```bash swift test --filter "testStringValue" ``` ```bash swift test list ``` -------------------------------- ### RadialGradient Source: https://github.com/nalexn/viewinspector/blob/0.10.4/readiness.md Represents a radial gradient with properties for gradient, center, start radius, and end radius. ```APIDOC ## RadialGradient ### Description Represents a radial gradient with properties for gradient, center, start radius, and end radius. ### Properties - `gradient` (Gradient) - The gradient to be rendered. - `center` (UnitPoint) - The center of the radial gradient. - `startRadius` (CGFloat) - The radius of the start of the gradient. - `endRadius` (CGFloat) - The radius of the end of the gradient. ``` -------------------------------- ### Quick Look Preview API (URL) Source: https://github.com/nalexn/viewinspector/blob/0.10.4/readiness.md Use this API to present a Quick Look preview for a single URL. It requires a binding to the URL. ```swift func quickLookPreview(_ item: Binding) -> some View ``` -------------------------------- ### Inspect Custom Button and Progress Styles Source: https://context7.com/nalexn/viewinspector/llms.txt Demonstrates how to inspect custom ButtonStyle and ProgressViewStyle implementations. Ensure the style conforms to the appropriate protocol and use the specific inspect methods for state. ```swift struct BlurButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label.blur(radius: configuration.isPressed ? 5 : 0) } } struct LoadingProgressStyle: ProgressViewStyle { func makeBody(configuration: Configuration) -> some View { VStack { configuration.label.brightness(3) configuration.currentValueLabel.blur(radius: 5) Text("Done: \(Int((configuration.fractionCompleted ?? 0) * 100))%") } } } ``` ```swift final class StyleTests: XCTestCase { func testButtonStylePressedState() throws { let sut = BlurButtonStyle() XCTAssertEqual(try sut.inspect(isPressed: false).blur().radius, 0) XCTAssertEqual(try sut.inspect(isPressed: true).blur().radius, 5) } func testProgressViewStyle() throws { let sut = LoadingProgressStyle() // Verify label brightness when no progress value XCTAssertEqual(try sut.inspect(fractionCompleted: nil).vStack() .styleConfigurationLabel(0).brightness(), 3) // Verify text with 42% progress XCTAssertEqual(try sut.inspect(fractionCompleted: 0.42).vStack() .text(2).string(), "Done: 42%") } func testAppliedStyle() throws { let sut = Button("OK") { }.buttonStyle(PlainButtonStyle()) XCTAssertTrue(try sut.inspect().buttonStyle() is PlainButtonStyle) } } ``` -------------------------------- ### Interaction Simulation - .tap(), .setInput(), .select() Source: https://context7.com/nalexn/viewinspector/llms.txt Simulate user interactions such as tapping buttons, inputting text into fields, and selecting options from pickers. These actions trigger associated callbacks and update bound state. ```APIDOC ## Interaction Simulation — `.tap()`, `.setInput()`, `.select()` ViewInspector lets tests simulate user interactions by calling action callbacks directly on controls, which updates bound state as if the user had acted. ### Example Usage: ```swift // Simulate button tap try sut.inspect().find(button: "Submit").tap() // Simulate text field input let field = try sut.inspect().find(ViewType.TextField.self) try field.setInput("alice@example.com") // Simulate picker selection let picker = try sut.inspect().find(ViewType.Picker.self) try picker.select(value: "Option B") // Call OnAppear for a list row try list[5].view(RowItemView.self).callOnAppear() ``` ``` -------------------------------- ### Async/Await Inspection with ViewHosting.host Source: https://context7.com/nalexn/viewinspector/llms.txt When writing `async` tests, `ViewHosting.host` manages the view lifecycle automatically when provided with a closure, eliminating the need for manual `ViewHosting.expel()`. ```swift final class AsyncTests: XCTestCase { func testAsyncInspection() async throws { let sut = ContentView(flag: false) try await ViewHosting.host(sut) { try await sut.inspection.inspect { view in let text = try view.button().labelView().text().string() XCTAssertEqual(text, "false") sut.publisher.send(true) } } } func testConcurrentPublisherInspection() async throws { let sut = ContentView(flag: false) try await ViewHosting.host(sut) { try await withThrowingDiscardingTaskGroup { group in group.addTask { try await sut.inspection.inspect { view in XCTAssertEqual(try view.button().labelView().text().string(), "false") sut.publisher.send(true) } } group.addTask { try await sut.inspection.inspect(onReceive: sut.publisher) { view in XCTAssertEqual(try view.button().labelView().text().string(), "true") } } } } } } ``` -------------------------------- ### Quick Look Preview API (Items) Source: https://github.com/nalexn/viewinspector/blob/0.10.4/readiness.md Use this API to present a Quick Look preview for a collection of items. It requires a binding for the selection and the collection of items. ```swift func quickLookPreview(_ selection: Binding, in items: Items) -> some View ``` -------------------------------- ### Picker View Source: https://github.com/nalexn/viewinspector/blob/0.10.4/readiness.md Represents a picker control with a contained view, label view, a select method, and a method to get the selected value. ```APIDOC ## Picker View ### Description Represents a picker control with a contained view, label view, a select method, and a method to get the selected value. ### Properties - `contained view` - The view contained within the picker. - `label view` - The view used as the label for the picker. ### Methods - `select(value: Hashable)` - Selects a specific value. - `selectedValue()` - Returns the currently selected value. ``` -------------------------------- ### Add New API Support Source: https://github.com/nalexn/viewinspector/blob/0.10.4/unsupported_swiftui_apis.md Use this command to add support for new SwiftUI APIs. Ensure to specify the API signature accurately. ```shell /new-api-support "Group(subviews:)" ``` ```shell /new-api-support "ScrollView scrollPosition" ``` ```shell /new-api-support "List swipeActions" ``` ```shell /new-api-support "NavigationStack navigationDestination" ``` ```shell /new-api-support "TabView Tab" ``` -------------------------------- ### Text View Source: https://github.com/nalexn/viewinspector/blob/0.10.4/readiness.md Represents a text view with properties for string value, attributes, attributed string, and images, and a method to get the string value. ```APIDOC ## Text View ### Description Represents a text view with properties for string value, attributes, attributed string, and images, and a method to get the string value. ### Properties - `attributes` (TextAttributes) - The attributes applied to the text. - `attributedString` (AttributedString) - The attributed string content. - `images` ([Image]) - An array of images embedded in the text. ### Methods - `string(locale: Locale) -> String` - Returns the string value localized for a given locale. ``` -------------------------------- ### Basic SwiftUI View Inspection Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide.md Demonstrates the fundamental steps for inspecting a SwiftUI view. Requires importing ViewInspector and annotating test functions with `throws`. ```swift struct ContentView: View { var body: some View { Text("Hello, world!") } } ``` ```swift import XCTest import ViewInspector // 1. @testable import MyApp final class ContentViewTests: XCTestCase { func testStringValue() throws { // 2. let sut = ContentView() let value = try sut.inspect().implicitAnyView().text().string() // 3. XCTAssertEqual(value, "Hello, world!") } } ``` -------------------------------- ### Define Environment Key and Value for Custom Style Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide_styles.md Sets up a custom environment key and extension for environment values to manage custom styles, addressing Swift's limitations with associated types in environment values. ```swift struct HelloWorldStyleKey: EnvironmentKey { static var defaultValue: AnyHelloWorldStyle = AnyHelloWorldStyle(DefaultHelloWorldStyle()) } extension EnvironmentValues { var helloWorldStyle: AnyHelloWorldStyle { get { self[HelloWorldStyleKey.self] } set { self[HelloWorldStyleKey.self] = newValue } } } ``` -------------------------------- ### SwiftUI Composed Gesture View Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide_gestures.md Defines a SwiftUI view that combines MagnificationGesture and RotationGesture into a SimultaneousGesture. This setup is useful for testing how ViewInspector handles nested gestures. ```swift @available(iOS 13.0, macOS 11.0, tvOS 13.0, watchOS 6.0, *) struct TestGestureView10: View { @State var scale: CGFloat = 1.0 @State var angle = Angle(degrees: 0) internal let inspection = Inspection() internal let publisher = PassthroughSubject() var body: some View { let magnificationGesture = MagnificationGesture() .onChanged { value in self.scale = value.magnitude } let rotationGesture = RotationGesture() .onChanged { value in self.angle = value } let gesture = SimultaneousGesture(magnificationGesture, rotationGesture) VStack { Image(systemName: "star.circle.fill") .font(.system(size: 200)) .foregroundColor(Color.red) .gesture(gesture) .rotationEffect(angle) .scaleEffect(scale) .animation(.easeInOut) .onReceive(inspection.notice) { self.inspection.visit(self, $0) } .onReceive(publisher) { } } } } ``` -------------------------------- ### SwiftUI View with Three Simple Gestures Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide_gestures.md Defines a SwiftUI view that composes three simple gestures (RotationGesture, MagnificationGesture, DragGesture) into a nested SimultaneousGesture. This example is for demonstration purposes. ```swift @available(iOS 13.0, macOS 11.0, tvOS 13.0, watchOS 6.0, *) struct TestGestureView12: View { internal let inspection = Inspection() internal let publisher = PassthroughSubject() var body: some View { let rotationGesture = RotationGesture() let magnificationGesture = MagnificationGesture() let dragGesture = DragGesture() let gesture = SimultaneousGesture( SimultaneousGesture(magnificationGesture, rotationGesture), dragGesture) ``` -------------------------------- ### Async Inspection with ViewHosting Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide.md Use this for async tests where ViewHosting.expel() is not needed. The API changes slightly to use a closure. ```swift let sut = TestView(flag: false) try await ViewHosting.host(sut) { try await sut.inspection.inspect { view in let text = try view.button().labelView().text().string() XCTAssertEqual(text, "false") sut.publisher.send(true) } } ``` -------------------------------- ### App Store Overlay API Source: https://github.com/nalexn/viewinspector/blob/0.10.4/readiness.md Use this API to present an App Store overlay. It requires a binding to control presentation and a closure to configure the overlay. ```swift func appStoreOverlay(isPresented: Binding, configuration: @escaping () -> SKOverlay.Configuration) -> some View ``` -------------------------------- ### Inspect @State/@EnvironmentObject with onAppear Source: https://context7.com/nalexn/viewinspector/llms.txt Add two lines to your production view to enable inspection within the `onAppear` callback. This approach avoids adding test framework dependencies to your main target. ```swift // Production view (main target) – add two lines: struct CounterView: View { @State var count: Int = 0 internal var didAppear: ((Self) -> Void)? // line 1 var body: some View { Button("Increment") { count += 1 } .onAppear { self.didAppear?(self) } // line 2 } } ``` ```swift // Test (test target): final class CounterViewTests: XCTestCase { func testIncrementButtonUpdatesCount() { var sut = CounterView() let exp = sut.on(\.didAppear) { view in XCTAssertEqual(try view.actualView().count, 0) try view.find(button: "Increment").tap() XCTAssertEqual(try view.actualView().count, 1) } ViewHosting.host(view: sut) defer { ViewHosting.expel() } wait(for: [exp], timeout: 0.1) } } ``` -------------------------------- ### Quick Look Preview (Multiple Items) Source: https://github.com/nalexn/viewinspector/blob/0.10.4/readiness.md Provides a Quick Look preview interface for a collection of items. Allows selection of a specific item within the collection. ```APIDOC ## quickLookPreview (Items) ### Description Enables Quick Look preview for a collection of items. ### Signature `func quickLookPreview(_ selection: Binding, in items: Items) -> some View` ### Parameters - **selection** (Binding) - A binding to the currently selected item for preview. - **items** (Items) - The collection of items to preview. ``` -------------------------------- ### Quick Look Preview (Single Item) Source: https://github.com/nalexn/viewinspector/blob/0.10.4/readiness.md Provides a Quick Look preview interface for a single URL. The presentation is controlled by a binding. ```APIDOC ## quickLookPreview (URL) ### Description Provides a Quick Look preview for a single URL. ### Signature `func quickLookPreview(_ item: Binding) -> some View` ### Parameters - **item** (Binding) - A binding to the URL to be previewed. ``` -------------------------------- ### App Store Overlay Source: https://github.com/nalexn/viewinspector/blob/0.10.4/readiness.md Presents an App Store overlay to the user, allowing them to view or purchase an app. The overlay's configuration is provided by a closure. ```APIDOC ## appStoreOverlay ### Description Displays an App Store overlay. ### Signature `func appStoreOverlay(isPresented: Binding, configuration: @escaping () -> SKOverlay.Configuration) -> some View` ### Parameters - **isPresented** (Binding) - A binding to control the presentation of the overlay. - **configuration** (@escaping () -> SKOverlay.Configuration) - A closure that returns the `SKOverlay.Configuration` for the overlay. ``` -------------------------------- ### ShortcutsLink Source: https://github.com/nalexn/viewinspector/blob/0.10.4/readiness.md Represents a link for triggering shortcuts. ```APIDOC ## ShortcutsLink ### Description Represents a link for triggering shortcuts. ``` -------------------------------- ### Apply Environment Object to View Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide.md Demonstrates how to apply an environment object to a view containing a modifier before hosting it. This is useful for modifiers that depend on environment data. ```swift let view = EmptyView().modifier(sut).environmentObject(envObject) ViewHosting.host(view: view) ``` -------------------------------- ### Apply Custom Style to View Hierarchy Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide_styles.md Demonstrates how to define a concrete style with specific visual properties (red outline) and apply it to a view hierarchy using the custom convenience method. ```swift struct Content: View { var body: some View { HelloWorld() .helloWorldStyle(RedOutlineHelloWorldStyle()) } } struct RedOutlineHelloWorldStyle: HelloWorldStyle { func makeBody(configuration: HelloWorldStyleConfiguration) -> some View { ZStack { Rectangle() .strokeBorder(Color.red, lineWidth: 3, antialiased: true) } } } ``` -------------------------------- ### Music Subscription Offer API Source: https://github.com/nalexn/viewinspector/blob/0.10.4/readiness.md Use this API to present a music subscription offer. It requires a binding for presentation control, options, and a load completion handler. ```swift func musicSubscriptionOffer(isPresented: Binding, options: ..., onLoadCompletion: ...) -> some View ``` -------------------------------- ### WatchOS App with @main for ViewInspector Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide_watchOS.md Integrate this code into your watchOS app's main App struct and ExtensionDelegate to enable ViewInspector testing. Ensure WKExtensionDelegateAdaptor is correctly set up. ```swift final class ExtensionDelegate: NSObject, WKExtensionDelegate { let testViewSubject = TestViewSubject([]) // #2 } @main struct MyWatchOSApp: App { @WKExtensionDelegateAdaptor(ExtensionDelegate.self) var extDelegate // #1 var body: some Scene { WindowGroup { ContentView() .testable(extDelegate.testViewSubject) // #3 } } } ``` -------------------------------- ### Inspect Text Attributes Source: https://context7.com/nalexn/viewinspector/llms.txt Verify font, color, bold, italic, kerning, and locale-aware string rendering of Text views. Requires importing Foundation for Locale. ```swift final class TextAttributeTests: XCTestCase { func testFormattedTextWithLocale() throws { let sut = Text("Completed by \(72.51, specifier: \"%.1f\")%").font(.caption) let text = try sut.inspect().text() // Locale-aware rendering let es = try text.string(locale: Locale(identifier: "es")) XCTAssertEqual(es, "Completado por 72,5%") // Font attribute XCTAssertEqual(try text.attributes().font(), .caption) } func testTextModifiers() throws { let sut = Text("Hello") .bold() .italic() .foregroundColor(.red) .kerning(2) let text = try sut.inspect().text() let attrs = try text.attributes() XCTAssertTrue(try attrs.isBold()) XCTAssertTrue(try attrs.isItalic()) XCTAssertEqual(try attrs.foregroundColor(), .red) XCTAssertEqual(try attrs.kerning(), 2) } } ``` -------------------------------- ### Simulate User Interactions in Swift Source: https://context7.com/nalexn/viewinspector/llms.txt Simulate user interactions like tapping buttons, entering text, and selecting picker options. These actions trigger associated callbacks and update bound state. ```swift final class InteractionTests: XCTestCase { func testButtonTogglesBoundFlag() throws { let flag = Binding(wrappedValue: false) let sut = ToggleView(isOn: flag) XCTAssertFalse(flag.wrappedValue) try sut.inspect().find(button: "Submit").tap() XCTAssertTrue(flag.wrappedValue) } func testTextFieldInput() throws { let sut = LoginView() let field = try sut.inspect().find(ViewType.TextField.self) XCTAssertEqual(try field.input(), "") try field.setInput("alice@example.com") XCTAssertEqual(try field.input(), "alice@example.com") } func testPickerSelection() throws { let sut = FilterView() let picker = try sut.inspect().find(ViewType.Picker.self) try picker.select(value: "Option B") XCTAssertEqual(try picker.selectedValue() as? String, "Option B") } func testListRowOnAppear() throws { let list = try sut.inspect().list() try list[5].view(RowItemView.self).callOnAppear() } } ``` -------------------------------- ### SiriTipView Source: https://github.com/nalexn/viewinspector/blob/0.10.4/readiness.md Represents a view for Siri tips. ```APIDOC ## SiriTipView ### Description Represents a view for Siri tips. ``` -------------------------------- ### Inspect First Gesture in Composition Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide_gestures.md Tests inspecting the first gesture (MagnificationGesture) within a SimultaneousGesture. It demonstrates how to extract the gesture and invoke its onChanged callback. ```swift func testComposedGestureFirst() throws { let sut = TestGestureView10() let exp1 = sut.inspection.inspect { view in let simultaneousGesture = try view .vStack() .image(0) .gesture(SimultaneousGesture.self) let magnificationGesture = try simultaneousGesture .first(MagnificationGesture.self) let value = MagnificationGesture.Value(2.0) try magnificationGesture.callOnChanged(value: value) sut.publisher.send() } let exp2 = sut.inspection.inspect { view in XCTAssertEqual(try view.actualView().scale, 2.0) } ViewHosting.host(view: sut) wait(for: [exp1, exp2], timeout: 0.1) } ``` -------------------------------- ### Multiple Inspections with Publisher Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide.md A test case demonstrating multiple delayed and publisher-triggered inspections within a single test, verifying state changes. ```swift final class ContentViewTests: XCTestCase { func testPublisherChangingValue() { let publisher = PassthroughSubject() let sut = ContentView(publisher: publisher) let exp1 = sut.inspection.inspect { view in XCTAssertFalse(try view.actualView().flag) publisher.send(true) } let exp2 = sut.inspection.inspect(onReceive: publisher) { view in XCTAssertTrue(try view.actualView().flag) publisher.send(false) } let exp3 = sut.inspection.inspect(after: 0.2) { view in XCTAssertFalse(try view.actualView().flag) } ViewHosting.host(view: sut) defer { ViewHosting.expel() } wait(for: [exp1, exp2, exp3], timeout: 0.3) } } ``` -------------------------------- ### NowPlayingView Source: https://github.com/nalexn/viewinspector/blob/0.10.4/readiness.md Represents the Now Playing view. ```APIDOC ## NowPlayingView ### Description Represents the Now Playing view. ``` -------------------------------- ### find(_:) and findAll(_:) - Dynamic Hierarchy Search Source: https://context7.com/nalexn/viewinspector/llms.txt These methods allow for searching within the view hierarchy. `find` returns the first match, while `findAll` returns all matches. They support searching by type, label, ID, accessibility identifier, or a custom condition. ```APIDOC ## `find(_:)` and `findAll(_:)` — Dynamic Hierarchy Search `find` performs a breadth-first traversal and returns the first matching view, throwing if none found. `findAll` performs a depth-first traversal and returns all matches as an array (never throws). Both accept a type, a shorthand label string, an `id`/`tag`/accessibility identifier, or a custom `where` condition. ### Example Usage: ```swift // Find by button text label let button = try sut.inspect().find(button: "Back") // Find by view type let hStack = try sut.inspect().find(ViewType.HStack.self) // Find by id modifier let tagged = try sut.inspect().find(viewWithId: 42) // Find using custom condition let boldTexts = try sut.inspect().findAll(ViewType.Text.self, where: { try $0.attributes().isBold() }) // Find by accessibility identifier let playBtn = try sut.inspect().find(viewWithAccessibilityIdentifier: "play_button") // Find parent by child text let cell = try sut.find(TableViewCell.self, containing: "Cell's title") ``` ``` -------------------------------- ### Handling Implicit AnyView in SwiftUI Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide.md Illustrates how to navigate views that contain implicit `AnyView` wrappers, common in newer Swift compiler versions. Use `.anyView()` or `.implicitAnyView()` to unwrap. ```swift struct MyView: View { var body: some View { HStack { Text("Hi") AnyView(OtherView()) } } } struct OtherView: View { var body: some View { Text("Ok") } } ``` ```swift let view = MyView() view.inspect().implicitAnyView().hStack().anyView(1).view(OtherView.self).text() ``` ```swift let view = MyView() let hStack = try view.inspect().implicitAnyView().hStack() let hiText = try hStack.text(0) let okText = try hStack.anyView(1).view(OtherView.self).text() ``` ```swift hStack[1].anyView() ``` -------------------------------- ### Dynamic View Finding with `find` Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide.md Shows how to use the `find` function to locate specific views within the hierarchy without writing the full inspection chain. `find` traverses breadth-first. ```swift try sut.inspect().implicitAnyView().anyView().find(ViewType.HStack.self).text(1) ``` ```swift try sut.inspect().find(where: { ... }).zStack() ``` ```swift .find(text: "xyz") // returns Text ``` ```swift .find(button: "xyz") // returns Button which label contains Text("xyz") ``` ```swift .find(viewWithId: 7) // returns a view with modifier .id(7) ``` ```swift .find(viewWithTag: "Home") // returns a view with modifier .tag("Home") ``` ```swift .find(ViewType.HStack.self) // returns the first found HStack ``` ```swift .find(CustomView.self) // returns CustomView ``` ```swift .find(viewWithAccessibilityLabel: "Play button") // returns the first view with accessibilityLabel "Play button" ``` ```swift .find(viewWithAccessibilityIdentifier: "play_button") // returns the first view with accessibilityIdentifier "play_button" ``` -------------------------------- ### Full Async Support with Inspection + InspectionEmissary Source: https://context7.com/nalexn/viewinspector/llms.txt Use `Inspection` and `InspectionEmissary` for advanced scenarios like delayed inspection or publisher-driven re-inspection. This requires adding `Inspection` to the main target and conforming it in the test target. ```swift // Shared snippet – add once to the main (build) target: import Combine import SwiftUI internal final class Inspection { let notice = PassthroughSubject() var callbacks = [UInt: (V) -> Void]() func visit(_ view: V, _ line: UInt) { if let callback = callbacks.removeValue(forKey: line) { callback(view) } } } ``` ```swift // Test target only: extension Inspection: InspectionEmissary { } ``` ```swift // Production view (main target) – add two lines: struct TimerView: View { @State var elapsed: Int = 0 let publisher = PassthroughSubject() internal let inspection = Inspection() // line 1 var body: some View { Text("Elapsed: \(elapsed)") .onReceive(inspection.notice) { self.inspection.visit(self, $0) } // line 2 .onReceive(publisher) { self.elapsed = $0 } } } ``` ```swift // Test – multiple inspections, publisher-driven: final class TimerViewTests: XCTestCase { func testPublisherUpdatesText() { let sut = TimerView() let exp1 = sut.inspection.inspect { view in XCTAssertEqual(try view.find(ViewType.Text.self).string(), "Elapsed: 0") sut.publisher.send(5) } let exp2 = sut.inspection.inspect(onReceive: sut.publisher) { view in XCTAssertEqual(try view.find(ViewType.Text.self).string(), "Elapsed: 5") } let exp3 = sut.inspection.inspect(after: 0.2) { view in // Inspect after a 0.2-second delay XCTAssertEqual(try view.actualView().elapsed, 5) } ViewHosting.host(view: sut) defer { ViewHosting.expel() } wait(for: [exp1, exp2, exp3], timeout: 0.5) } } ``` -------------------------------- ### Music Subscription Offer Source: https://github.com/nalexn/viewinspector/blob/0.10.4/readiness.md Presents an offer for a music subscription. This function allows for configuration of the offer presentation and handles load completion events. ```APIDOC ## musicSubscriptionOffer ### Description Displays an offer for a music subscription. ### Signature `func musicSubscriptionOffer(isPresented: Binding, options: ..., onLoadCompletion: ...) -> some View` ### Parameters - **isPresented** (Binding) - A binding to control the presentation of the offer. - **options** (...) - Configuration options for the offer. - **onLoadCompletion** (...) - A closure to be called upon completion of the offer load. ``` -------------------------------- ### Configuring View Previews Source: https://github.com/nalexn/viewinspector/blob/0.10.4/readiness.md Modifiers for configuring the appearance and behavior of view previews. ```APIDOC ## Preview Modifiers ### Description These modifiers are used to customize how your views are rendered in the preview canvas. ### Methods - `previewDevice(PreviewDevice?) -> some View`: Sets the target device for the preview. - `previewDisplayName(String?) -> some View`: Sets a custom display name for the preview. - `previewLayout(PreviewLayout) -> some View`: Specifies the layout behavior for the preview. - `previewContext(C) -> some View`: Provides a custom context for the preview. - `previewInterfaceOrientation(_ value: InterfaceOrientation) -> some View`: Sets the interface orientation for the preview. ``` -------------------------------- ### Inspection on Publisher Emission Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide.md Shows how to inspect a view right after a Publisher emits a value using the `onReceive` parameter. ```swift let exp = sut.inspection.inspect(onReceive: publisher) { view in ... } ``` -------------------------------- ### WidgetConfiguration Supported Families Source: https://github.com/nalexn/viewinspector/blob/0.10.4/readiness.md Specifies the families of devices (e.g., compact, regular, large) for which the widget configuration is supported. ```APIDOC ## supportedFamilies ### Description Defines the supported widget families for this configuration. ### Signature `func supportedFamilies(_ families: [WidgetFamily]) -> some WidgetConfiguration` ### Parameters - **families** ([WidgetFamily]) - An array of `WidgetFamily` values indicating supported sizes and contexts. ``` -------------------------------- ### Implement Custom ToggleStyle Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide_styles.md Define a custom ToggleStyle by conforming to the ToggleStyle protocol. Use this for custom toggle appearances. ```swift struct CustomToggleStyle: ToggleStyle { func makeBody(configuration: Configuration) -> some View { configuration.label .blur(radius: configuration.isOn ? 5 : 0) } } ``` -------------------------------- ### SharePreview Source: https://github.com/nalexn/viewinspector/blob/0.10.4/readiness.md Configuration for the preview of shared content, including title, image, and icon. ```APIDOC ## SharePreview ### Description Configuration for the preview of shared content, including title, image, and icon. ### Properties - `title` (String) - The title of the share preview. - `image` - The image to display in the share preview. - `icon` - The icon to display in the share preview. ``` -------------------------------- ### Access UIKit View Instance Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide.md Shows how to access the underlying UIKit view or view controller from a `UIViewRepresentable` or `UIViewControllerRepresentable` after inspecting the SwiftUI view. ```swift let swiftuiView = try sut.inspect().find(MyCustomView.self) let uikitView = try swiftuiView.actualView().uiView() // or .viewController() ``` -------------------------------- ### Conditional View Finding with `find` Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide.md Demonstrates using the `where` parameter with `find` to specify a condition for locating a view. This allows for more precise searching based on view properties. ```swift .find(ViewType.Text.self, where: { try $0.string() == "abc" }) ``` -------------------------------- ### Define Custom Style Protocol and Concrete Style Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide_styles.md Defines a protocol for custom styles with an associated body type and configuration. Includes a concrete style conforming to the protocol and a view that applies this style via environment. ```swift struct HelloWorldStyleConfiguration {} protocol HelloWorldStyle { associatedtype Body: View typealias Configuration = HelloWorldStyleConfiguration func makeBody(configuration: Self.Configuration) -> Self.Body } struct DefaultHelloWorldStyle: HelloWorldStyle { func makeBody(configuration: HelloWorldStyleConfiguration) -> some View { ZStack { Rectangle() .strokeBorder(Color.accentColor, lineWidth: 1, antialiased: true) } } } struct HelloWorld: View { @Environment(\.helloWorldStyle) var style var body: some View { ZStack { Text("Hello World!") style.makeBody(configuration: HelloWorldStyle.Configuration()) } } } ``` -------------------------------- ### MenuStyleConfiguration.Content Source: https://github.com/nalexn/viewinspector/blob/0.10.4/readiness.md Configuration for the content of a menu style. ```APIDOC ## MenuStyleConfiguration.Content ### Description Configuration for the content of a menu style. ``` -------------------------------- ### Basic Inspection Test Case Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide.md A basic test case to verify button tap functionality and flag toggling using ViewInspector. ```swift final class ContentViewTests: XCTestCase { func testButtonTogglesFlag() { let sut = ContentView() let exp = sut.inspection.inspect { view in XCTAssertFalse(try view.actualView().flag) try view.button().tap() XCTAssertTrue(try view.actualView().flag) } ViewHosting.host(view: sut) defer { ViewHosting.expel() } wait(for: [exp], timeout: 0.1) } } ``` -------------------------------- ### .actualView() - Accessing Custom View State Source: https://context7.com/nalexn/viewinspector/llms.txt The `.actualView()` method retrieves the actual Swift struct instance of a custom view, providing access to live state properties like `@State`, `@Binding`, `@ObservedObject`, and `@EnvironmentObject`. ```APIDOC ## `.actualView()` — Accessing Custom View State `actualView()` returns the actual Swift struct instance of a custom view, including live `@State`, `@Binding`, `@ObservedObject`, and `@EnvironmentObject` values, making it possible to assert on view model state directly. ### Example Usage: ```swift // Obtain the live custom view struct let actual = try sut.inspect().find(ProfileView.self).actualView() XCTAssertTrue(actual.viewModel.isLoggedIn) ``` ``` -------------------------------- ### Test Custom ProgressViewStyle Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide_styles.md Inspect custom ProgressViewStyle to verify label brightness, currentValueLabel blur radius, and completion percentage text. ```swift func testCustomProgressViewStyle() throws { let sut = CustomProgressViewStyle() XCTAssertEqual(try sut.inspect(fractionCompleted: nil).vStack().styleConfigurationLabel(0).brightness(), 3) XCTAssertEqual(try sut.inspect(fractionCompleted: nil).vStack().styleConfigurationCurrentValueLabel(1).blur().radius, 5) XCTAssertEqual(try sut.inspect(fractionCompleted: 0.42).vStack().text(2).string(), "Completed: 42%") } ``` -------------------------------- ### WatchOS App with Storyboard for ViewInspector Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide_watchOS.md Adapt your root interface controller for ViewInspector testing when using storyboards. This involves subclassing WKHostingController and modifying the body property. ```swift final class RootInterfaceController: WKHostingController> { // #1 , #5 let testViewSubject = TestViewSubject([]) // #2 override var body: RootView { // #4 return ContentView() .testable(testViewSubject) // #3 } } struct ContentView: View { var body: some View { Text("Hi") } } ``` -------------------------------- ### Async Inspection with Task Groups for Combine Publishers Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide.md Use task groups when testing async views that rely on Combine publishers to ensure inspection subscribes in time. This allows for concurrent inspection of different states. ```swift let sut = TestView(flag: false) try await ViewHosting.host(sut) { try await withThrowingDiscardingTaskGroup { group in group.addTask { try await sut.inspection.inspect { view in let text = try view.button().labelView().text().string() XCTAssertEqual(text, "false") sut.publisher.send(true) } } group.addTask { try await sut.inspection.inspect(onReceive: sut.publisher) { view in let text = try view.button().labelView().text().string() XCTAssertEqual(text, "true") sut.publisher.send(false) } } } } ``` -------------------------------- ### Inspect DragGesture Properties Source: https://github.com/nalexn/viewinspector/blob/0.10.4/guide_gestures.md Use this snippet to verify the configuration of a DragGesture, such as its minimum distance and coordinate space. ```Swift struct TestGestureView4: View { @State var isDragging = false var body: some View { let drag = DragGesture(minimumDistance: 20, coordinateSpace: .global) .onChanged { _ in self.isDragging = true } .onEnded { _ in self.isDragging = false } return Rectangle() .fill(self.isDragging ? Color.blue : Color.red) .frame(width: 10, height: 10) .gesture(drag) } } ``` ```Swift func testTestGestureModifier() throws { let sut = TestGestureView() let rectangle = try sut.inspect().shape(0) let gesture = try rectangle.gesture(DragGesture.self).actualGesture() XCTAssertEqual(gesture.minimumDistance, 20) XCTAssertEqual(gesture.coordinateSpace, .global) } ``` -------------------------------- ### ProgressViewStyleConfiguration.Label Source: https://github.com/nalexn/viewinspector/blob/0.10.4/readiness.md Configuration for the label of a progress view style. ```APIDOC ## ProgressViewStyleConfiguration.Label ### Description Configuration for the label of a progress view style. ``` -------------------------------- ### Inspect Native Popups with Wrapper Source: https://context7.com/nalexn/viewinspector/llms.txt To inspect native popups like alerts, sheets, and popovers, a wrapper ViewModifier is required in the main target. confirmationDialog does not need a wrapper. ```swift // Main target – wrapper for Alert: extension View { func alert2(isPresented: Binding, content: @escaping () -> Alert) -> some View { modifier(InspectableAlert(isPresented: isPresented, popupBuilder: content)) } } struct InspectableAlert: ViewModifier { let isPresented: Binding let popupBuilder: () -> Alert let onDismiss: (() -> Void)? = nil func body(content: Self.Content) -> some View { content.alert(isPresented: isPresented, content: popupBuilder) } } // Test target: extension InspectableAlert: PopupPresenter { } ``` ```swift final class AlertTests: XCTestCase { func testAlertContents() throws { let binding = Binding(wrappedValue: true) let sut = EmptyView().alert2(isPresented: binding) { Alert(title: Text("Title"), message: Text("Message"), primaryButton: .destructive(Text("Delete")), secondaryButton: .cancel(Text("Cancel"))) } let alert = try sut.inspect().emptyView().alert() XCTAssertEqual(try alert.title().string(), "Title") XCTAssertEqual(try alert.message().string(), "Message") XCTAssertEqual(try alert.primaryButton().style(), .destructive) // Tap the Cancel button try sut.inspect().find(ViewType.AlertButton.self, containing: "Cancel").tap() } func testConfirmationDialog() throws { // No wrapper needed for confirmationDialog let sut = ContentViewWithDialog() let dialog = try sut.inspect().find(ViewType.ConfirmationDialog.self) XCTAssertEqual(try dialog.title().string(), "Are you sure?") } } ``` -------------------------------- ### WidgetConfiguration Description Source: https://github.com/nalexn/viewinspector/blob/0.10.4/readiness.md Sets a description for a widget configuration. This description provides more details about the widget's functionality. ```APIDOC ## description ### Description Sets the description for a widget configuration. ### Signature `func description(...) -> some WidgetConfiguration` ### Parameters - **...** - Placeholder for parameters related to the description. ```