Runtime introspection and unit testing of SwiftUI views

Overview

ViewInspector 🕵️‍♂️ for SwiftUI

Platform Build Status codecov

ViewInspector is a library for unit testing SwiftUI views. It allows for traversing a view hierarchy at runtime providing direct access to the underlying View structs.

Why?

SwiftUI view is a function of state. We could provide it with the input, but were unable to verify the output... Until now!

Helpful links

Use cases

1. Search the view of a specific type or condition

Use one of the find functions to quickly locate a specific view or assert there are none of such:

try sut.inspect().find(button: "Back")

try sut.inspect().findAll(ViewType.Text.self,
                          where: { try $0.attributes().isBold() })

Check out this section in the guide for the reference.

2. Read the inner state of the standard views

Standard SwiftUI views are no longer a black box:

let sut = Text("Completed by \(72.51, specifier: "%.1f")%").font(.caption)

let string = try sut.inspect().text().string(locale: Locale(identifier: "es"))
XCTAssertEqual(string, "Completado por 72,5%")

XCTAssertEqual(try sut.inspect().text().attributes().font(), .caption)

Each view has its own set of inspectable parameters, you can refer to the API coverage document to see what's available for a particular SwiftUI view.

3. Verify your custom view's state

Obtain a copy of your custom view with actual state and references from the hierarchy of any depth:

let sut = try view.inspect().find(CustomView.self).actualView()
XCTAssertTrue(sut.viewModel.isUserLoggedIn)

The library can operate with various types of the view's state, such as @Binding, @State, @ObservedObject and @EnvironmentObject.

4. Trigger side effects

You can simulate user interaction by programmatically triggering system-controls callbacks:

try sut.inspect().find(button: "Close").tap()

let list = try view.inspect().list()
try list[5].view(RowItemView.self).callOnAppear()

The library provides helpers for writing asynchronous tests for views with callbacks.

FAQs

Which views and modifiers are supported?

Check out the API coverage. There is currently almost full support for SwiftUI v1 API, the v2 and v3 support is under active development.

Is it using private APIs?

ViewInspector is using official Swift reflection API to dissect the view structures. So it'll be production-friendly even if you could somehow ship the test target to the production.

How do I add it to my Xcode project?

Assure you're adding the framework to your unit-test target. Do NOT add it to the main build target.

Swift Package Manager

https://github.com/nalexn/ViewInspector

Carthage

github "nalexn/ViewInspector"

CocoaPods

pod 'ViewInspector'

How do I use it in my project?

Please refer to the Inspection guide. You can also check out my other project that harnesses the ViewInspector for testing the entire UI.

Other questions, concerns or suggestions?

Ping me on Twitter or just submit an issue or a pull request on Github.


blog venmo

Comments
  • Inspecting Custom View Modifiers Issue

    Inspecting Custom View Modifiers Issue

    Consider the following ViewModifier:

    @available(iOS 13.0, macOS 10.15, tvOS 13.0, *)
    private struct InspectableTestModifier: ViewModifier, Inspectable {
        
        @Binding var flag: Bool
        
        var didAppear: ((Self.Body) -> Void)?
        
        func body(content: Self.Content) -> some View {
            HStack {
                content
                Button(
                    action: { self.flag.toggle() },
                    label: { Text(flag ? "true" : "false") }
                )
            }
            .onAppear { self.didAppear?(self.body(content: content)) }
        }
    }
    

    And the following test:

        func testViewModifierOnFunction() throws {
            @Binding var flag = false
            var sut = InspectableTestModifier(flag: $flag)
            let exp = sut.on(\.didAppear) { body in
                let text1 = try body.find(relation: .child, traversal: .breadthFirst, skipFound: 0) {
                    try $0.text().string() == "false"
                }
                let text2 = try body.find(relation: .child, traversal: .breadthFirst, skipFound: 1) {
                    try $0.text().string() == "false"
                }
                
                var label1 = try body.hStack()
                    .button(1).labelView().text().string()
                var label2 = try body.hStack().modifier(InspectableTestModifier.self).hStack()
                    .button(1).labelView().text().string()
                print("\(text1.pathToRoot) = \(label1)")
                print("\(text2.pathToRoot) = \(label2)")
                
                try body.hStack().button(1).tap()
                
                label1 = try body.hStack()
                    .button(1).labelView().text().string()
                label2 = try body.hStack().modifier(InspectableTestModifier.self).hStack()
                    .button(1).labelView().text().string()
                print("\(text1.pathToRoot) = \(label1)")
                print("\(text2.pathToRoot) = \(label2)")
            }
            ViewHosting.host(view: Circle().modifier(sut))
            wait(for: [exp], timeout: 3.2)
        }
    

    Here's a result of running the test:

    Test Suite 'Selected tests' started at 2021-05-08 15:58:43.465
    Test Suite 'ViewInspectorTests.xctest' started at 2021-05-08 15:58:43.466
    Test Suite 'InspectionEmissaryTests' started at 2021-05-08 15:58:43.466
    Test Case '-[ViewInspectorTests.InspectionEmissaryTests testViewModifierOnFunction]' started.
    hStack().button(1).labelView().text() = false
    hStack().modifier(InspectableTestModifier.self).hStack().button(1).labelView().text() = false
    hStack().button(1).labelView().text() = false
    hStack().modifier(InspectableTestModifier.self).hStack().button(1).labelView().text() = true
    Test Case '-[ViewInspectorTests.InspectionEmissaryTests testViewModifierOnFunction]' passed (0.177 seconds).
    Test Suite 'InspectionEmissaryTests' passed at 2021-05-08 15:58:43.644.
    	 Executed 1 test, with 0 failures (0 unexpected) in 0.177 (0.178) seconds
    Test Suite 'ViewInspectorTests.xctest' passed at 2021-05-08 15:58:43.644.
    	 Executed 1 test, with 0 failures (0 unexpected) in 0.177 (0.179) seconds
    Test Suite 'Selected tests' passed at 2021-05-08 15:58:43.645.
    	 Executed 1 test, with 0 failures (0 unexpected) in 0.177 (0.180) seconds
    Program ended with exit code: 0
    

    I've been chasing this for a couple weeks and cannot unravel this mystery.

    Question #1, do you have any idea why the view generates the second instance of the label?

    Question #2, do you have any idea why the second instance of the label has the root-to-path that it has?

    Question #3, why does the button tap change the second instance of the label, but not the first?

    opened by gili-labs 22
  • Inspector emissary for view modifier

    Inspector emissary for view modifier

    @nalexn I have created this PR with the changes I made to InspectionEmissary to support view modifiers. It works great. However, the testViewModifierOnFunction test fails. I left it this way on purpose in order that you can look at the difference between this and the InspectionEmissaryForViewModifier. I do not see a difference. I dumped the body of the modifier in the debugger, and they seem identical and cannot ascertain why the ViewInspector would treat them differently.

    opened by gili-labs 18
  • Issue when inspecting views that interact with navigationBarItems

    Issue when inspecting views that interact with navigationBarItems

    Hi! Awesome work here, thank you! Given this view and its test:

    struct DetailView: View {
        var body: some View {
            EmptyView()
            .navigationBarItems(trailing: Text("Done")) // commenting this makes the test run fine.
        }
    }
    
    func testNavigationBarItems() {
      expect {
        // expected to not throw any error, got <notSupported("Please use \'navigationBarItems()\' for unwrapping the underlying view hierarchy.")>
        try detailView.body.inspect().emptyView()
      }.notTo(throwError())
      expect {
        // expected to not throw any error, got <modifierNotFound(parent: "DetailView", modifier: "navigationBarItems")>
        try detailView.inspect().navigationBarItems().emptyView()
      }.notTo(throwError())
    }
    

    Is this not supported, or am I missing something?

    opened by nobre84 17
  • Could not install on Xcode 12.2 beta

    Could not install on Xcode 12.2 beta

    I am on Xcode 12.2 beta and I could not install the package. After following the steps described in the documentation the "Finish" button did not become highlighted. Is this a known issue?

    Screenshot 2020-10-07 at 16 47 19 Thanks

    opened by multitudes 13
  • Click on alert button

    Click on alert button

    Hi @nalexn do you know if it is possible to click on an alert view button

    struct ContentView: View {
        
        @State private var showingAlert = false
    
        var body: some View {
            Button("Show Alert") {
                showingAlert = true
            }
            .alert(isPresented: $showingAlert) {
                Alert(title: Text("Important message"), message: Text("Wear sunscreen"), dismissButton: .default(Text("Got it!")))
            }
        }
    
    }
    

    what I've tried

        func testClickOnAlertButton() throws {
            // Given
            let view = ContentView()
            ViewHosting.host(view: view)
            
            let button = try view.inspect()
                .find(ContentView.self)
                .find(text: "Got it!")
                
            print(button)
        }
    

    How do I click on the Got it! button?

    opened by 2inqui 9
  • Error compiling unit tests on macOS

    Error compiling unit tests on macOS

    Hello,

    I've experiencing an issue writing SwiftUI unit tests on macOS 10.5.7 and 11.2.1, Xcode Version 12.4 (12D4e).

    A simple view:

    import SwiftUI
    
    struct ContentView: View {
        var body: some View {
            Text("Hello, World!")
        }
    }
    

    with a simple test case:

    import XCTest
    import ViewInspector
    
    @testable import ViewInspectorIssue
    
    extension ContentView: Inspectable { }
    
    class ContentViewTests: XCTestCase {
    
        func testExample() throws {
            let sut = ContentView()
        }
    
    }
    

    fails to compile with the error:

    xctest (19241) encountered an error...
    ...Underlying Error: The bundle “ViewInspectorIssueTests” couldn’t be loaded. The bundle couldn’t be loaded...
    ...Symbol not found: _$s7SwiftUI16PageTabViewStyleV16IndexDisplayModeVM...
    

    So, I cloned the repository and discovered those struct's are not being marked unavailable or ignored for macOS builds.

    I think the lines with:

    @available(iOS 14.0, tvOS 14.0, *)
    @available(macOS, unavailable)
    ...
    

    Should be:

    #if canImport(UIKit)
    @available(iOS 14.0, tvOS 14.0, *)
    ...
    #endif
    

    To test that theory, I've made those modifications locally and added the local repository using Xcode package manager with expected build results. :)

    opened by krissmi 9
  • Live Preview on Device not working

    Live Preview on Device not working

    After I'd added ViewInspector, Live Preview on Device stopped working, it seems:

    linker command failed with exit code 1 (use -v to see invocation)
    
    Build target ViewInspector:
    note: Set ENABLE_PREVIEWS=NO because SWIFT_OPTIMIZATION_LEVEL=-O, expected -Onone (in target 'ViewInspector' from project 'Pods')
    
    
    Link ViewInspector (arm64):
    ld: '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework/XCTest' does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. file '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework/XCTest' for architecture arm64
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    

    The regular Live Preview is working fine though

    opened by NeverwinterMoon 9
  • [Import error] No such module 'ViewInspector'

    [Import error] No such module 'ViewInspector'

    Xcode version: 11.4 Swift version: 5.1 Cocoapods version: 1.9.1

    Hello there!!

    I'm trying add this package into my project to SwiftUI tests targets, and I've found the error below:

    viewinspector-import-error

    I tried use this package with Swift package manager and with Cocoapods, and the library is not found with both package managers. I've tried import this package in two targets: Unit and UI Tests, and the problem is the same for both.

    I've added another library to my Xcode 11 project/workspace and works great!

    Do you know what I'm doing wrong?

    opened by mfdeveloper 9
  • Updating and accessing @State properties

    Updating and accessing @State properties

    I have the following view, in which an optional Text view is toggled in and out of visibility when the button is tapped:

    struct MyView: View {
    
        @State var showErrorMessage = false
    
        var body: some View {
            VStack {
                Button(action: {
                    self.showErrorMessage.toggle()
                    print("showErrorMessage = \(self.showErrorMessage)")
                }) {
                    Text("Click me.")
                }
    
                if showErrorMessage {
                    Text("ERROR")
                        .foregroundColor(Color.red)
                }
            }
        }
    }
    

    I am trying to test the optional view with the following:

    it("should show an error message when the button is pressed") {
            do {
                let view = MyView()
                let button = try view.inspect().vStack().button(0)
    
                try button.tap()
    
                let errorMessage = try view.inspect().vStack().text(1).string()
    
                expect(view.showErrorMessage).to(beTrue())
                expect(errorMessage).to(equal("ERROR"))
            } catch {
                fail("failure - \(error)")
            }
     }
    

    The test fails on both assertions, as the showErrorMessage property remains false and the optional Text view remains hidden. Additionally, showErrorMessage = false is printed to the console when try button.tap() is called.

    When I run the code in the UI simulator, it works as expected; the Text view is toggled in and out of visibility and the correct value of showErrorMessage is printed to the console.

    Is there something I'm missing that's causing the different behavior in the test versus the UI simulator?

    opened by shayna34 9
  • Is there a way to identify a view of any type in a SwiftUI view hierarchy given some constraint?

    Is there a way to identify a view of any type in a SwiftUI view hierarchy given some constraint?

    Hi there.

    So I know there exists a way to inspect the view hierarchy for a particular UI component by specifying the view hierarchy explicitly, such as try view.inspect().vStack().button(0). However, to avoid exposing implementation details of the view hierarchy in my tests, I'd instead like to determine if there exists a UI component within a view hierarchy that matches some constraint.

    For example, say there is a landing screen with a view hierarchy that contains a sign-in button along with other UI components. In my test, I'd like to verify that there exists a button with the text, "Sign In," rather than specifying the view hierarchy explicitly and thereby exposing implementation details.

    Is there any way to do this? Much appreciated.

    opened by SumoSimo 8
  • Can't inspect `accessibilityLabel()`

    Can't inspect `accessibilityLabel()`

    It appears that because the path for accessibilityLabel() uses elements|some, I always get a thrown error when trying to read it. In my inspection of the inspector (sorry!) I have the following modifier:

        ▿ modifier : AccessibilityAttachmentModifier
          ▿ attachment : Optional<AccessibilityAttachment>
            ▿ some : AccessibilityAttachment
              ▿ properties : AccessibilityProperties
                ▿ plist : [TypedValueKey = Optional(SwiftUI.AccessibilityValue(value: nil, description: Optional(SwiftUI.Text(storage: SwiftUI.Text.Storage.verbatim("123456789012"), modifiers: [])))), LabelKey = Optional(SwiftUI.Text(storage: SwiftUI.Text.Storage.verbatim("UPCA product code"), modifiers: [])), TraitsKey = AccessibilityTraitStorage(mask: SwiftUI.AccessibilityTraitStorage.(unknown context at $7fff2c6c4d64).TraitSet(rawValue: 288), values: SwiftUI.AccessibilityTraitStorage.(unknown context at $7fff2c6c4d64).TraitSet(rawValue: 288))]
                  ▿ elements : Optional<Element>
                    ▿ some : TypedValueKey = Optional(SwiftUI.AccessibilityValue(value: nil, description: Optional(SwiftUI.Text(storage: SwiftUI.Text.Storage.verbatim("123456789012"), modifiers: []))))
    

    As you can see, LabelKey is in plist, but does not appear in plist.elements.some. Only my accessibilityValue() is in elements. Since accessibilityValue() is also in plist, I think the appropriate fix is to simply chop off elements|some in the path for accessibilityElement(_::::)

    opened by nrivard 8
  • Problem with find and view with generics

    Problem with find and view with generics

    Before I get to the problem let me explain my motivation. The library I'm writing handles navigation, so the tests for it involve creating an absurd number of views that are unique to a given test. The problem is that find (and, for that matter, some other inspection methods) no longer work due to the regex check on type names.

    Here is a test that reproduces the problem:

    func testViewContainerWithNestedGenericParameter() throws {
        struct ViewWrapper<V: View>: View {
            @ViewBuilder var view: () -> V
            init(@ViewBuilder view: @escaping () -> V) {
                self.view = view
            }
            var body: some View {
                view()
            }
        }
        let sut = try ViewWrapper {
            ViewWrapper {
                Text("findme")
            }
        }.inspect()
        XCTAssertNoThrow(try sut.find(ViewWrapper<ViewWrapper<Text>>.self))
        XCTAssertNoThrow(try sut.find(ViewWrapper<Text>.self))
    }
    

    Here is the commit that caused the problem: https://github.com/nalexn/ViewInspector/commit/22a48cec7ca25851a79b500e91d70893f76ae462

    I've verified in a fork that reverting this commit gets me back to the old, functioning behavior.

    I have another problem with find and generics that likely has a similar cause, but I'm having trouble creating a minimally reproducible example.

    opened by Tyler-Keith-Thompson 5
  • Running tests on iPhone simulator results in crash

    Running tests on iPhone simulator results in crash

    I am developing using Xcode 14.1 on macOS 13.1. My Mac has an M1 processor. I opened the Package.swift and ran the tests (Cmd + u) for the iPhone 14 Pro simulator on the HEAD SHA of the master branch (ac7df67c4e0593470eda90a550f8493a81609745). I encounter a crash in TimelineViewTests.testEnclosedView().

    Screenshot 2022-12-18 at 5 18 12 PM Screenshot 2022-12-18 at 5 18 15 PM
    opened by bachand 0
  • View does not have 'onDisappear' modifier

    View does not have 'onDisappear' modifier

    Hi @nalexn

    I want test code which should call in onDisappear function. But for me isn't possible to call this func from the test.

    I try this here

    try sut.inspect().callOnDisappear()
    XCTAssertEqual(makeSUTResult.cameraSettingsDelegateSpy.storeCameraSettingsCount, 1)
    

    But I get always this error

    failed: caught error: "CameraView does not have 'onDisappear' modifier"

    I added the onDisappear to my view which I'm testing.

    How I can write a test for my use case?

    Thank you

    opened by bitsmakerde 0
  • Not finding Elements when a View is wrapped with NavigationStack

    Not finding Elements when a View is wrapped with NavigationStack

    I have a view which has a NavigationStack around it.

    var body: some View {
    return NavigationStack {
                VStack {
                    Text("Hello")
                    ..........
                }
    

    Now when trying to find the text "Hello" via (I use quick and nimble for testing, but XCTest gives the same result)

                    expect(try homeView.inspect()
                        .find(text: "Hello")
                    ).toNot(beNil())
    

    it gives me a failed test. When removing the NavigationStack the test succeeds. Also when adding a tag to the NavigationStack as .tag property and print the output of

    expect(try homeView.inspect()
                        .find(viewWithTag: "NavigationStack")
    

    (this test also succeeds)

    it gives me the whole hierarchy which clearly shows (SwiftUI.Text(storage: SwiftUI.Text.Storage.anyTextStorage(<LocalizedTextStorage: 0x00006000010656d0>: "Hello") at some point inside of it.

    Am I missing something or is there a bug when traversing through Views wrapped in a NavigationStack via ViewInspector?

    bug 
    opened by domfz 2
  • Question: why do views need to conform to `Inspectable` to be inspected?

    Question: why do views need to conform to `Inspectable` to be inspected?

    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:

    import SwiftUI
    
    struct MyScreen: View {
      var body: some View {
        MyTextWrapperView(title: "Hello world")
      }
    }
    
    struct MyTextWrapperView: View {
      let title: String
    
      var body: some View {
        Text(title)
      }
    }
    

    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:

    import ViewInspector
    import XCTest
    
    final class MyScreenTests: XCTestCase {
      func test_verifyTextPresence() {
        XCTAssertNoThrow(try MyScreen().inspect().find(text: "Hello world"))
      }
    }
    

    I find that for this to to pass I need to conform both MyScreen and MyTextWrapperView to Inspectable.

    extension MyScreen: Inspectable { }
    extension MyTextWrapperView: Inspectable { }
    

    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's find(text:) would work out of the box without any view needing to be conformed to Inspectable. I don't mind needing to conform MyScreen to Inspectable; the part that is more difficult to reason about is the need to conform child view types, like MyTextWrapperView, used within the hierarchy.

    I am trying to understand the reason why the Inspectable conformance is necessary. I see that the Inspectable protocol has one property and one method, and that there's a default implementation for each when Self: View. However I haven't been able to find any documentation as to why the Inspectable protocol is necessary.

    Thanks again for filling this huge gap in the SwiftUI ecosystem which such a thoughtfully built library 😄

    help wanted 
    opened by bachand 6
Releases(0.9.3)
  • 0.9.3(Dec 25, 2022)

    Released on 2022-12-25.

    What's new:

    • Conformance to Inspectable protocol is no longer a requirement when inspecting custom views and modifiers. Kudos to @bachand for R&D of this feature (#216)

    • Fixes and PRs:

      • Added AttributedString support for Text (#202)
      • Fixed "cannot find type 'ToolbarPlacement' in scope" (#199, #206)
    Source code(tar.gz)
    Source code(zip)
  • 0.9.2(Sep 17, 2022)

    Released on 2022-09-17.

    Requires Xcode 14.0

    What's new:

    • Adds support for SwiftUI 4.0

    • New APIs:

      • find(viewWithAccessibilityLabel: ) and find(viewWithAccessibilityIdentifier: ) (PR #168)
      • axes: Axis.Set and showsIndicators: Bool inspectable attributes on ScrollView
      • callOnSubmit inspection of the onSubmit modifier (PR #184)
      • primaryAction inspection support on Menu (#183)
      • Inspectable attributes on Rotation3D (PR #191)
      • Label(_:systemImage:) inspection (#176)
    • Fixes and PRs:

      • 'callOnChange' not recognizing Optional parameters (#163)
      • Error when accessing the keyboardType in TextField (#171)
      • Failure to find .background() when BackgroundView contains Color (#169)
      • Error 'Cannot find 'XCTFail' in scope' when integrating with CocoaPods (#145)
    Source code(tar.gz)
    Source code(zip)
  • 0.9.1(Dec 30, 2021)

    Released on 2021-12-30.

    Requires Xcode 13.2

    What's new:

    • Added inspection support for:

      • AsyncImage
      • Canvas
      • ControlGroup
      • LocationButton
      • SignInWithAppleButton
      • TimelineView
      • VideoPlayer
      • EllipticalGradient (PR #135)
      • ContainerRelativeShape
      • tint() (PR #139)
      • listItemTint() (PR #137)
      • overlayPreferenceValue() (#144)
      • backgroundPreferenceValue()
      • statusBarHidden()
    • New APIs:

      • isAbsent: Bool for checking if optional view is present. Requested in #129 and #136
      • classified() for erasing the type of the view to ViewType.ClassifiedView

    Fixes and PRs:

    • Fixed 'Cannot find 'XCTFail' in scope' when integrating with CocoaPods (#145)
    • Fixed actualView() not injecting EnvironmentObjects (issue #149, PR #150)
    • Fixed find().actualView() gets type mismatch error on a nested view type within a custom generic container (#138)

    Happy coming New Year!

    Source code(tar.gz)
    Source code(zip)
  • 0.9.0(Sep 19, 2021)

    Released on 2021-09-19.

    Requires Xcode 13.0

    What's new:

    • Added watchOS support (#125)
    • Existing inspection code updated for iOS 15 (#128)
    • Added support for Toolbar
    • Added support for safeAreaInset overlay
    • Added support for confirmationDialog
    • Inspector.print(:) is made public for easier view debugging

    Breaking changes:

    Fixes and PRs:

    • navigationBarItems no longer block the inspection starting from iOS 15 (#29)
    • Fixed Inspector.print(:) cyclic recursion (#105)
    • Fixed "callOnChange is not found on _ConditionalContent" (#126)
    • Fixed Shape views being reported as blockers for search (#133)
    • Merged PR #132: Adds support for FullScreenCover
    • Merged PR #124: Include ToolBarItem in readiness.md
    Source code(tar.gz)
    Source code(zip)
  • 0.8.1(Jun 17, 2021)

    Released on 2021-06-17.

    • Fixed compilation issues on Xcode 12.4 (#116)
    • Fixed some tests not passing for iOS 13
    • Merged PR #118: Fixed a link in the guide
    Source code(tar.gz)
    Source code(zip)
  • 0.8.0(Jun 15, 2021)

    Released on 2021-06-15.

    • Inspection of a custom ViewModifier is now fully aligned with a custom View inspection and is devoid of some shortcomings. Your existing tests won't break, but if you choose to migrate to the new one - please refer to the guide for updated snippets for the class Inspection. Kudos to @gili-labs for playing an essential role in the research and development of this feature.

    • A View that has applied custom ViewModifier now inherits view and environment modifiers applied inside that custom ViewModifier. See the example below:

    struct MyModifier: ViewModifier {
        func body(content: Self.Content) -> some View {
            content
               .padding(.top, 15)
        }
    }
    
    let view = Text("Hi").modifier(MyModifier())
    

    Before: Text would report it has no padding modifier applied. Now: Text reports the padding applied from inside the MyModifier.

    • Merged PR #112: Added support for Map view inspection (requested in #59).
    • Added support for MapAnnotation inspection.
    Source code(tar.gz)
    Source code(zip)
  • 0.7.7(May 24, 2021)

  • 0.7.6(May 23, 2021)

    Released on 2021-05-23.

    • Added support for inspecting Alert, ActionSheet and Sheet. Instructions are provided in the guide.

    • New func isResponsive() -> Bool for checking if the control is responsive to the user's touch input.

    • All controls that are disabled, hidden or with allowsHitTesting(false) now throw an exception on attempt to programmatically trigger a user interaction.

    • Many of the view modifiers that impose their effect on the enclosed hierarchy, such as hidden, are now associated with the inner views as well.

    • Merged PR #107: Disabled Button throws an exception on tap attempt.

    • Merged PR #109: Added API for selecting a date with DatePicker

    • Fixed #104

    Source code(tar.gz)
    Source code(zip)
Owner
Alexey Naumov
Designing software the ruthless way
Alexey Naumov
Zeplin component preview for your SwiftUI views

A Zeplin component preview for your SwiftUI views. You can use Zeplin components instead of real views within your app until you implement them.

Danis Tazetdinov 4 Sep 1, 2022
A SwiftUI Views for wrapping HStack elements into multiple lines

SwiftUI WrappingStack A SwiftUI Views for wrapping HStack elements into multiple lines. List of supported views WrappingHStack - provides HStack that

Denis 50 Jan 6, 2023
Create SwiftUI Views with any data

Create SwiftUI Views with any data

Zach Eriksen 20 Jun 27, 2022
Easily use UIKit views in your SwiftUI applications. Create Xcode Previews for UIView elements

SwiftUIKitView Easily use UIKit views in SwiftUI. Convert UIView to SwiftUI View Create Xcode Previews from UIView elements SwiftUI functional updatin

Antoine van der Lee 682 Dec 29, 2022
Placeholder views based on content, loading, error or empty states

StatefulViewController A protocol to enable UIViewControllers or UIViews to present placeholder views based on content, loading, error or empty states

Alexander Schuch 2.1k Dec 8, 2022
Compose views using enums swiftly: `let label: UILabel = [.text("Hello"), .textColor(.red)]`

ViewComposer Style views using an enum array with its attributes: let label: UILabel = [.text("Hello World"), .textColor(.red)] Table of Contents Inst

Alexander Cyon 28 Jul 5, 2022
A paging scroll view for SwiftUI, using internal SwiftUI components

PagingView A paging scroll view for SwiftUI, using internal SwiftUI components. This is basically the same as TabView in the paging mode with the inde

Eric Lewis 18 Dec 25, 2022
SwiftUI-Drawer - A bottom-up drawer in swiftUI

SwiftUI-Drawer A bottom-up drawer view. Contents Installation Examples Installat

Bruno Wide 9 Dec 29, 2022
SwiftUI-Margin adds a margin() viewModifier to a SwiftUI view.

SwiftUI-Margin adds a margin() viewModifier to a SwiftUI view. You will be able to layout the margins in a CSS/Flutter-like.

Masaaki Kakimoto(柿本匡章) 2 Jul 14, 2022
Blobmorphism is a brand new design language I've created to break free of the material overload in iOS, built in SwiftUI. Everything feels smooth and fluid.

Blobmorphism is a brand new design language I've created to break free of the material overload in iOS, built in SwiftUI. Everything feels smooth and fluid.

Ethan Lipnik 89 Nov 29, 2022
SwiftUI view enabling navigation between pages of content, imitating the behaviour of UIPageViewController for iOS and watchOS

PageView SwiftUI view enabling page-based navigation, imitating the behaviour of UIPageViewController in iOS. Why SwiftUI doesn't have any kind of pag

Kacper Rączy 365 Dec 29, 2022
A set of UIKit helpers that simplify the usage of UIKit view's and controller's in SwiftUI.

A set of UIKit helpers that simplify the usage of UIKit view's and controller's in SwiftUI. Many of these helpers are useful even in a pure UIKit project.

SwiftUI+ 6 Oct 28, 2022
The Bloc Pattern is a way to separate UI and Logic in SwiftUI codes;

The Bloc Pattern is a way to separate UI and Logic in SwiftUI codes. The Bloc is like a state machine where it accepts an event and produce a state.

mehdi sohrabi 3 Apr 20, 2022
A liberated _ScrollView and _PagingView of SwiftUI.

SolidScroll A liberated _ScrollView and _PagingView of SwiftUI. Overview SolidScroll allows to unlock the potential of the scroll view in SwiftUI. Unl

Eugene Dudnyk 30 Dec 29, 2022
A few drop-in SwiftUI components for easily importing and thumb-nailing files

FilesUI A few drop-in SwiftUI components for easily importing and thumb-nailing files Usage 1. Import Files To import files you can use the FileImport

Brianna Zamora 3 Oct 19, 2022
SwiftUI components and extensions that seem to be highly reusable

SwiftUI components and extensions that seem to be highly reusable

Yusuke Hosonuma 56 Dec 15, 2022
A number of preset loading indicators created with SwiftUI

ActivityIndicatorView A number of preset loading indicators created with SwiftUI We are a development agency building phenomenal apps. Usage Create an

Exyte 956 Dec 26, 2022
A better way to present a SFSafariViewController or start a ASWebAuthenticationSession in SwiftUI.

BetterSafariView A better way to present a SFSafariViewController or start a ASWebAuthenticationSession in SwiftUI. Contents Motivation Requirements U

Dongkyu Kim 392 Dec 31, 2022
A SwiftUI Library for creating resizable partitions for View Content.

Partition Kit Recently Featured In Top 10 Trending Android and iOS Libraries in October and in 5 iOS libraries to enhance your app! What is PartitionK

Kieran Brown 230 Oct 27, 2022