From a2b9069cb3738261a2f024a16d1de809b0eb0b01 Mon Sep 17 00:00:00 2001 From: ryohey Date: Mon, 1 May 2023 18:35:19 +0900 Subject: [PATCH 01/18] Update README to include UICollectionView introspection support (#218) --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7e710da..c02e48d 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,10 @@ NavigationView (DoubleColumnNavigationViewStyle) | UISplitViewController | _N/A_ NavigationView (DoubleColumnNavigationViewStyle) | _N/A_ | NSSplitView | `.introspectSplitView()` _Any embedded view_ | UIViewController | _N/A_ | `.introspectViewController()` ScrollView | UIScrollView | NSScrollView | `.introspectScrollView()` -List | UITableView | NSTableView | `.introspectTableView()` -View in List | UITableViewCell | NSTableCellView | `introspectTableViewCell()` +List (iOS15 and below) | UITableView | NSTableView | `.introspectTableView()` +View in List (iOS15 and below) | UITableViewCell | NSTableCellView | `introspectTableViewCell()` +List (iOS 16) | UICollectionView | _N/A_ | `.introspectCollectionView()` +View in List (iOS 16) | UICollectionViewCell | _N/A_ | `.introspectCollectionViewCell()` TabView | UITabBarController | NSTabView | `.introspectTabBarController()` (iOS)
`.introspectTabView()` (macOS) TextField | UITextField | NSTextField | `.introspectTextField()` Toggle | UISwitch | NSButton | `.introspectSwitch()` (iOS)
`.introspectButton()` (macOS) From bad095e578fece765e8c40cc63f68eabe8c3904e Mon Sep 17 00:00:00 2001 From: Pascal Burlet <59558722+paescebu@users.noreply.github.com> Date: Fri, 12 May 2023 18:30:26 +0200 Subject: [PATCH 02/18] Fix finding UIScrollViews that are clipped(), masked or combined with clipShape() or cornerRadius() (#213) Co-authored-by: Pascal Burlet Co-authored-by: David Roman <2538074+davdroman@users.noreply.github.com> --- Introspect/Introspect.swift | 10 ++++++ Introspect/ViewExtensions.swift | 4 +-- IntrospectTests/AppKitTests.swift | 54 +++++++++++++++++++++++++++++- IntrospectTests/UIKitTests.swift | 55 +++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+), 3 deletions(-) diff --git a/Introspect/Introspect.swift b/Introspect/Introspect.swift index cbc7562..09e8b36 100644 --- a/Introspect/Introspect.swift +++ b/Introspect/Introspect.swift @@ -318,6 +318,16 @@ public enum TargetViewSelector { } return Introspect.findAncestor(ofType: TargetView.self, from: entry) } + + public static func siblingOrAncestorOrSiblingContainingOrAncestorChild(from entry: PlatformView) -> TargetView? { + if let sibling: TargetView = siblingOfType(from: entry) { + return sibling + } + if let ancestor: TargetView = Introspect.findAncestor(ofType: TargetView.self, from: entry) { + return ancestor + } + return siblingContainingOrAncestorOrAncestorChild(from: entry) + } public static func ancestorOrSiblingContaining(from entry: PlatformView) -> TargetView? { if let tableView = Introspect.findAncestor(ofType: TargetView.self, from: entry) { diff --git a/Introspect/ViewExtensions.swift b/Introspect/ViewExtensions.swift index 56f2fcc..b339340 100644 --- a/Introspect/ViewExtensions.swift +++ b/Introspect/ViewExtensions.swift @@ -120,7 +120,7 @@ extension View { /// Finds a `UIScrollView` from a `SwiftUI.ScrollView`, or `SwiftUI.ScrollView` child. public func introspectScrollView(customize: @escaping (UIScrollView) -> ()) -> some View { if #available(iOS 14, tvOS 14, *) { - return introspect(selector: TargetViewSelector.siblingOfTypeOrAncestor, customize: customize) + return introspect(selector: TargetViewSelector.siblingOrAncestorOrSiblingContainingOrAncestorChild, customize: customize) } else { return introspect(selector: TargetViewSelector.siblingContainingOrAncestor, customize: customize) } @@ -227,7 +227,7 @@ extension View { /// Finds a `NSScrollView` from a `SwiftUI.ScrollView`, or `SwiftUI.ScrollView` child. public func introspectScrollView(customize: @escaping (NSScrollView) -> ()) -> some View { if #available(macOS 11, *) { - return introspect(selector: TargetViewSelector.siblingOfTypeOrAncestor, customize: customize) + return introspect(selector: TargetViewSelector.siblingOrAncestorOrSiblingContainingOrAncestorChild, customize: customize) } else { return introspect(selector: TargetViewSelector.siblingContainingOrAncestor, customize: customize) } diff --git a/IntrospectTests/AppKitTests.swift b/IntrospectTests/AppKitTests.swift index dc0de13..5a14e78 100644 --- a/IntrospectTests/AppKitTests.swift +++ b/IntrospectTests/AppKitTests.swift @@ -113,6 +113,32 @@ private struct NestedScrollTestView: View { } } +private struct MaskedScrollTestView: View { + + let spy1: (NSScrollView) -> Void + let spy2: (NSScrollView) -> Void + + var body: some View { + HStack { + ScrollView { + Text("Item 1") + } + .introspectScrollView { scrollView in + self.spy1(scrollView) + } + .clipped() + .clipShape(RoundedRectangle(cornerRadius: 20.0)) + .cornerRadius(2.0) + ScrollView { + Text("Item 1") + .introspectScrollView { scrollView in + self.spy2(scrollView) + } + } + } + } +} + private struct TextFieldTestView: View { let spy: () -> Void @State private var textFieldValue = "" @@ -314,7 +340,6 @@ class AppKitTests: XCTestCase { } func testNestedScrollView() throws { - let expectation1 = XCTestExpectation() let expectation2 = XCTestExpectation() @@ -339,6 +364,33 @@ class AppKitTests: XCTestCase { XCTAssertNotEqual(unwrappedScrollView1, unwrappedScrollView2) } + + func testMaskedScrollView() throws { + let expectation1 = XCTestExpectation() + let expectation2 = XCTestExpectation() + + var scrollView1: NSScrollView? + var scrollView2: NSScrollView? + + let view = MaskedScrollTestView( + spy1: { scrollView in + scrollView1 = scrollView + expectation1.fulfill() + }, + spy2: { scrollView in + scrollView2 = scrollView + expectation2.fulfill() + } + ) + + TestUtils.present(view: view) + wait(for: [expectation1, expectation2], timeout: TestUtils.Constants.timeout) + + let unwrappedScrollView1 = try XCTUnwrap(scrollView1) + let unwrappedScrollView2 = try XCTUnwrap(scrollView2) + + XCTAssertNotEqual(unwrappedScrollView1, unwrappedScrollView2) + } func testTextField() { diff --git a/IntrospectTests/UIKitTests.swift b/IntrospectTests/UIKitTests.swift index 22a7417..64ecdf0 100644 --- a/IntrospectTests/UIKitTests.swift +++ b/IntrospectTests/UIKitTests.swift @@ -183,6 +183,7 @@ private struct ListTestView: View { } } + private struct ScrollTestView: View { let spy1: (UIScrollView) -> Void @@ -230,6 +231,32 @@ private struct NestedScrollTestView: View { } } +private struct MaskedScrollTestView: View { + + let spy1: (UIScrollView) -> Void + let spy2: (UIScrollView) -> Void + + var body: some View { + HStack { + ScrollView { + Text("Item 1") + } + .introspectScrollView { scrollView in + self.spy1(scrollView) + } + .clipped() + .clipShape(RoundedRectangle(cornerRadius: 20.0)) + .cornerRadius(2.0) + ScrollView { + Text("Item 1") + .introspectScrollView { scrollView in + self.spy2(scrollView) + } + } + } + } +} + private struct TextFieldTestView: View { let spy1: (UITextField) -> Void let spy2: (UITextField) -> Void @@ -482,6 +509,34 @@ class UIKitTests: XCTestCase { XCTAssertNotEqual(unwrappedScrollView1, unwrappedScrollView2) } + + func testMaskedScrollView() throws { + + let expectation1 = XCTestExpectation() + let expectation2 = XCTestExpectation() + + var scrollView1: UIScrollView? + var scrollView2: UIScrollView? + + let view = MaskedScrollTestView( + spy1: { scrollView in + scrollView1 = scrollView + expectation1.fulfill() + }, + spy2: { scrollView in + scrollView2 = scrollView + expectation2.fulfill() + } + ) + + TestUtils.present(view: view) + wait(for: [expectation1, expectation2], timeout: TestUtils.Constants.timeout) + + let unwrappedScrollView1 = try XCTUnwrap(scrollView1) + let unwrappedScrollView2 = try XCTUnwrap(scrollView2) + + XCTAssertNotEqual(unwrappedScrollView1, unwrappedScrollView2) + } func testTextField() throws { From b9f6bc32772d61cc76ec4b206df0840a3b7c96f4 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 12 May 2023 17:50:17 +0100 Subject: [PATCH 03/18] Bump to 0.3.0 (#219) --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d1eacc..0a7025e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,12 @@ Changelog ## master +## [0.3.0] + - Changed: minimum language version required is now Swift 5.5 (#209) -- Infrastructure: symlink older SDKs to use in newer Xcode versions (#208) +- Fixed: finding UIScrollViews that are clipped(), masked or combined with clipShape() or cornerRadius() (#213) +- Documentation: UICollectionView introspection support in README (#218) +- Infrastructure: symlink older SDKs to use in newer Xcode versions on CI (#208) ## [0.2.3] From 05e13467a00aaac4a6290f16a6cb0fe25ba8e50b Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 12 May 2023 18:59:11 +0100 Subject: [PATCH 04/18] Fix wrong Swift version in podspec (#220) --- Introspect.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Introspect.podspec b/Introspect.podspec index 5344f47..ba45cbe 100644 --- a/Introspect.podspec +++ b/Introspect.podspec @@ -12,7 +12,7 @@ Pod::Spec.new do |spec| spec.source_files = 'Introspect/*.swift' - spec.swift_version = '5.4' + spec.swift_version = '5.5' spec.ios.deployment_target = '13.0' spec.tvos.deployment_target = '13.0' spec.osx.deployment_target = '10.15' From 5b3f3996c7a2a84d5f4ba0e03cd7d584154778f2 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 12 May 2023 19:10:15 +0100 Subject: [PATCH 05/18] Bump to 0.3.1 (#221) --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a7025e..6bcf7f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ Changelog ## master +## [0.3.1] + +- Fixed: wrong Swift version in podspec (#220) + ## [0.3.0] - Changed: minimum language version required is now Swift 5.5 (#209) From 0955cfcec93754971fba28904b871a0ea3f06163 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Thu, 1 Jun 2023 19:40:17 +0100 Subject: [PATCH 06/18] All-new implementation, API, and module (#207) --- .github/workflows/cd.yml | 12 +- .github/workflows/ci.yml | 112 ++- .../Showcase.xcodeproj/project.pbxproj | 56 +- .../xcshareddata/xcschemes/Showcase.xcscheme | 33 +- Examples/Showcase/Showcase/App.swift | 15 + .../AccentColor.colorset/Contents.json | 11 - .../AppIcon.appiconset/Contents.json | 13 - .../Showcase/Assets.xcassets/Contents.json | 6 - Examples/Showcase/Showcase/ContentView.swift | 307 +++++-- Examples/Showcase/Showcase/Helpers.swift | 3 +- .../Preview Assets.xcassets/Contents.json | 6 - .../contents.xcworkspacedata | 3 + ...ic.xcscheme => SwiftUIIntrospect.xcscheme} | 12 +- Package.swift | 4 +- Package@swift-5.7.swift | 39 + README.md | 8 + Sources/Introspect.swift | 201 +++++ Sources/IntrospectableViewType.swift | 7 + Sources/IntrospectionView.swift | 170 ++++ Sources/PlatformVersion.swift | 165 ++++ Sources/PlatformView.swift | 55 ++ Sources/PlatformViewVersion.swift | 48 ++ Sources/RuntimeWarnings.swift | 83 ++ Sources/ViewTypes/Button.swift | 20 + Sources/ViewTypes/ColorPicker.swift | 31 + Sources/ViewTypes/DatePicker.swift | 27 + .../DatePickerWithCompactStyle.swift | 34 + .../ViewTypes/DatePickerWithFieldStyle.swift | 24 + .../DatePickerWithGraphicalStyleType.swift | 32 + .../DatePickerWithStepperFieldStyle.swift | 24 + .../ViewTypes/DatePickerWithWheelStyle.swift | 24 + Sources/ViewTypes/Form.swift | 30 + Sources/ViewTypes/FormWithGroupedStyle.swift | 48 ++ Sources/ViewTypes/List.swift | 40 + Sources/ViewTypes/ListCell.swift | 37 + Sources/ViewTypes/ListWithBorderedStyle.swift | 26 + Sources/ViewTypes/ListWithGroupedStyle.swift | 34 + .../ViewTypes/ListWithInsetGroupedStyle.swift | 28 + Sources/ViewTypes/ListWithInsetStyle.swift | 36 + Sources/ViewTypes/ListWithSidebarStyle.swift | 35 + Sources/ViewTypes/NavigationSplitView.swift | 44 + Sources/ViewTypes/NavigationStack.swift | 33 + .../NavigationViewWithColumnsStyle.swift | 36 + .../NavigationViewWithStackStyle.swift | 29 + Sources/ViewTypes/PickerWithMenuStyle.swift | 25 + .../ViewTypes/PickerWithSegmentedStyle.swift | 36 + Sources/ViewTypes/PickerWithWheelStyle.swift | 24 + .../ProgressViewWithCircularStyle.swift | 39 + .../ProgressViewWithLinearStyle.swift | 39 + Sources/ViewTypes/ScrollView.swift | 32 + Sources/ViewTypes/SearchField.swift | 29 + Sources/ViewTypes/Slider.swift | 27 + Sources/ViewTypes/Stepper.swift | 27 + Sources/ViewTypes/TabView.swift | 32 + Sources/ViewTypes/TabViewWithPageStyle.swift | 33 + Sources/ViewTypes/Table.swift | 32 + Sources/ViewTypes/TextEditor.swift | 29 + Sources/ViewTypes/TextField.swift | 32 + .../ViewTypes/TextFieldWithVerticalAxis.swift | 50 ++ Sources/ViewTypes/Toggle.swift | 27 + Sources/ViewTypes/ToggleWithButtonStyle.swift | 26 + .../ViewTypes/ToggleWithCheckboxStyle.swift | 24 + Sources/ViewTypes/ToggleWithSwitchStyle.swift | 31 + Sources/ViewTypes/View.swift | 27 + SwiftUIIntrospect.podspec | 19 + Tests/Package.swift | 9 + Tests/Tests.xcodeproj/project.pbxproj | 774 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcschemes/SwiftUIIntrospectTests.xcscheme | 88 ++ Tests/Tests/PlatformTests.swift | 97 +++ Tests/Tests/TestUtils.swift | 130 +++ Tests/Tests/ViewTypes/ButtonTests.swift | 49 ++ Tests/Tests/ViewTypes/ColorPickerTests.swift | 61 ++ Tests/Tests/ViewTypes/DatePickerTests.swift | 60 ++ ...DatePickerWithCompactFieldStyleTests.swift | 68 ++ .../DatePickerWithFieldStyleTests.swift | 51 ++ .../DatePickerWithGraphicalStyleTests.swift | 68 ++ ...DatePickerWithStepperFieldStyleTests.swift | 51 ++ .../DatePickerWithWheelStyleTests.swift | 51 ++ Tests/Tests/ViewTypes/FormTests.swift | 40 + .../ViewTypes/FormWithGroupedStyleTests.swift | 49 ++ Tests/Tests/ViewTypes/ListCellTests.swift | 46 ++ Tests/Tests/ViewTypes/ListTests.swift | 103 +++ .../ListWithBorderedStyleTests.swift | 43 + .../ViewTypes/ListWithGroupedStyleTests.swift | 40 + .../ListWithInsetGroupedStyleTests.swift | 45 + .../ViewTypes/ListWithInsetStyleTests.swift | 51 ++ .../ViewTypes/ListWithPlainStyleTests.swift | 44 + .../ViewTypes/ListWithSidebarStyleTests.swift | 51 ++ .../ViewTypes/NavigationSplitViewTests.swift | 71 ++ .../ViewTypes/NavigationStackTests.swift | 52 ++ .../NavigationViewWithColumnsStyleTests.swift | 55 ++ .../NavigationViewWithStackStyleTests.swift | 45 + .../ViewTypes/PickerWithMenuStyleTests.swift | 56 ++ .../PickerWithSegmentedStyleTests.swift | 66 ++ .../ViewTypes/PickerWithWheelStyleTests.swift | 56 ++ .../ProgressViewWithCircularStyleTests.swift | 55 ++ .../ProgressViewWithLinearStyleTests.swift | 59 ++ Tests/Tests/ViewTypes/ScrollViewTests.swift | 132 +++ Tests/Tests/ViewTypes/SearchFieldTests.swift | 31 + Tests/Tests/ViewTypes/SliderTests.swift | 56 ++ Tests/Tests/ViewTypes/StepperTests.swift | 48 ++ Tests/Tests/ViewTypes/TabViewTests.swift | 49 ++ .../ViewTypes/TabViewWithPageStyleTests.swift | 52 ++ Tests/Tests/ViewTypes/TableTests.swift | 152 ++++ Tests/Tests/ViewTypes/TextEditorTests.swift | 61 ++ Tests/Tests/ViewTypes/TextFieldTests.swift | 95 +++ .../TextFieldWithVerticalAxisTests.swift | 69 ++ Tests/Tests/ViewTypes/ToggleTests.swift | 54 ++ .../ToggleWithButtonStyleTests.swift | 50 ++ .../ToggleWithCheckboxStyleTests.swift | 45 + .../ToggleWithSwitchStyleTests.swift | 57 ++ Tests/Tests/ViewTypes/ViewTests.swift | 32 + Tests/TestsHostApp/TestsHostApp.swift | 40 + docs/SwiftUIIntrospect.md | 201 +++++ fastlane/Fastfile | 139 ++-- 117 files changed, 6366 insertions(+), 277 deletions(-) rename Introspect.xcworkspace/xcshareddata/xcschemes/Introspect-Dynamic.xcscheme => Examples/Showcase/Showcase.xcodeproj/xcshareddata/xcschemes/Showcase.xcscheme (65%) delete mode 100644 Examples/Showcase/Showcase/Assets.xcassets/AccentColor.colorset/Contents.json delete mode 100644 Examples/Showcase/Showcase/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 Examples/Showcase/Showcase/Assets.xcassets/Contents.json delete mode 100644 Examples/Showcase/Showcase/Preview Content/Preview Assets.xcassets/Contents.json rename Introspect.xcworkspace/xcshareddata/xcschemes/{Introspect-Static.xcscheme => SwiftUIIntrospect.xcscheme} (86%) create mode 100644 Package@swift-5.7.swift create mode 100644 Sources/Introspect.swift create mode 100644 Sources/IntrospectableViewType.swift create mode 100644 Sources/IntrospectionView.swift create mode 100644 Sources/PlatformVersion.swift create mode 100644 Sources/PlatformView.swift create mode 100644 Sources/PlatformViewVersion.swift create mode 100644 Sources/RuntimeWarnings.swift create mode 100644 Sources/ViewTypes/Button.swift create mode 100644 Sources/ViewTypes/ColorPicker.swift create mode 100644 Sources/ViewTypes/DatePicker.swift create mode 100644 Sources/ViewTypes/DatePickerWithCompactStyle.swift create mode 100644 Sources/ViewTypes/DatePickerWithFieldStyle.swift create mode 100644 Sources/ViewTypes/DatePickerWithGraphicalStyleType.swift create mode 100644 Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift create mode 100644 Sources/ViewTypes/DatePickerWithWheelStyle.swift create mode 100644 Sources/ViewTypes/Form.swift create mode 100644 Sources/ViewTypes/FormWithGroupedStyle.swift create mode 100644 Sources/ViewTypes/List.swift create mode 100644 Sources/ViewTypes/ListCell.swift create mode 100644 Sources/ViewTypes/ListWithBorderedStyle.swift create mode 100644 Sources/ViewTypes/ListWithGroupedStyle.swift create mode 100644 Sources/ViewTypes/ListWithInsetGroupedStyle.swift create mode 100644 Sources/ViewTypes/ListWithInsetStyle.swift create mode 100644 Sources/ViewTypes/ListWithSidebarStyle.swift create mode 100644 Sources/ViewTypes/NavigationSplitView.swift create mode 100644 Sources/ViewTypes/NavigationStack.swift create mode 100644 Sources/ViewTypes/NavigationViewWithColumnsStyle.swift create mode 100644 Sources/ViewTypes/NavigationViewWithStackStyle.swift create mode 100644 Sources/ViewTypes/PickerWithMenuStyle.swift create mode 100644 Sources/ViewTypes/PickerWithSegmentedStyle.swift create mode 100644 Sources/ViewTypes/PickerWithWheelStyle.swift create mode 100644 Sources/ViewTypes/ProgressViewWithCircularStyle.swift create mode 100644 Sources/ViewTypes/ProgressViewWithLinearStyle.swift create mode 100644 Sources/ViewTypes/ScrollView.swift create mode 100644 Sources/ViewTypes/SearchField.swift create mode 100644 Sources/ViewTypes/Slider.swift create mode 100644 Sources/ViewTypes/Stepper.swift create mode 100644 Sources/ViewTypes/TabView.swift create mode 100644 Sources/ViewTypes/TabViewWithPageStyle.swift create mode 100644 Sources/ViewTypes/Table.swift create mode 100644 Sources/ViewTypes/TextEditor.swift create mode 100644 Sources/ViewTypes/TextField.swift create mode 100644 Sources/ViewTypes/TextFieldWithVerticalAxis.swift create mode 100644 Sources/ViewTypes/Toggle.swift create mode 100644 Sources/ViewTypes/ToggleWithButtonStyle.swift create mode 100644 Sources/ViewTypes/ToggleWithCheckboxStyle.swift create mode 100644 Sources/ViewTypes/ToggleWithSwitchStyle.swift create mode 100644 Sources/ViewTypes/View.swift create mode 100644 SwiftUIIntrospect.podspec create mode 100644 Tests/Package.swift create mode 100644 Tests/Tests.xcodeproj/project.pbxproj create mode 100644 Tests/Tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Tests/Tests.xcodeproj/xcshareddata/xcschemes/SwiftUIIntrospectTests.xcscheme create mode 100644 Tests/Tests/PlatformTests.swift create mode 100644 Tests/Tests/TestUtils.swift create mode 100644 Tests/Tests/ViewTypes/ButtonTests.swift create mode 100644 Tests/Tests/ViewTypes/ColorPickerTests.swift create mode 100644 Tests/Tests/ViewTypes/DatePickerTests.swift create mode 100644 Tests/Tests/ViewTypes/DatePickerWithCompactFieldStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/DatePickerWithFieldStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/DatePickerWithGraphicalStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/DatePickerWithStepperFieldStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/DatePickerWithWheelStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/FormTests.swift create mode 100644 Tests/Tests/ViewTypes/FormWithGroupedStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/ListCellTests.swift create mode 100644 Tests/Tests/ViewTypes/ListTests.swift create mode 100644 Tests/Tests/ViewTypes/ListWithBorderedStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/ListWithGroupedStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/ListWithInsetGroupedStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/ListWithInsetStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/ListWithPlainStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/ListWithSidebarStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/NavigationSplitViewTests.swift create mode 100644 Tests/Tests/ViewTypes/NavigationStackTests.swift create mode 100644 Tests/Tests/ViewTypes/NavigationViewWithColumnsStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/NavigationViewWithStackStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/PickerWithMenuStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/PickerWithSegmentedStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/PickerWithWheelStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/ProgressViewWithCircularStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/ProgressViewWithLinearStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/ScrollViewTests.swift create mode 100644 Tests/Tests/ViewTypes/SearchFieldTests.swift create mode 100644 Tests/Tests/ViewTypes/SliderTests.swift create mode 100644 Tests/Tests/ViewTypes/StepperTests.swift create mode 100644 Tests/Tests/ViewTypes/TabViewTests.swift create mode 100644 Tests/Tests/ViewTypes/TabViewWithPageStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/TableTests.swift create mode 100644 Tests/Tests/ViewTypes/TextEditorTests.swift create mode 100644 Tests/Tests/ViewTypes/TextFieldTests.swift create mode 100644 Tests/Tests/ViewTypes/TextFieldWithVerticalAxisTests.swift create mode 100644 Tests/Tests/ViewTypes/ToggleTests.swift create mode 100644 Tests/Tests/ViewTypes/ToggleWithButtonStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/ToggleWithCheckboxStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/ToggleWithSwitchStyleTests.swift create mode 100644 Tests/Tests/ViewTypes/ViewTests.swift create mode 100644 Tests/TestsHostApp/TestsHostApp.swift create mode 100644 docs/SwiftUIIntrospect.md diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 293791c..9bd0fef 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -8,17 +8,23 @@ on: jobs: deploy: runs-on: macos-latest + strategy: + fail-fast: false + matrix: + podspec: + - Introspect.podspec + - SwiftUIIntrospect.podspec steps: - name: Git Checkout uses: actions/checkout@v3 with: fetch-depth: 0 # required to be able to find Git tags - - name: Deploy to CocoaPods Trunk + - name: Deploy to CocoaPods Trunk (${{ matrix.podspec }}) run: | set -eo pipefail export LIB_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) - pod lib lint --allow-warnings - pod trunk push --allow-warnings + pod lib lint ${{ matrix.podspec }} --allow-warnings + pod trunk push ${{ matrix.podspec }} --allow-warnings env: COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fb6c3a3..77050d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,8 +13,8 @@ concurrency: cancel-in-progress: true jobs: - lint-podspec: - name: lint podspec + lint-podspecs: + name: lint podspecs runs-on: macos-latest steps: - name: Git Checkout @@ -22,61 +22,77 @@ jobs: with: fetch-depth: 0 # required to be able to find Git tags - - name: Lint Podspec + - name: Lint Introspect.podspec run: | set -eo pipefail export LIB_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) - pod lib lint --allow-warnings + pod lib lint Introspect.podspec --allow-warnings + + - name: Lint SwiftUIIntrospect.podspec + run: | + set -eo pipefail + export LIB_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) + pod lib lint SwiftUIIntrospect.podspec --allow-warnings ci: name: ${{ matrix.platform[0] }} ${{ matrix.platform[1] }} - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.os || 'macos-13' }} strategy: fail-fast: false matrix: platform: + - [ios, 13] - [ios, 14] - [ios, 15] - [ios, 16] + - [tvos, 13] - [tvos, 14] - [tvos, 15] - [tvos, 16] - [macos, 11] - [macos, 12] + - [macos, 13] include: + - platform: [ios, 13] + runtime: iOS 13.7 + install: true - platform: [ios, 14] - os: macos-11 - xcode_version: 13.2.1 - sdk: [12.5.1, iPhoneOS, iOS, 14.5] - - platform: [tvos, 14] - os: macos-11 - xcode_version: 13.2.1 - sdk: [12.5.1, AppleTVOS, tvOS, 14.5] - + runtime: iOS 14.5 + install: true - platform: [ios, 15] - os: macos-12 - xcode_version: 14.2 - sdk: [13.4.1, iPhoneOS, iOS, 15.5] - - platform: [tvos, 15] - os: macos-12 - xcode_version: 14.2 - sdk: [13.4.1, AppleTVOS, tvOS, 15.5] - + runtime: iOS 15.5 + install: true - platform: [ios, 16] - os: macos-12 - xcode_version: 14.2 + runtime: iOS 16.4 + install: false + + - platform: [tvos, 13] + runtime: tvOS 13.4 + install: true + - platform: [tvos, 14] + runtime: tvOS 14.5 + install: true + - platform: [tvos, 15] + runtime: tvOS 15.4 + install: true - platform: [tvos, 16] - os: macos-12 - xcode_version: 14.2 + runtime: tvOS 16.4 + install: false - platform: [macos, 11] os: macos-11 - xcode_version: 13.2.1 + xcode: 13.2.1 + install: false - platform: [macos, 12] os: macos-12 - xcode_version: 14.2 + xcode: 14.2 + install: false + - platform: [macos, 13] + os: macos-13 + xcode: 14.3 + install: false steps: - name: Git Checkout uses: actions/checkout@v3 @@ -84,22 +100,32 @@ jobs: - name: Select Xcode version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: ${{ matrix.xcode_version }} + xcode-version: ${{ matrix.xcode || '14.3' }} - - if: ${{ matrix.sdk }} - name: Symlink SDK - run: | - echo "Creating Runtimes folder if needed..." - sudo mkdir -p /Library/Developer/CoreSimulator/Profiles/Runtimes - - echo "Creating symlink of the ${{ matrix.sdk[2] }} ${{ matrix.sdk[3] }} runtime..." - sudo ln -s /Applications/Xcode_${{ matrix.sdk[0] }}.app/Contents/Developer/Platforms/${{ matrix.sdk[1] }}.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/${{ matrix.sdk[2] }}.simruntime /Library/Developer/CoreSimulator/Profiles/Runtimes/${{ matrix.sdk[2] }}\ ${{ matrix.sdk[3] }}.simruntime - - - name: Install Homebrew dependencies + - name: Install xcbeautify run: brew install xcbeautify - - name: Run Tests - run: fastlane test platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }} - env: - SKIP_SLOW_FASTLANE_WARNINGS: 1 - FASTLANE_SKIP_UPDATE_CHECK: 1 + - if: ${{ matrix.install }} + name: Install Required Runtime + run: | + brew install xcodesorg/made/xcodes + sudo xcodes runtimes install '${{ matrix.runtime }}' + + - name: List Available Simulators + run: xcrun simctl list devices available + + - if: ${{ join(matrix.platform, ' ') != 'macos 11' }} + name: Build Showcase + run: fastlane build platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }} scheme:Showcase + + - if: ${{ join(matrix.platform, ' ') != 'ios 13' && join(matrix.platform, ' ') != 'tvos 13' }} + name: Run Tests (Introspect) + run: fastlane test platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }} scheme:Introspect + + - if: ${{ join(matrix.platform, ' ') != 'macos 11' }} + name: Run Tests (SwiftUIIntrospect, Debug) + run: fastlane test platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }} scheme:SwiftUIIntrospectTests configuration:Debug + + - if: ${{ join(matrix.platform, ' ') != 'macos 11' }} + name: Run Tests (SwiftUIIntrospect, Release) + run: fastlane test platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }} scheme:SwiftUIIntrospectTests configuration:Release diff --git a/Examples/Showcase/Showcase.xcodeproj/project.pbxproj b/Examples/Showcase/Showcase.xcodeproj/project.pbxproj index 21c20d3..08f01b9 100644 --- a/Examples/Showcase/Showcase.xcodeproj/project.pbxproj +++ b/Examples/Showcase/Showcase.xcodeproj/project.pbxproj @@ -3,24 +3,20 @@ archiveVersion = 1; classes = { }; - objectVersion = 56; + objectVersion = 55; objects = { /* Begin PBXBuildFile section */ D53071F729983CEF00F1936C /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53071F629983CEF00F1936C /* App.swift */; }; D53071F929983CEF00F1936C /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53071F829983CEF00F1936C /* ContentView.swift */; }; - D53071FB29983CF000F1936C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D53071FA29983CF000F1936C /* Assets.xcassets */; }; - D53071FE29983CF000F1936C /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D53071FD29983CF000F1936C /* Preview Assets.xcassets */; }; - D530720729983DCA00F1936C /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = D530720629983DCA00F1936C /* Introspect */; }; D5B829752999738200920EBD /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B829742999738200920EBD /* Helpers.swift */; }; + D5E3180329C132B6005847DC /* SwiftUIIntrospect in Frameworks */ = {isa = PBXBuildFile; productRef = D5E3180229C132B6005847DC /* SwiftUIIntrospect */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ D53071F329983CEF00F1936C /* Showcase.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Showcase.app; sourceTree = BUILT_PRODUCTS_DIR; }; D53071F629983CEF00F1936C /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; D53071F829983CEF00F1936C /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; - D53071FA29983CF000F1936C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - D53071FD29983CF000F1936C /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; D530720429983D9300F1936C /* Showcase.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Showcase.entitlements; sourceTree = ""; }; D5B829742999738200920EBD /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -30,7 +26,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D530720729983DCA00F1936C /* Introspect in Frameworks */, + D5E3180329C132B6005847DC /* SwiftUIIntrospect in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -61,20 +57,10 @@ D53071F629983CEF00F1936C /* App.swift */, D53071F829983CEF00F1936C /* ContentView.swift */, D5B829742999738200920EBD /* Helpers.swift */, - D53071FA29983CF000F1936C /* Assets.xcassets */, - D53071FC29983CF000F1936C /* Preview Content */, ); path = Showcase; sourceTree = ""; }; - D53071FC29983CF000F1936C /* Preview Content */ = { - isa = PBXGroup; - children = ( - D53071FD29983CF000F1936C /* Preview Assets.xcassets */, - ); - path = "Preview Content"; - sourceTree = ""; - }; D530720529983DCA00F1936C /* Frameworks */ = { isa = PBXGroup; children = ( @@ -99,7 +85,7 @@ ); name = Showcase; packageProductDependencies = ( - D530720629983DCA00F1936C /* Introspect */, + D5E3180229C132B6005847DC /* SwiftUIIntrospect */, ); productName = Showcase; productReference = D53071F329983CEF00F1936C /* Showcase.app */; @@ -121,7 +107,7 @@ }; }; buildConfigurationList = D53071EE29983CEF00F1936C /* Build configuration list for PBXProject "Showcase" */; - compatibilityVersion = "Xcode 14.0"; + compatibilityVersion = "Xcode 13.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -143,8 +129,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - D53071FE29983CF000F1936C /* Preview Assets.xcassets in Resources */, - D53071FB29983CF000F1936C /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -167,6 +151,7 @@ D53071FF29983CF000F1936C /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -214,20 +199,24 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos"; + SUPPORTS_MACCATALYST = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - TVOS_DEPLOYMENT_TARGET = 14.0; + TVOS_DEPLOYMENT_TARGET = 13.0; }; name = Debug; }; D530720029983CF000F1936C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -269,13 +258,16 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MACOSX_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos"; + SUPPORTS_MACCATALYST = YES; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; - TVOS_DEPLOYMENT_TARGET = 14.0; + TVOS_DEPLOYMENT_TARGET = 13.0; VALIDATE_PRODUCT = YES; }; name = Release; @@ -283,18 +275,16 @@ D530720229983CF000F1936C /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Showcase/Showcase.entitlements; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = "\"Showcase/Preview Content\""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UILaunchStoryboardName = ""; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; LD_RUNPATH_SEARCH_PATHS = ( @@ -304,7 +294,6 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Showcase; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; @@ -316,18 +305,16 @@ D530720329983CF000F1936C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Showcase/Showcase.entitlements; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = "\"Showcase/Preview Content\""; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UILaunchStoryboardName = ""; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; LD_RUNPATH_SEARCH_PATHS = ( @@ -337,7 +324,6 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Showcase; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; @@ -370,9 +356,9 @@ /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ - D530720629983DCA00F1936C /* Introspect */ = { + D5E3180229C132B6005847DC /* SwiftUIIntrospect */ = { isa = XCSwiftPackageProductDependency; - productName = Introspect; + productName = SwiftUIIntrospect; }; /* End XCSwiftPackageProductDependency section */ }; diff --git a/Introspect.xcworkspace/xcshareddata/xcschemes/Introspect-Dynamic.xcscheme b/Examples/Showcase/Showcase.xcodeproj/xcshareddata/xcschemes/Showcase.xcscheme similarity index 65% rename from Introspect.xcworkspace/xcshareddata/xcschemes/Introspect-Dynamic.xcscheme rename to Examples/Showcase/Showcase.xcodeproj/xcshareddata/xcschemes/Showcase.xcscheme index 80c68be..109f41c 100644 --- a/Introspect.xcworkspace/xcshareddata/xcschemes/Introspect-Dynamic.xcscheme +++ b/Examples/Showcase/Showcase.xcodeproj/xcshareddata/xcschemes/Showcase.xcscheme @@ -1,6 +1,6 @@ + BlueprintIdentifier = "D53071F229983CEF00F1936C" + BuildableName = "Showcase.app" + BlueprintName = "Showcase" + ReferencedContainer = "container:Showcase.xcodeproj"> @@ -40,6 +40,16 @@ debugDocumentVersioning = "YES" debugServiceExtension = "internal" allowLocationSimulation = "YES"> + + + + - + + BlueprintIdentifier = "D53071F229983CEF00F1936C" + BuildableName = "Showcase.app" + BlueprintName = "Showcase" + ReferencedContainer = "container:Showcase.xcodeproj"> - + diff --git a/Examples/Showcase/Showcase/App.swift b/Examples/Showcase/Showcase/App.swift index 88579ae..f9cecf9 100644 --- a/Examples/Showcase/Showcase/App.swift +++ b/Examples/Showcase/Showcase/App.swift @@ -1,5 +1,19 @@ import SwiftUI +#if os(iOS) || os(tvOS) +@UIApplicationMain +final class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + window = UIWindow(frame: UIScreen.main.bounds) + window?.rootViewController = UIHostingController(rootView: ContentView()) + window?.makeKeyAndVisible() + return true + } +} +#elseif os(macOS) @main struct App: SwiftUI.App { var body: some Scene { @@ -8,3 +22,4 @@ struct App: SwiftUI.App { } } } +#endif diff --git a/Examples/Showcase/Showcase/Assets.xcassets/AccentColor.colorset/Contents.json b/Examples/Showcase/Showcase/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index eb87897..0000000 --- a/Examples/Showcase/Showcase/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors" : [ - { - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Examples/Showcase/Showcase/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/Showcase/Showcase/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 13613e3..0000000 --- a/Examples/Showcase/Showcase/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Examples/Showcase/Showcase/Assets.xcassets/Contents.json b/Examples/Showcase/Showcase/Assets.xcassets/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/Examples/Showcase/Showcase/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Examples/Showcase/Showcase/ContentView.swift b/Examples/Showcase/Showcase/ContentView.swift index 24f9c3f..dafb151 100644 --- a/Examples/Showcase/Showcase/ContentView.swift +++ b/Examples/Showcase/Showcase/ContentView.swift @@ -1,38 +1,50 @@ import SwiftUI -import Introspect +import SwiftUIIntrospect struct ContentView: View { - @State private var selection = 0 + @State var selection = 0 + var body: some View { TabView(selection: $selection) { ListShowcase() .tabItem { Text("List") } .tag(0) - .introspectTabBarController { tabBarController in - tabBarController.tabBar.layer.backgroundColor = UIColor.green.cgColor - } ScrollViewShowcase() .tabItem { Text("ScrollView") } .tag(1) + #if !os(macOS) NavigationShowcase() .tabItem { Text("Navigation") } .tag(2) ViewControllerShowcase() .tabItem { Text("ViewController") } .tag(3) + #endif SimpleElementsShowcase() .tabItem { Text("Simple elements") } .tag(4) } + #if os(iOS) || os(tvOS) + .introspect(.tabView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { tabBarController in + tabBarController.tabBar.layer.backgroundColor = UIColor.green.cgColor + } + #elseif os(macOS) + .introspect(.tabView, on: .macOS(.v10_15, .v11, .v12, .v13)) { splitView in + splitView.subviews.first?.layer?.backgroundColor = NSColor.green.cgColor + } + #endif + .preferredColorScheme(.light) } } struct ListShowcase: View { var body: some View { - - HStack { + VStack(spacing: 40) { VStack { Text("Default") + .lineLimit(1) + .minimumScaleFactor(0.5) + .padding(.horizontal, 12) List { Text("Item 1") Text("Item 2") @@ -40,28 +52,54 @@ struct ListShowcase: View { } VStack { - Text("List.introspectTableView()") + Text(".introspect(.list, ...)") + .lineLimit(1) + .minimumScaleFactor(0.5) + .padding(.horizontal, 12) + .font(.system(.subheadline, design: .monospaced)) List { Text("Item 1") Text("Item 2") } - .introspectTableView { tableView in - #if !os(tvOS) - tableView.separatorStyle = .none - #endif + #if os(iOS) || os(tvOS) + .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { tableView in + tableView.backgroundView = UIView() + tableView.backgroundColor = .cyan } + .introspect(.list, on: .iOS(.v16)) { collectionView in + collectionView.backgroundView = UIView() + collectionView.subviews.dropFirst(1).first?.backgroundColor = .cyan + } + #elseif os(macOS) + .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13)) { tableView in + tableView.backgroundColor = .cyan + } + #endif } VStack { - Text("child.introspectTableView()") + Text(".introspect(.list, ..., scope: .ancestor)") + .lineLimit(1) + .minimumScaleFactor(0.5) + .padding(.horizontal, 12) + .font(.system(.subheadline, design: .monospaced)) List { Text("Item 1") Text("Item 2") - .introspectTableView { tableView in - #if !os(tvOS) - tableView.separatorStyle = .none - #endif + #if os(iOS) || os(tvOS) + .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor) { tableView in + tableView.backgroundView = UIView() + tableView.backgroundColor = .cyan } + .introspect(.list, on: .iOS(.v16), scope: .ancestor) { collectionView in + collectionView.backgroundView = UIView() + collectionView.subviews.dropFirst(1).first?.backgroundColor = .cyan + } + #elseif os(macOS) + .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13), scope: .ancestor) { tableView in + tableView.backgroundColor = .cyan + } + #endif } } } @@ -69,75 +107,114 @@ struct ListShowcase: View { } } +struct ScrollViewShowcase: View { + var body: some View { + VStack(spacing: 40) { + ScrollView { + Text("Default") + .frame(maxWidth: .infinity) + .lineLimit(1) + .minimumScaleFactor(0.5) + .padding(.horizontal, 12) + } + + ScrollView { + Text(".introspect(.scrollView, ...)") + .frame(maxWidth: .infinity) + .lineLimit(1) + .minimumScaleFactor(0.5) + .padding(.horizontal, 12) + .font(.system(.subheadline, design: .monospaced)) + } + #if os(iOS) || os(tvOS) + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { scrollView in + scrollView.layer.backgroundColor = UIColor.cyan.cgColor + } + #elseif os(macOS) + .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13)) { scrollView in + scrollView.drawsBackground = true + scrollView.backgroundColor = .cyan + } + #endif + + ScrollView { + Text(".introspect(.scrollView, ..., scope: .ancestor)") + .frame(maxWidth: .infinity) + .lineLimit(1) + .minimumScaleFactor(0.5) + .padding(.horizontal, 12) + .font(.system(.subheadline, design: .monospaced)) + #if os(iOS) || os(tvOS) + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor) { scrollView in + scrollView.layer.backgroundColor = UIColor.cyan.cgColor + } + #elseif os(macOS) + .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13), scope: .ancestor) { scrollView in + scrollView.drawsBackground = true + scrollView.backgroundColor = .cyan + } + #endif + } + } + } +} + struct NavigationShowcase: View { var body: some View { NavigationView { - Text("Customized") - .do { - if #available(iOS 15, tvOS 15, *) { + Text("Content") + .modifier { + if #available(iOS 15, tvOS 15, macOS 12, *) { $0.searchable(text: .constant("")) } else { $0 } } - .do { - #if os(iOS) - if #available(iOS 15, *) { - $0.introspectSearchController { searchController in - searchController.searchBar.backgroundColor = .purple - } - } - #else - $0 - #endif - } - #if !os(tvOS) + #if os(iOS) .navigationBarTitle(Text("Customized"), displayMode: .inline) + #elseif os(macOS) + .navigationTitle(Text("Navigation")) #endif - .introspectNavigationController { navigationController in - navigationController.navigationBar.backgroundColor = .red - } } - .introspectSplitViewController { splitViewController in + #if os(iOS) || os(tvOS) + .introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { navigationController in + navigationController.navigationBar.backgroundColor = .cyan + } + .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16)) { splitViewController in splitViewController.preferredDisplayMode = .oneOverSecondary } + .introspect(.navigationView(style: .columns), on: .tvOS(.v13, .v14, .v15, .v16)) { navigationController in + navigationController.navigationBar.backgroundColor = .cyan + } + .introspect(.searchField, on: .iOS(.v15, .v16), .tvOS(.v15, .v16)) { searchField in + searchField.backgroundColor = .red + #if os(iOS) + searchField.searchTextField.backgroundColor = .purple + #endif + } + #endif } } +#if os(iOS) || os(tvOS) struct ViewControllerShowcase: View { var body: some View { NavigationView { VStack { - Text("Customized") - } - .introspectViewController { viewController in - viewController.navigationItem.title = "Customized" - } - } - } -} - -struct ScrollViewShowcase: View { - var body: some View { - HStack { - ScrollView { - Text("Default") - } - ScrollView { - Text("ScrollView.introspectScrollView()") - } - .introspectScrollView { scrollView in - scrollView.layer.backgroundColor = UIColor.red.cgColor - } - ScrollView { - Text("child.introspectScrollView()") - .introspectScrollView { scrollView in - scrollView.layer.backgroundColor = UIColor.green.cgColor - } + Text(".introspect(.view, ...)") + .lineLimit(1) + .minimumScaleFactor(0.5) + .padding(.horizontal, 12) + .font(.system(.subheadline, design: .monospaced)) } } + .navigationViewStyle(.stack) + .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { viewController in + viewController.children.first?.view.backgroundColor = .cyan + } } } +#endif struct SimpleElementsShowcase: View { @@ -151,28 +228,49 @@ struct SimpleElementsShowcase: View { VStack { HStack { TextField("Text Field Red", text: $textFieldValue) - .introspectTextField { textField in - textField.layer.backgroundColor = UIColor.red.cgColor + #if os(iOS) || os(tvOS) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { textField in + textField.backgroundColor = .red } + #elseif os(macOS) + .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13)) { textField in + textField.backgroundColor = .red + } + #endif TextField("Text Field Green", text: $textFieldValue) - .introspectTextField { textField in - textField.layer.backgroundColor = UIColor.green.cgColor + .cornerRadius(8) + #if os(iOS) || os(tvOS) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { textField in + textField.backgroundColor = .green } + #elseif os(macOS) + .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13)) { textField in + textField.backgroundColor = .green + } + #endif } HStack { Toggle("Toggle Red", isOn: $toggleValue) - #if !os(tvOS) - .introspectSwitch { uiSwitch in - uiSwitch.layer.backgroundColor = UIColor.red.cgColor + #if os(iOS) + .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16)) { toggle in + toggle.backgroundColor = .red + } + #elseif os(macOS) + .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13)) { toggle in + toggle.layer?.backgroundColor = NSColor.red.cgColor } #endif Toggle("Toggle Green", isOn: $toggleValue) - #if !os(tvOS) - .introspectSwitch { uiSwitch in - uiSwitch.layer.backgroundColor = UIColor.green.cgColor + #if os(iOS) + .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16)) { toggle in + toggle.backgroundColor = .green + } + #elseif os(macOS) + .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13)) { toggle in + toggle.layer?.backgroundColor = NSColor.green.cgColor } #endif } @@ -180,39 +278,69 @@ struct SimpleElementsShowcase: View { #if !os(tvOS) HStack { Slider(value: $sliderValue, in: 0...100) - .introspectSlider { slider in - slider.layer.backgroundColor = UIColor.red.cgColor + #if os(iOS) + .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16)) { slider in + slider.backgroundColor = .red } + #elseif os(macOS) + .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13)) { slider in + slider.layer?.backgroundColor = NSColor.red.cgColor + } + #endif Slider(value: $sliderValue, in: 0...100) - .introspectSlider { slider in - slider.layer.backgroundColor = UIColor.green.cgColor + #if os(iOS) + .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16)) { slider in + slider.backgroundColor = .green } + #elseif os(macOS) + .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13)) { slider in + slider.layer?.backgroundColor = NSColor.green.cgColor + } + #endif } HStack { Stepper(onIncrement: {}, onDecrement: {}) { Text("Stepper Red") } - .introspectStepper { stepper in - stepper.layer.backgroundColor = UIColor.red.cgColor + #if os(iOS) + .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16)) { stepper in + stepper.backgroundColor = .red } + #elseif os(macOS) + .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13)) { stepper in + stepper.layer?.backgroundColor = NSColor.red.cgColor + } + #endif Stepper(onIncrement: {}, onDecrement: {}) { Text("Stepper Green") } - .introspectStepper { stepper in - stepper.layer.backgroundColor = UIColor.green.cgColor + #if os(iOS) + .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16)) { stepper in + stepper.backgroundColor = .green } + #elseif os(macOS) + .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13)) { stepper in + stepper.layer?.backgroundColor = NSColor.green.cgColor + } + #endif } HStack { DatePicker(selection: $datePickerValue) { Text("DatePicker Red") } - .introspectDatePicker { datePicker in - datePicker.layer.backgroundColor = UIColor.red.cgColor + #if os(iOS) + .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16)) { datePicker in + datePicker.backgroundColor = .red } + #elseif os(macOS) + .introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13)) { datePicker in + datePicker.layer?.backgroundColor = NSColor.red.cgColor + } + #endif } #endif @@ -223,9 +351,15 @@ struct SimpleElementsShowcase: View { Text("Option 3").tag(2) } .pickerStyle(SegmentedPickerStyle()) - .introspectSegmentedControl { segmentedControl in - segmentedControl.layer.backgroundColor = UIColor.red.cgColor + #if os(iOS) || os(tvOS) + .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { datePicker in + datePicker.backgroundColor = .red } + #elseif os(macOS) + .introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13)) { datePicker in + datePicker.layer?.backgroundColor = NSColor.red.cgColor + } + #endif } } @@ -234,9 +368,6 @@ struct SimpleElementsShowcase: View { struct ContentView_Previews: PreviewProvider { static var previews: some View { - Group { - ListShowcase() - NavigationShowcase() - } + ContentView() } } diff --git a/Examples/Showcase/Showcase/Helpers.swift b/Examples/Showcase/Showcase/Helpers.swift index 5670498..580bb9f 100644 --- a/Examples/Showcase/Showcase/Helpers.swift +++ b/Examples/Showcase/Showcase/Helpers.swift @@ -1,7 +1,8 @@ import SwiftUI extension View { - func `do`( + @ViewBuilder + func modifier( @ViewBuilder transform: (Self) -> TransformedView ) -> TransformedView { transform(self) diff --git a/Examples/Showcase/Showcase/Preview Content/Preview Assets.xcassets/Contents.json b/Examples/Showcase/Showcase/Preview Content/Preview Assets.xcassets/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/Examples/Showcase/Showcase/Preview Content/Preview Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Introspect.xcworkspace/contents.xcworkspacedata b/Introspect.xcworkspace/contents.xcworkspacedata index 9c25725..b369549 100644 --- a/Introspect.xcworkspace/contents.xcworkspacedata +++ b/Introspect.xcworkspace/contents.xcworkspacedata @@ -4,6 +4,9 @@ + + diff --git a/Introspect.xcworkspace/xcshareddata/xcschemes/Introspect-Static.xcscheme b/Introspect.xcworkspace/xcshareddata/xcschemes/SwiftUIIntrospect.xcscheme similarity index 86% rename from Introspect.xcworkspace/xcshareddata/xcschemes/Introspect-Static.xcscheme rename to Introspect.xcworkspace/xcshareddata/xcschemes/SwiftUIIntrospect.xcscheme index aebc960..825b517 100644 --- a/Introspect.xcworkspace/xcshareddata/xcschemes/Introspect-Static.xcscheme +++ b/Introspect.xcworkspace/xcshareddata/xcschemes/SwiftUIIntrospect.xcscheme @@ -14,9 +14,9 @@ buildForAnalyzing = "YES"> @@ -50,9 +50,9 @@ diff --git a/Package.swift b/Package.swift index 0204e00..1e30861 100644 --- a/Package.swift +++ b/Package.swift @@ -17,12 +17,12 @@ let package = Package( targets: [ .target( name: "Introspect", - path: "Introspect" // TODO: rename to Sources for v1.0 + path: "Introspect" ), .testTarget( name: "IntrospectTests", dependencies: ["Introspect"], - path: "IntrospectTests" // TODO: rename to Tests for v1.0 + path: "IntrospectTests" ), ] ) diff --git a/Package@swift-5.7.swift b/Package@swift-5.7.swift new file mode 100644 index 0000000..4b1cf8b --- /dev/null +++ b/Package@swift-5.7.swift @@ -0,0 +1,39 @@ +// swift-tools-version:5.7 + +import PackageDescription + +let package = Package( + name: "swiftui-introspect", + platforms: [ + .iOS(.v13), + .macOS(.v10_15), + .tvOS(.v13), + ], + products: [ + // legacy library + .library(name: "Introspect", targets: ["Introspect"]), + .library(name: "Introspect-Static", type: .static, targets: ["Introspect"]), + .library(name: "Introspect-Dynamic", type: .dynamic, targets: ["Introspect"]), + + // new experimental library + .library(name: "SwiftUIIntrospect", targets: ["SwiftUIIntrospect"]), + .library(name: "SwiftUIIntrospect-Static", type: .static, targets: ["SwiftUIIntrospect"]), + .library(name: "SwiftUIIntrospect-Dynamic", type: .dynamic, targets: ["SwiftUIIntrospect"]), + ], + targets: [ + .target( + name: "Introspect", + path: "Introspect" + ), + .testTarget( + name: "IntrospectTests", + dependencies: ["Introspect"], + path: "IntrospectTests" + ), + + .target( + name: "SwiftUIIntrospect", + path: "Sources" + ), + ] +) diff --git a/README.md b/README.md index c02e48d..93d804f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ +> **Note** +> +> [`SwiftUIIntrospect`](Package@swift-5.7.swift#L19) is an all-new module based off the original [`Introspect`](Package.swift#L13) module that improves on stability, predictability, and ergonomics. +> +> Both modules currently live together under this repo, but the plan is to ultimately obsolete `Introspect` in favor of `SwiftUIIntrospect` as part of a 1.0 release. +> +> Read the [`SwiftUIIntrospect` documentation](docs/SwiftUIIntrospect.md) to learn more. + Introspect for SwiftUI ====================== diff --git a/Sources/Introspect.swift b/Sources/Introspect.swift new file mode 100644 index 0000000..08012ba --- /dev/null +++ b/Sources/Introspect.swift @@ -0,0 +1,201 @@ +import SwiftUI + +public struct IntrospectionScope: OptionSet { + public static let receiver = Self(rawValue: 1 << 0) + public static let ancestor = Self(rawValue: 1 << 1) + + @_spi(Private) public let rawValue: UInt + + @_spi(Private) public init(rawValue: UInt) { + self.rawValue = rawValue + } +} + +extension View { + @ViewBuilder + public func introspect( + _ viewType: SwiftUIViewType, + on platforms: (PlatformViewVersions)..., + scope: IntrospectionScope? = nil, + customize: @escaping (PlatformSpecificView) -> Void + ) -> some View { + if platforms.contains(where: \.isCurrent) { + let id = UUID() + self.background( + IntrospectionAnchorView( + id: id + ) + .frame(width: 0, height: 0) + ) + .overlay( + IntrospectionView( + selector: { (view: PlatformView) in + let scope = scope ?? viewType.scope + if + scope.contains(.receiver), + let target = view.receiver(ofType: PlatformSpecificView.self, anchorID: id) + { + return target + } + if + scope.contains(.ancestor), + let target = view.ancestor(ofType: PlatformSpecificView.self) + { + return target + } + return nil + }, + customize: customize + ) + .frame(width: 0, height: 0) + ) + } else { + self + } + } + + @ViewBuilder + public func introspect( + _ viewType: SwiftUIViewType, + on platforms: (PlatformViewVersions)..., + scope: IntrospectionScope? = nil, + customize: @escaping (PlatformSpecificViewController) -> Void + ) -> some View { + if platforms.contains(where: \.isCurrent) { + self.overlay( + IntrospectionView( + selector: { (viewController: PlatformViewController) in + let scope = scope ?? viewType.scope + if + scope.contains(.receiver), + let target = viewController.receiver(ofType: PlatformSpecificViewController.self) + { + return target + } + if + scope.contains(.ancestor), + let target = viewController.ancestor(ofType: PlatformSpecificViewController.self) + { + return target + } + return nil + }, + customize: customize + ) + .frame(width: 0, height: 0) + ) + } else { + self + } + } +} + +extension PlatformView { + fileprivate func receiver( + ofType type: PlatformSpecificView.Type, + anchorID: IntrospectionAnchorView.ID + ) -> PlatformSpecificView? { + let frontView = self + guard + let backView = Array(frontView.superviews).last?.viewWithTag(anchorID.hashValue), + let superview = backView.nearestCommonSuperviewWith(frontView) + else { + return nil + } + + return superview + .subviewsBetween(backView, and: frontView) + .compactMap { $0 as? PlatformSpecificView } + .first + } + + fileprivate func ancestor( + ofType type: PlatformSpecificView.Type + ) -> PlatformSpecificView? { + self.superviews.lazy.compactMap { $0 as? PlatformSpecificView }.first + } +} + +extension PlatformView { + private var superviews: some Sequence { + sequence(first: self, next: \.superview).dropFirst() + } + + private func nearestCommonSuperviewWith(_ other: PlatformView) -> PlatformView? { + var nearestAncestor: PlatformView? = self + + while let currentView = nearestAncestor, !other.isDescendant(of: currentView) { + nearestAncestor = currentView.superview + } + + return nearestAncestor + } + + private func subviewsBetween(_ bottomView: PlatformView, and topView: PlatformView) -> [PlatformView] { + var entered = false + var result: [PlatformView] = [] + + for subview in self.allSubviews { + if subview === bottomView { + entered = true + continue + } + if subview === topView { + return result + } + if entered { + result.append(subview) + } + } + + return result + } + + private var allSubviews: [PlatformView] { + self.subviews.reduce([self]) { $0 + $1.allSubviews } + } +} + +extension PlatformViewController { + fileprivate func receiver( + ofType type: PlatformSpecificViewController.Type + ) -> PlatformSpecificViewController? { + self.hostingView? + .allChildren(ofType: PlatformSpecificViewController.self) + .filter { !($0 is IntrospectionPlatformViewController) } + .first + } + + fileprivate func ancestor( + ofType type: PlatformSpecificViewController.Type + ) -> PlatformSpecificViewController? { + self.parents + .lazy + .filter { !($0 is IntrospectionPlatformViewController) } + .compactMap { $0 as? PlatformSpecificViewController } + .first + } +} + +extension PlatformViewController { + private var parents: some Sequence { + sequence(first: self, next: \.parent).dropFirst() + } + + private var hostingView: PlatformViewController? { + self.parents.first(where: { + let type = String(reflecting: type(of: $0)) + return type.hasPrefix("SwiftUI.") && type.contains("Hosting") + }) + } + + private func allChildren( + ofType type: PlatformSpecificViewController.Type + ) -> [PlatformSpecificViewController] { + var result = self.children.compactMap { $0 as? PlatformSpecificViewController } + for subview in self.children { + result.append(contentsOf: subview.allChildren(ofType: type)) + } + return result + } +} diff --git a/Sources/IntrospectableViewType.swift b/Sources/IntrospectableViewType.swift new file mode 100644 index 0000000..4f41398 --- /dev/null +++ b/Sources/IntrospectableViewType.swift @@ -0,0 +1,7 @@ +public protocol IntrospectableViewType { + var scope: IntrospectionScope { get } +} + +extension IntrospectableViewType { + public var scope: IntrospectionScope { .receiver } +} diff --git a/Sources/IntrospectionView.swift b/Sources/IntrospectionView.swift new file mode 100644 index 0000000..2997007 --- /dev/null +++ b/Sources/IntrospectionView.swift @@ -0,0 +1,170 @@ +import SwiftUI + +/// ⚓️ +struct IntrospectionAnchorView: PlatformViewRepresentable { + typealias ID = UUID + + @Binding + private var observed: Void // workaround for state changes not triggering view updates + + let id: ID + + init(id: ID) { + self._observed = .constant(()) + self.id = id + } + + #if canImport(UIKit) + func makeUIView(context: Context) -> UIView { + let view = UIView() + view.tag = id.hashValue + return view + } + func updateUIView(_ controller: UIView, context: Context) {} + #elseif canImport(AppKit) + func makeNSView(context: Context) -> NSView { + final class TaggableView: NSView { + private var _tag: Int? + override var tag: Int { + get { _tag ?? super.tag } + set { _tag = newValue } + } + } + let view = TaggableView() + view.tag = id.hashValue + return view + } + func updateNSView(_ controller: NSView, context: Context) {} + #endif +} + +struct IntrospectionView: PlatformViewControllerRepresentable { + final class TargetCache { + weak var target: Target? + } + + @Binding + private var observed: Void // workaround for state changes not triggering view updates + private let selector: (IntrospectionPlatformViewController) -> Target? + private let customize: (Target) -> Void + + init( + selector: @escaping (PlatformView) -> Target?, + customize: @escaping (Target) -> Void + ) { + self._observed = .constant(()) + self.selector = { introspectionViewController in + #if canImport(UIKit) + if let introspectionView = introspectionViewController.viewIfLoaded { + return selector(introspectionView) + } + #elseif canImport(AppKit) + if introspectionViewController.isViewLoaded { + return selector(introspectionViewController.view) + } + #endif + return nil + } + self.customize = customize + } + + init( + selector: @escaping (PlatformViewController) -> Target?, + customize: @escaping (Target) -> Void + ) { + self._observed = .constant(()) + self.selector = { selector($0) } + self.customize = customize + } + + func makeCoordinator() -> TargetCache { + TargetCache() + } + + func makePlatformViewController(context: Context) -> IntrospectionPlatformViewController { + let controller = IntrospectionPlatformViewController { controller in + guard let target = selector(controller) else { + return + } + context.coordinator.target = target + customize(target) + controller.handler = nil + } + + // - Workaround - + // iOS/tvOS 13 sometimes need a nudge on the next run loop. + if #available(iOS 14, tvOS 14, *) {} else { + DispatchQueue.main.async { [weak controller] in + controller?.handler?() + } + } + + return controller + } + + func updatePlatformViewController(_ controller: IntrospectionPlatformViewController, context: Context) { + guard let target = context.coordinator.target ?? selector(controller) else { + return + } + customize(target) + } + + static func dismantlePlatformViewController(_ controller: IntrospectionPlatformViewController, coordinator: Coordinator) { + controller.handler = nil + } +} + +final class IntrospectionPlatformViewController: PlatformViewController { + var handler: (() -> Void)? = nil + + fileprivate init(handler: ((IntrospectionPlatformViewController) -> Void)?) { + super.init(nibName: nil, bundle: nil) + self.handler = { [weak self] in + guard let self = self else { + return + } + handler?(self) + } + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + #if canImport(UIKit) + override func didMove(toParent parent: UIViewController?) { + super.didMove(toParent: parent) + handler?() + } + + override func viewDidLoad() { + super.viewDidLoad() + handler?() + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + handler?() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + handler?() + } + #elseif canImport(AppKit) + override func loadView() { + view = NSView() + } + + override func viewDidLoad() { + super.viewDidLoad() + handler?() + } + + override func viewDidAppear() { + super.viewDidAppear() + handler?() + } + #endif +} diff --git a/Sources/PlatformVersion.swift b/Sources/PlatformVersion.swift new file mode 100644 index 0000000..332ef5f --- /dev/null +++ b/Sources/PlatformVersion.swift @@ -0,0 +1,165 @@ +import Foundation + +public protocol PlatformVersion { + var isCurrent: Bool { get } +} + +public struct iOSVersion: PlatformVersion { + public let isCurrent: Bool + + public init(isCurrent: () -> Bool) { + self.isCurrent = isCurrent() + } +} + +extension iOSVersion { + public static let v13 = iOSVersion { + if #available(iOS 14, *) { + return false + } + if #available(iOS 13, *) { + return true + } + return false + } + + public static let v14 = iOSVersion { + if #available(iOS 15, *) { + return false + } + if #available(iOS 14, *) { + return true + } + return false + } + + public static let v15 = iOSVersion { + if #available(iOS 16, *) { + return false + } + if #available(iOS 15, *) { + return true + } + return false + } + + public static let v16 = iOSVersion { + if #available(iOS 17, *) { + return false + } + if #available(iOS 16, *) { + return true + } + return false + } +} + +public struct tvOSVersion: PlatformVersion { + public let isCurrent: Bool + + public init(isCurrent: () -> Bool) { + self.isCurrent = isCurrent() + } +} + +extension tvOSVersion { + public static let v13 = tvOSVersion { + if #available(tvOS 14, *) { + return false + } + if #available(tvOS 13, *) { + return true + } + return false + } + + public static let v14 = tvOSVersion { + if #available(tvOS 15, *) { + return false + } + if #available(tvOS 14, *) { + return true + } + return false + } + + public static let v15 = tvOSVersion { + if #available(tvOS 16, *) { + return false + } + if #available(tvOS 15, *) { + return true + } + return false + } + + public static let v16 = tvOSVersion { + if #available(tvOS 17, *) { + return false + } + if #available(tvOS 16, *) { + return true + } + return false + } +} + +public struct macOSVersion: PlatformVersion { + public let isCurrent: Bool + + public init(isCurrent: () -> Bool) { + self.isCurrent = isCurrent() + } +} + +extension macOSVersion { + public static let v10_15 = macOSVersion { + if #available(macOS 11, *) { + return false + } + if #available(macOS 10.15, *) { + return true + } + return false + } + + public static let v10_15_4 = macOSVersion { + if #available(macOS 11, *) { + return false + } + if #available(macOS 10.15.4, *) { + return true + } + return false + } + + public static let v11 = macOSVersion { + if #available(macOS 12, *) { + return false + } + if #available(macOS 11, *) { + return true + } + return false + } + + public static let v12 = macOSVersion { + if #available(macOS 13, *) { + return false + } + if #available(macOS 12, *) { + return true + } + return false + } + + public static let v13 = macOSVersion { + if #available(macOS 14, *) { + return false + } + if #available(macOS 13, *) { + return true + } + return false + } +} diff --git a/Sources/PlatformView.swift b/Sources/PlatformView.swift new file mode 100644 index 0000000..a89c412 --- /dev/null +++ b/Sources/PlatformView.swift @@ -0,0 +1,55 @@ +import SwiftUI + +#if canImport(UIKit) +public typealias PlatformView = UIView +#elseif canImport(AppKit) +public typealias PlatformView = NSView +#endif + +#if canImport(UIKit) +typealias PlatformViewRepresentable = UIViewRepresentable +#elseif canImport(AppKit) +typealias PlatformViewRepresentable = NSViewRepresentable +#endif + +#if canImport(UIKit) +public typealias PlatformViewController = UIViewController +#elseif canImport(AppKit) +public typealias PlatformViewController = NSViewController +#endif + +#if canImport(UIKit) +typealias _PlatformViewControllerRepresentable = UIViewControllerRepresentable +#elseif canImport(AppKit) +typealias _PlatformViewControllerRepresentable = NSViewControllerRepresentable +#endif + +protocol PlatformViewControllerRepresentable: _PlatformViewControllerRepresentable { + func makePlatformViewController(context: Context) -> IntrospectionPlatformViewController + func updatePlatformViewController(_ controller: IntrospectionPlatformViewController, context: Context) + static func dismantlePlatformViewController(_ controller: IntrospectionPlatformViewController, coordinator: Coordinator) +} + +extension PlatformViewControllerRepresentable { + #if canImport(UIKit) + func makeUIViewController(context: Context) -> IntrospectionPlatformViewController { + makePlatformViewController(context: context) + } + func updateUIViewController(_ controller: IntrospectionPlatformViewController, context: Context) { + updatePlatformViewController(controller, context: context) + } + static func dismantleUIViewController(_ controller: IntrospectionPlatformViewController, coordinator: Coordinator) { + dismantlePlatformViewController(controller, coordinator: coordinator) + } + #elseif canImport(AppKit) + func makeNSViewController(context: Context) -> IntrospectionPlatformViewController { + makePlatformViewController(context: context) + } + func updateNSViewController(_ controller: IntrospectionPlatformViewController, context: Context) { + updatePlatformViewController(controller, context: context) + } + static func dismantleNSViewController(_ controller: IntrospectionPlatformViewController, coordinator: Coordinator) { + dismantlePlatformViewController(controller, coordinator: coordinator) + } + #endif +} diff --git a/Sources/PlatformViewVersion.swift b/Sources/PlatformViewVersion.swift new file mode 100644 index 0000000..09f883d --- /dev/null +++ b/Sources/PlatformViewVersion.swift @@ -0,0 +1,48 @@ +import SwiftUI + +public struct PlatformViewVersions { + let isCurrent: Bool + + public static func iOS(_ versions: (iOSViewVersion)...) -> Self { + Self(isCurrent: versions.contains(where: \.isCurrent)) + } + + public static func tvOS(_ versions: (tvOSViewVersion)...) -> Self { + Self(isCurrent: versions.contains(where: \.isCurrent)) + } + + public static func macOS(_ versions: (macOSViewVersion)...) -> Self { + Self(isCurrent: versions.contains(where: \.isCurrent)) + } +} + +public typealias iOSViewVersion = PlatformViewVersion +public typealias tvOSViewVersion = PlatformViewVersion +public typealias macOSViewVersion = PlatformViewVersion + +public struct PlatformViewVersion { + let isCurrent: Bool +} + +extension PlatformViewVersion { + @_spi(Internals) public init(for version: Version) { + self.init(isCurrent: version.isCurrent) + } + + @_spi(Internals) public static func unavailable(file: StaticString = #file, line: UInt = #line) -> Self { + let filePath = file.withUTF8Buffer { String(decoding: $0, as: UTF8.self) } + let fileName = URL(fileURLWithPath: filePath).lastPathComponent + runtimeWarn( + """ + If you're seeing this, someone forgot to mark \(fileName):\(line) as unavailable. + + This won't have any effect, but it should be disallowed altogether. + + Please report it upstream so we can properly fix it by using the following link: + + https://github.com/siteline/swiftui-introspect/issues/new?title=`\(fileName):\(line)`+should+be+marked+unavailable + """ + ) + return Self(isCurrent: false) + } +} diff --git a/Sources/RuntimeWarnings.swift b/Sources/RuntimeWarnings.swift new file mode 100644 index 0000000..bee2476 --- /dev/null +++ b/Sources/RuntimeWarnings.swift @@ -0,0 +1,83 @@ +// MIT License +// +// Copyright (c) 2020 Point-Free, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import Foundation + +@_transparent +@usableFromInline +@inline(__always) +func runtimeWarn( + _ message: @autoclosure () -> String, + category: String? = "SwiftUIIntrospect" +) { + #if DEBUG + let message = message() + let category = category ?? "Runtime Warning" + #if canImport(os) + os_log( + .fault, + dso: dso, + log: OSLog(subsystem: "com.apple.runtime-issues", category: category), + "%@", + message + ) + #else + fputs("\(formatter.string(from: Date())) [\(category)] \(message)\n", stderr) + #endif + #endif +} + +#if DEBUG + #if canImport(os) + import os + + // NB: Xcode runtime warnings offer a much better experience than traditional assertions and + // breakpoints, but Apple provides no means of creating custom runtime warnings ourselves. + // To work around this, we hook into SwiftUI's runtime issue delivery mechanism, instead. + // + // Feedback filed: https://gist.github.com/stephencelis/a8d06383ed6ccde3e5ef5d1b3ad52bbc + @usableFromInline + let dso = { () -> UnsafeMutableRawPointer in + let count = _dyld_image_count() + for i in 0.. { + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif +#endif diff --git a/Sources/ViewTypes/ColorPicker.swift b/Sources/ViewTypes/ColorPicker.swift new file mode 100644 index 0000000..943cd75 --- /dev/null +++ b/Sources/ViewTypes/ColorPicker.swift @@ -0,0 +1,31 @@ +#if !os(tvOS) +import SwiftUI + +// MARK: SwiftUI.ColorPicker + +public struct ColorPickerType: IntrospectableViewType {} + +extension IntrospectableViewType where Self == ColorPickerType { + public static var colorPicker: Self { .init() } +} + +#if canImport(UIKit) +@available(iOS 14, *) +extension iOSViewVersion { + @available(*, unavailable, message: "ColorPicker isn't available on iOS 13") + public static let v13 = Self.unavailable() + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +@available(macOS 11, *) +extension macOSViewVersion { + @available(*, unavailable, message: "ColorPicker isn't available on macOS 10.15") + public static let v10_15 = Self.unavailable() + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif +#endif diff --git a/Sources/ViewTypes/DatePicker.swift b/Sources/ViewTypes/DatePicker.swift new file mode 100644 index 0000000..0f9e37a --- /dev/null +++ b/Sources/ViewTypes/DatePicker.swift @@ -0,0 +1,27 @@ +#if os(iOS) || os(macOS) +import SwiftUI + +// MARK: SwiftUI.DatePicker + +public struct DatePickerType: IntrospectableViewType {} + +extension IntrospectableViewType where Self == DatePickerType { + public static var datePicker: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif +#endif diff --git a/Sources/ViewTypes/DatePickerWithCompactStyle.swift b/Sources/ViewTypes/DatePickerWithCompactStyle.swift new file mode 100644 index 0000000..b6d9a97 --- /dev/null +++ b/Sources/ViewTypes/DatePickerWithCompactStyle.swift @@ -0,0 +1,34 @@ +#if os(iOS) || os(macOS) +import SwiftUI + +// MARK: SwiftUI.DatePicker { ... }.datePickerStyle(.compact) + +public struct DatePickerWithCompactStyleType: IntrospectableViewType { + public enum Style { + case compact + } +} + +extension IntrospectableViewType where Self == DatePickerWithCompactStyleType { + public static func datePicker(style: Self.Style) -> Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + @available(*, unavailable, message: ".datePickerStyle(.compact) isn't available on iOS 13") + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) && !targetEnvironment(macCatalyst) +extension macOSViewVersion { + @available(*, unavailable, message: ".datePickerStyle(.compact) isn't available on macOS 10.15") + public static let v10_15 = Self(for: .v10_15) + public static let v10_15_4 = Self(for: .v10_15_4) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif +#endif diff --git a/Sources/ViewTypes/DatePickerWithFieldStyle.swift b/Sources/ViewTypes/DatePickerWithFieldStyle.swift new file mode 100644 index 0000000..a5d04ac --- /dev/null +++ b/Sources/ViewTypes/DatePickerWithFieldStyle.swift @@ -0,0 +1,24 @@ +#if os(macOS) +import SwiftUI + +// MARK: SwiftUI.DatePicker { ... }.datePickerStyle(.field) + +public struct DatePickerWithFieldStyleType: IntrospectableViewType { + public enum Style { + case field + } +} + +extension IntrospectableViewType where Self == DatePickerWithFieldStyleType { + public static func datePicker(style: Self.Style) -> Self { .init() } +} + +#if canImport(AppKit) && !targetEnvironment(macCatalyst) +extension macOSViewVersion { + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif +#endif diff --git a/Sources/ViewTypes/DatePickerWithGraphicalStyleType.swift b/Sources/ViewTypes/DatePickerWithGraphicalStyleType.swift new file mode 100644 index 0000000..7ef7eab --- /dev/null +++ b/Sources/ViewTypes/DatePickerWithGraphicalStyleType.swift @@ -0,0 +1,32 @@ +#if os(iOS) || os(macOS) +import SwiftUI + +// MARK: SwiftUI.DatePicker { ... }.datePickerStyle(.graphical) + +public struct DatePickerWithGraphicalStyleType: IntrospectableViewType { + public enum Style { + case graphical + } +} + +extension IntrospectableViewType where Self == DatePickerWithGraphicalStyleType { + public static func datePicker(style: Self.Style) -> Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + @available(*, unavailable, message: ".datePickerStyle(.graphical) isn't available on iOS 13") + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) && !targetEnvironment(macCatalyst) +extension macOSViewVersion { + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif +#endif diff --git a/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift b/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift new file mode 100644 index 0000000..690748f --- /dev/null +++ b/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift @@ -0,0 +1,24 @@ +#if os(macOS) +import SwiftUI + +// MARK: SwiftUI.DatePicker { ... }.datePickerStyle(.stepperField) + +public struct DatePickerWithStepperFieldStyleType: IntrospectableViewType { + public enum Style { + case stepperField + } +} + +extension IntrospectableViewType where Self == DatePickerWithStepperFieldStyleType { + public static func datePicker(style: Self.Style) -> Self { .init() } +} + +#if canImport(AppKit) && !targetEnvironment(macCatalyst) +extension macOSViewVersion { + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif +#endif diff --git a/Sources/ViewTypes/DatePickerWithWheelStyle.swift b/Sources/ViewTypes/DatePickerWithWheelStyle.swift new file mode 100644 index 0000000..991ac17 --- /dev/null +++ b/Sources/ViewTypes/DatePickerWithWheelStyle.swift @@ -0,0 +1,24 @@ +#if os(iOS) +import SwiftUI + +// MARK: SwiftUI.DatePicker { ... }.datePickerStyle(.wheel) + +public struct DatePickerWithWheelStyleType: IntrospectableViewType { + public enum Style { + case wheel + } +} + +extension IntrospectableViewType where Self == DatePickerWithWheelStyleType { + public static func datePicker(style: Self.Style) -> Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#endif +#endif diff --git a/Sources/ViewTypes/Form.swift b/Sources/ViewTypes/Form.swift new file mode 100644 index 0000000..cf7a70f --- /dev/null +++ b/Sources/ViewTypes/Form.swift @@ -0,0 +1,30 @@ +#if !os(macOS) +import SwiftUI + +// MARK: SwiftUI.Form + +public struct FormType: IntrospectableViewType {} + +extension IntrospectableViewType where Self == FormType { + public static var form: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) +} + +extension iOSViewVersion { + public static let v16 = Self(for: .v16) +} + +extension tvOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#endif +#endif diff --git a/Sources/ViewTypes/FormWithGroupedStyle.swift b/Sources/ViewTypes/FormWithGroupedStyle.swift new file mode 100644 index 0000000..a8b333d --- /dev/null +++ b/Sources/ViewTypes/FormWithGroupedStyle.swift @@ -0,0 +1,48 @@ +import SwiftUI + +// MARK: SwiftUI.Form { ... }.formStyle(.grouped) + +public struct FormWithGroupedStyleType: IntrospectableViewType { + public enum Style { + case grouped + } +} + +extension IntrospectableViewType where Self == FormWithGroupedStyleType { + public static func form(style: Self.Style) -> Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + @available(*, unavailable, message: ".formStyle(.grouped) isn't available on iOS 13") + public static let v13 = Self.unavailable() + @available(*, unavailable, message: ".formStyle(.grouped) isn't available on iOS 14") + public static let v14 = Self.unavailable() + @available(*, unavailable, message: ".formStyle(.grouped) isn't available on iOS 15") + public static let v15 = Self.unavailable() +} + +extension iOSViewVersion { + public static let v16 = Self(for: .v16) +} + +extension tvOSViewVersion { + @available(*, unavailable, message: ".formStyle(.grouped) isn't available on tvOS 13") + public static let v13 = Self.unavailable() + @available(*, unavailable, message: ".formStyle(.grouped) isn't available on tvOS 14") + public static let v14 = Self.unavailable() + @available(*, unavailable, message: ".formStyle(.grouped) isn't available on tvOS 15") + public static let v15 = Self.unavailable() + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + @available(*, unavailable, message: ".formStyle(.grouped) isn't available on macOS 10.15") + public static let v10_15 = Self.unavailable() + @available(*, unavailable, message: ".formStyle(.grouped) isn't available on macOS 11") + public static let v11 = Self.unavailable() + @available(*, unavailable, message: ".formStyle(.grouped) isn't available on macOS 12") + public static let v12 = Self.unavailable() + public static let v13 = Self(for: .v13) +} +#endif diff --git a/Sources/ViewTypes/List.swift b/Sources/ViewTypes/List.swift new file mode 100644 index 0000000..a3bbdb1 --- /dev/null +++ b/Sources/ViewTypes/List.swift @@ -0,0 +1,40 @@ +import SwiftUI + +// MARK: SwiftUI.List + +public struct ListType: IntrospectableViewType { + public enum Style { + case plain + } +} + +extension IntrospectableViewType where Self == ListType { + public static var list: Self { .init() } + public static func list(style: Self.Style) -> Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) +} + +extension iOSViewVersion { + public static let v16 = Self(for: .v16) +} + +extension tvOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif diff --git a/Sources/ViewTypes/ListCell.swift b/Sources/ViewTypes/ListCell.swift new file mode 100644 index 0000000..8f5e3de --- /dev/null +++ b/Sources/ViewTypes/ListCell.swift @@ -0,0 +1,37 @@ +import SwiftUI + +// MARK: SwiftUI.List { Cell() } + +public struct ListCellType: IntrospectableViewType { + public var scope: IntrospectionScope { .ancestor } +} + +extension IntrospectableViewType where Self == ListCellType { + public static var listCell: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) +} + +extension iOSViewVersion { + public static let v16 = Self(for: .v16) +} + +extension tvOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif diff --git a/Sources/ViewTypes/ListWithBorderedStyle.swift b/Sources/ViewTypes/ListWithBorderedStyle.swift new file mode 100644 index 0000000..4136a1a --- /dev/null +++ b/Sources/ViewTypes/ListWithBorderedStyle.swift @@ -0,0 +1,26 @@ +#if os(macOS) +import SwiftUI + +// MARK: SwiftUI.List { ... }.listStyle(.bordered) + +public struct ListWithBorderedStyleType: IntrospectableViewType { + public enum Style { + case bordered + } +} + +extension IntrospectableViewType where Self == ListWithBorderedStyleType { + public static func list(style: Self.Style) -> Self { .init() } +} + +#if canImport(AppKit) && !targetEnvironment(macCatalyst) +extension macOSViewVersion { + @available(*, unavailable, message: ".listStyle(.insetGrouped) isn't available on macOS 10.15") + public static let v10_15 = Self.unavailable() + @available(*, unavailable, message: ".listStyle(.insetGrouped) isn't available on macOS 11") + public static let v11 = Self.unavailable() + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif +#endif diff --git a/Sources/ViewTypes/ListWithGroupedStyle.swift b/Sources/ViewTypes/ListWithGroupedStyle.swift new file mode 100644 index 0000000..b96fab0 --- /dev/null +++ b/Sources/ViewTypes/ListWithGroupedStyle.swift @@ -0,0 +1,34 @@ +#if os(iOS) || os(tvOS) +import SwiftUI + +// MARK: SwiftUI.List { ... }.listStyle(.grouped) + +public struct ListWithGroupedStyleType: IntrospectableViewType { + public enum Style { + case grouped + } +} + +extension IntrospectableViewType where Self == ListWithGroupedStyleType { + public static func list(style: Self.Style) -> Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) +} + +extension iOSViewVersion { + public static let v16 = Self(for: .v16) +} + +extension tvOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#endif +#endif diff --git a/Sources/ViewTypes/ListWithInsetGroupedStyle.swift b/Sources/ViewTypes/ListWithInsetGroupedStyle.swift new file mode 100644 index 0000000..b40d282 --- /dev/null +++ b/Sources/ViewTypes/ListWithInsetGroupedStyle.swift @@ -0,0 +1,28 @@ +#if os(iOS) +import SwiftUI + +// MARK: SwiftUI.List { ... }.listStyle(.insetGrouped) + +public struct ListWithInsetGroupedStyleType: IntrospectableViewType { + public enum Style { + case insetGrouped + } +} + +extension IntrospectableViewType where Self == ListWithInsetGroupedStyleType { + public static func list(style: Self.Style) -> Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + @available(*, unavailable, message: ".listStyle(.insetGrouped) isn't available on iOS 13") + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) +} + +extension iOSViewVersion { + public static let v16 = Self(for: .v16) +} +#endif +#endif diff --git a/Sources/ViewTypes/ListWithInsetStyle.swift b/Sources/ViewTypes/ListWithInsetStyle.swift new file mode 100644 index 0000000..9a09fd6 --- /dev/null +++ b/Sources/ViewTypes/ListWithInsetStyle.swift @@ -0,0 +1,36 @@ +#if os(iOS) || os(macOS) +import SwiftUI + +// MARK: SwiftUI.List { ... }.listStyle(.inset) + +public struct ListWithInsetStyleType: IntrospectableViewType { + public enum Style { + case inset + } +} + +extension IntrospectableViewType where Self == ListWithInsetStyleType { + public static func list(style: Self.Style) -> Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + @available(*, unavailable, message: ".listStyle(.inset) isn't available on iOS 13") + public static let v13 = Self.unavailable() + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) +} + +extension iOSViewVersion { + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + @available(*, unavailable, message: ".listStyle(.inset) isn't available on macOS 10.15") + public static let v10_15 = Self.unavailable() + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif +#endif diff --git a/Sources/ViewTypes/ListWithSidebarStyle.swift b/Sources/ViewTypes/ListWithSidebarStyle.swift new file mode 100644 index 0000000..6820bf7 --- /dev/null +++ b/Sources/ViewTypes/ListWithSidebarStyle.swift @@ -0,0 +1,35 @@ +#if os(iOS) || os(macOS) +import SwiftUI + +// MARK: SwiftUI.List { ... }.listStyle(.sidebar) + +public struct ListWithSidebarStyleType: IntrospectableViewType { + public enum Style { + case sidebar + } +} + +extension IntrospectableViewType where Self == ListWithSidebarStyleType { + public static func list(style: Self.Style) -> Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + @available(*, unavailable, message: ".listStyle(.sidebar) isn't available on iOS 13") + public static let v13 = Self.unavailable() + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) +} + +extension iOSViewVersion { + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif +#endif diff --git a/Sources/ViewTypes/NavigationSplitView.swift b/Sources/ViewTypes/NavigationSplitView.swift new file mode 100644 index 0000000..b0b2b59 --- /dev/null +++ b/Sources/ViewTypes/NavigationSplitView.swift @@ -0,0 +1,44 @@ +import SwiftUI + +// MARK: SwiftUI.NavigationSplitView + +public struct NavigationSplitViewType: IntrospectableViewType {} + +extension IntrospectableViewType where Self == NavigationSplitViewType { + public static var navigationSplitView: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + @available(*, unavailable, message: "NavigationSplitView isn't available on iOS 13") + public static let v13 = Self.unavailable() + @available(*, unavailable, message: "NavigationSplitView isn't available on iOS 14") + public static let v14 = Self.unavailable() + @available(*, unavailable, message: "NavigationSplitView isn't available on iOS 15") + public static let v15 = Self.unavailable() + + public static let v16 = Self(for: .v16) +} + +extension tvOSViewVersion { + @available(*, unavailable, message: "NavigationSplitView isn't available on tvOS 13") + public static let v13 = Self.unavailable() + @available(*, unavailable, message: "NavigationSplitView isn't available on tvOS 14") + public static let v14 = Self.unavailable() + @available(*, unavailable, message: "NavigationSplitView isn't available on tvOS 15") + public static let v15 = Self.unavailable() + + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + @available(*, unavailable, message: "NavigationSplitView isn't available on macOS 10.15") + public static let v10_15 = Self.unavailable() + @available(*, unavailable, message: "NavigationSplitView isn't available on macOS 11") + public static let v11 = Self.unavailable() + @available(*, unavailable, message: "NavigationSplitView isn't available on macOS 12") + public static let v12 = Self.unavailable() + + public static let v13 = Self(for: .v13) +} +#endif diff --git a/Sources/ViewTypes/NavigationStack.swift b/Sources/ViewTypes/NavigationStack.swift new file mode 100644 index 0000000..7aa1028 --- /dev/null +++ b/Sources/ViewTypes/NavigationStack.swift @@ -0,0 +1,33 @@ +import SwiftUI + +// MARK: SwiftUI.NavigationStack + +public struct NavigationStackType: IntrospectableViewType {} + +extension IntrospectableViewType where Self == NavigationStackType { + public static var navigationStack: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + @available(*, unavailable, message: "NavigationStack isn't available on iOS 13") + public static let v13 = Self.unavailable() + @available(*, unavailable, message: "NavigationStack isn't available on iOS 14") + public static let v14 = Self.unavailable() + @available(*, unavailable, message: "NavigationStack isn't available on iOS 15") + public static let v15 = Self.unavailable() + + public static let v16 = Self(for: .v16) +} + +extension tvOSViewVersion { + @available(*, unavailable, message: "NavigationStack isn't available on tvOS 13") + public static let v13 = Self.unavailable() + @available(*, unavailable, message: "NavigationStack isn't available on tvOS 14") + public static let v14 = Self.unavailable() + @available(*, unavailable, message: "NavigationStack isn't available on tvOS 15") + public static let v15 = Self.unavailable() + + public static let v16 = Self(for: .v16) +} +#endif diff --git a/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift b/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift new file mode 100644 index 0000000..41231a0 --- /dev/null +++ b/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift @@ -0,0 +1,36 @@ +import SwiftUI + +// MARK: SwiftUI.NavigationView { ... }.navigationViewStyle(.columns) + +public struct NavigationViewWithColumnsStyleType: IntrospectableViewType { + public enum Style { + case columns + } +} + +extension IntrospectableViewType where Self == NavigationViewWithColumnsStyleType { + public static func navigationView(style: Self.Style) -> Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} + +extension tvOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif diff --git a/Sources/ViewTypes/NavigationViewWithStackStyle.swift b/Sources/ViewTypes/NavigationViewWithStackStyle.swift new file mode 100644 index 0000000..b6e1083 --- /dev/null +++ b/Sources/ViewTypes/NavigationViewWithStackStyle.swift @@ -0,0 +1,29 @@ +import SwiftUI + +// MARK: SwiftUI.NavigationView { ... }.navigationViewStyle(.stack) + +public struct NavigationViewWithStackStyleType: IntrospectableViewType { + public enum Style { + case stack + } +} + +extension IntrospectableViewType where Self == NavigationViewWithStackStyleType { + public static func navigationView(style: Self.Style) -> Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} + +extension tvOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#endif diff --git a/Sources/ViewTypes/PickerWithMenuStyle.swift b/Sources/ViewTypes/PickerWithMenuStyle.swift new file mode 100644 index 0000000..b8e98c6 --- /dev/null +++ b/Sources/ViewTypes/PickerWithMenuStyle.swift @@ -0,0 +1,25 @@ +#if os(macOS) +import SwiftUI + +// MARK: SwiftUI.Picker { ... }.pickerStyle(.menu) + +public struct PickerWithMenuStyleType: IntrospectableViewType { + public enum Style { + case menu + } +} + +extension IntrospectableViewType where Self == PickerWithMenuStyleType { + public static func picker(style: Self.Style) -> Self { .init() } +} + +#if canImport(AppKit) && !targetEnvironment(macCatalyst) +extension macOSViewVersion { + @available(*, unavailable, message: ".pickerStyle(.menu) isn't available on macOS 10.15") + public static let v10_15 = Self.unavailable() + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif +#endif diff --git a/Sources/ViewTypes/PickerWithSegmentedStyle.swift b/Sources/ViewTypes/PickerWithSegmentedStyle.swift new file mode 100644 index 0000000..e8f93d6 --- /dev/null +++ b/Sources/ViewTypes/PickerWithSegmentedStyle.swift @@ -0,0 +1,36 @@ +import SwiftUI + +// MARK: SwiftUI.Picker { ... }.pickerStyle(.segmented) + +public struct PickerWithSegmentedStyleType: IntrospectableViewType { + public enum Style { + case segmented + } +} + +extension IntrospectableViewType where Self == PickerWithSegmentedStyleType { + public static func picker(style: Self.Style) -> Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} + +extension tvOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif diff --git a/Sources/ViewTypes/PickerWithWheelStyle.swift b/Sources/ViewTypes/PickerWithWheelStyle.swift new file mode 100644 index 0000000..aa97a11 --- /dev/null +++ b/Sources/ViewTypes/PickerWithWheelStyle.swift @@ -0,0 +1,24 @@ +#if os(iOS) +import SwiftUI + +// MARK: SwiftUI.Picker { ... }.pickerStyle(.wheel) + +public struct PickerWithWheelStyleType: IntrospectableViewType { + public enum Style { + case wheel + } +} + +extension IntrospectableViewType where Self == PickerWithWheelStyleType { + public static func picker(style: Self.Style) -> Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#endif +#endif diff --git a/Sources/ViewTypes/ProgressViewWithCircularStyle.swift b/Sources/ViewTypes/ProgressViewWithCircularStyle.swift new file mode 100644 index 0000000..38f017b --- /dev/null +++ b/Sources/ViewTypes/ProgressViewWithCircularStyle.swift @@ -0,0 +1,39 @@ +import SwiftUI + +// MARK: SwiftUI.ProgressView().progressViewStyle(.circular) + +public struct ProgressViewWithCircularStyleType: IntrospectableViewType { + public enum Style { + case circular + } +} + +extension IntrospectableViewType where Self == ProgressViewWithCircularStyleType { + public static func progressView(style: Self.Style) -> Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + @available(*, unavailable, message: ".progressViewStyle(.circular) isn't available on iOS 13") + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} + +extension tvOSViewVersion { + @available(*, unavailable, message: ".progressViewStyle(.circular) isn't available on tvOS 13") + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + @available(*, unavailable, message: ".progressViewStyle(.circular) isn't available on macOS 10.15") + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif diff --git a/Sources/ViewTypes/ProgressViewWithLinearStyle.swift b/Sources/ViewTypes/ProgressViewWithLinearStyle.swift new file mode 100644 index 0000000..9a56d07 --- /dev/null +++ b/Sources/ViewTypes/ProgressViewWithLinearStyle.swift @@ -0,0 +1,39 @@ +import SwiftUI + +// MARK: SwiftUI.ProgressView().progressViewStyle(.linear) + +public struct ProgressViewWithLinearStyleType: IntrospectableViewType { + public enum Style { + case linear + } +} + +extension IntrospectableViewType where Self == ProgressViewWithLinearStyleType { + public static func progressView(style: Self.Style) -> Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + @available(*, unavailable, message: ".progressViewStyle(.linear) isn't available on iOS 13") + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} + +extension tvOSViewVersion { + @available(*, unavailable, message: ".progressViewStyle(.linear) isn't available on tvOS 13") + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + @available(*, unavailable, message: ".progressViewStyle(.linear) isn't available on macOS 10.15") + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif diff --git a/Sources/ViewTypes/ScrollView.swift b/Sources/ViewTypes/ScrollView.swift new file mode 100644 index 0000000..edd6cf7 --- /dev/null +++ b/Sources/ViewTypes/ScrollView.swift @@ -0,0 +1,32 @@ +import SwiftUI + +// MARK: SwiftUI.ScrollView + +public struct ScrollViewType: IntrospectableViewType {} + +extension IntrospectableViewType where Self == ScrollViewType { + public static var scrollView: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} + +extension tvOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif diff --git a/Sources/ViewTypes/SearchField.swift b/Sources/ViewTypes/SearchField.swift new file mode 100644 index 0000000..0b7a06d --- /dev/null +++ b/Sources/ViewTypes/SearchField.swift @@ -0,0 +1,29 @@ +import SwiftUI + +// MARK: SwiftUI.View.searchable(...) + +public struct SearchFieldType: IntrospectableViewType {} + +extension IntrospectableViewType where Self == SearchFieldType { + public static var searchField: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + @available(*, unavailable, message: ".searchable isn't available on iOS 13") + public static let v13 = Self.unavailable() + @available(*, unavailable, message: ".searchable isn't available on iOS 14") + public static let v14 = Self.unavailable() + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} + +extension tvOSViewVersion { + @available(*, unavailable, message: ".searchable isn't available on tvOS 13") + public static let v13 = Self.unavailable() + @available(*, unavailable, message: ".searchable isn't available on tvOS 14") + public static let v14 = Self.unavailable() + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#endif diff --git a/Sources/ViewTypes/Slider.swift b/Sources/ViewTypes/Slider.swift new file mode 100644 index 0000000..2c7d6e1 --- /dev/null +++ b/Sources/ViewTypes/Slider.swift @@ -0,0 +1,27 @@ +#if !os(tvOS) +import SwiftUI + +// MARK: SwiftUI.Slider + +public struct SliderType: IntrospectableViewType {} + +extension IntrospectableViewType where Self == SliderType { + public static var slider: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif +#endif diff --git a/Sources/ViewTypes/Stepper.swift b/Sources/ViewTypes/Stepper.swift new file mode 100644 index 0000000..d4d952a --- /dev/null +++ b/Sources/ViewTypes/Stepper.swift @@ -0,0 +1,27 @@ +#if !os(tvOS) +import SwiftUI + +// MARK: SwiftUI.Stepper + +public struct StepperType: IntrospectableViewType {} + +extension IntrospectableViewType where Self == StepperType { + public static var stepper: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif +#endif diff --git a/Sources/ViewTypes/TabView.swift b/Sources/ViewTypes/TabView.swift new file mode 100644 index 0000000..1f16a7f --- /dev/null +++ b/Sources/ViewTypes/TabView.swift @@ -0,0 +1,32 @@ +import SwiftUI + +// MARK: SwiftUI.TabView + +public struct TabViewType: IntrospectableViewType {} + +extension IntrospectableViewType where Self == TabViewType { + public static var tabView: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} + +extension tvOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif diff --git a/Sources/ViewTypes/TabViewWithPageStyle.swift b/Sources/ViewTypes/TabViewWithPageStyle.swift new file mode 100644 index 0000000..0c904ee --- /dev/null +++ b/Sources/ViewTypes/TabViewWithPageStyle.swift @@ -0,0 +1,33 @@ +#if !os(macOS) +import SwiftUI + +// MARK: SwiftUI.TabView { ... }.tabViewStyle(.page) + +public struct TabViewWithPageStyleType: IntrospectableViewType { + public enum Style { + case page + } +} + +extension IntrospectableViewType where Self == TabViewWithPageStyleType { + public static func tabView(style: Self.Style) -> Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + @available(*, unavailable, message: "TabView {}.tabViewStyle(.page) isn't available on iOS 13") + public static let v13 = Self.unavailable() + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} + +extension tvOSViewVersion { + @available(*, unavailable, message: "TabView {}.tabViewStyle(.page) isn't available on tvOS 13") + public static let v13 = Self.unavailable() + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#endif +#endif diff --git a/Sources/ViewTypes/Table.swift b/Sources/ViewTypes/Table.swift new file mode 100644 index 0000000..4c6aefd --- /dev/null +++ b/Sources/ViewTypes/Table.swift @@ -0,0 +1,32 @@ +#if os(iOS) || os(macOS) +import SwiftUI + +// MARK: SwiftUI.Table + +public struct TableType: IntrospectableViewType {} + +extension IntrospectableViewType where Self == TableType { + public static var table: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + @available(*, unavailable, message: "Table isn't available on iOS 13") + public static let v13 = Self(for: .v13) + @available(*, unavailable, message: "Table isn't available on iOS 14") + public static let v14 = Self(for: .v14) + @available(*, unavailable, message: "Table isn't available on iOS 15") + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) && !targetEnvironment(macCatalyst) +extension macOSViewVersion { + @available(*, unavailable, message: "Table isn't available on macOS 10.15") + public static let v10_15 = Self(for: .v10_15) + @available(*, unavailable, message: "Table isn't available on macOS 11") + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif +#endif diff --git a/Sources/ViewTypes/TextEditor.swift b/Sources/ViewTypes/TextEditor.swift new file mode 100644 index 0000000..629d146 --- /dev/null +++ b/Sources/ViewTypes/TextEditor.swift @@ -0,0 +1,29 @@ +#if !os(tvOS) +import SwiftUI + +// MARK: SwiftUI.TextEditor + +public struct TextEditorType: IntrospectableViewType {} + +extension IntrospectableViewType where Self == TextEditorType { + public static var textEditor: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + @available(*, unavailable, message: "TextEditor isn't available on iOS 13") + public static let v13 = Self.unavailable() + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + @available(*, unavailable, message: "TextEditor isn't available on macOS 10.15") + public static let v10_15 = Self.unavailable() + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif +#endif diff --git a/Sources/ViewTypes/TextField.swift b/Sources/ViewTypes/TextField.swift new file mode 100644 index 0000000..d95d65b --- /dev/null +++ b/Sources/ViewTypes/TextField.swift @@ -0,0 +1,32 @@ +import SwiftUI + +// MARK: SwiftUI.TextField + +public struct TextFieldType: IntrospectableViewType {} + +extension IntrospectableViewType where Self == TextFieldType { + public static var textField: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} + +extension tvOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif diff --git a/Sources/ViewTypes/TextFieldWithVerticalAxis.swift b/Sources/ViewTypes/TextFieldWithVerticalAxis.swift new file mode 100644 index 0000000..579a116 --- /dev/null +++ b/Sources/ViewTypes/TextFieldWithVerticalAxis.swift @@ -0,0 +1,50 @@ +import SwiftUI + +// MARK: SwiftUI.TextField(..., axis: .vertical) + +public struct TextFieldWithVerticalAxisType: IntrospectableViewType { + public enum Axis { + case vertical + } +} + +extension IntrospectableViewType where Self == TextFieldWithVerticalAxisType { + public static func textField(axis: Self.Axis) -> Self { .init() } +} + +// MARK: SwiftUI.TextField(..., axis: .vertical) - iOS + +#if canImport(UIKit) +extension iOSViewVersion { + @available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on iOS 13") + public static let v13 = Self.unavailable() + @available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on iOS 14") + public static let v14 = Self.unavailable() + @available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on iOS 15") + public static let v15 = Self.unavailable() + + public static let v16 = Self(for: .v16) +} + +extension tvOSViewVersion { + @available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on tvOS 13") + public static let v13 = Self.unavailable() + @available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on tvOS 14") + public static let v14 = Self.unavailable() + @available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on tvOS 15") + public static let v15 = Self.unavailable() + + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + @available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on macOS 10.15") + public static let v10_15 = Self.unavailable() + @available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on macOS 11") + public static let v11 = Self.unavailable() + @available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on macOS 12") + public static let v12 = Self.unavailable() + + public static let v13 = Self(for: .v13) +} +#endif diff --git a/Sources/ViewTypes/Toggle.swift b/Sources/ViewTypes/Toggle.swift new file mode 100644 index 0000000..27a0012 --- /dev/null +++ b/Sources/ViewTypes/Toggle.swift @@ -0,0 +1,27 @@ +#if !os(tvOS) +import SwiftUI + +// MARK: SwiftUI.Toggle + +public struct ToggleType: IntrospectableViewType {} + +extension IntrospectableViewType where Self == ToggleType { + public static var toggle: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif +#endif diff --git a/Sources/ViewTypes/ToggleWithButtonStyle.swift b/Sources/ViewTypes/ToggleWithButtonStyle.swift new file mode 100644 index 0000000..b530bcd --- /dev/null +++ b/Sources/ViewTypes/ToggleWithButtonStyle.swift @@ -0,0 +1,26 @@ +#if os(macOS) +import SwiftUI + +// MARK: SwiftUI.Toggle(...).toggleStyle(.button) + +public struct ToggleWithButtonStyleType: IntrospectableViewType { + public enum Style { + case button + } +} + +extension IntrospectableViewType where Self == ToggleWithButtonStyleType { + public static func toggle(style: Self.Style) -> Self { .init() } +} + +#if canImport(AppKit) && !targetEnvironment(macCatalyst) +extension macOSViewVersion { + @available(*, unavailable, message: ".toggleStyle(.button) isn't available on macOS 10.15") + public static let v10_15 = Self.unavailable() + @available(*, unavailable, message: ".toggleStyle(.button) isn't available on macOS 11") + public static let v11 = Self.unavailable() + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif +#endif diff --git a/Sources/ViewTypes/ToggleWithCheckboxStyle.swift b/Sources/ViewTypes/ToggleWithCheckboxStyle.swift new file mode 100644 index 0000000..bb2e01e --- /dev/null +++ b/Sources/ViewTypes/ToggleWithCheckboxStyle.swift @@ -0,0 +1,24 @@ +#if os(macOS) +import SwiftUI + +// MARK: SwiftUI.Toggle(...).toggleStyle(.checkbox) + +public struct ToggleWithCheckboxStyleType: IntrospectableViewType { + public enum Style { + case checkbox + } +} + +extension IntrospectableViewType where Self == ToggleWithCheckboxStyleType { + public static func toggle(style: Self.Style) -> Self { .init() } +} + +#if canImport(AppKit) && !targetEnvironment(macCatalyst) +extension macOSViewVersion { + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif +#endif diff --git a/Sources/ViewTypes/ToggleWithSwitchStyle.swift b/Sources/ViewTypes/ToggleWithSwitchStyle.swift new file mode 100644 index 0000000..f4ca188 --- /dev/null +++ b/Sources/ViewTypes/ToggleWithSwitchStyle.swift @@ -0,0 +1,31 @@ +#if !os(tvOS) +import SwiftUI + +// MARK: SwiftUI.Toggle(...).toggleStyle(.switch) + +public struct ToggleWithSwitchStyleType: IntrospectableViewType { + public enum Style { + case `switch` + } +} + +extension IntrospectableViewType where Self == ToggleWithSwitchStyleType { + public static func toggle(style: Self.Style) -> Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif +#endif diff --git a/Sources/ViewTypes/View.swift b/Sources/ViewTypes/View.swift new file mode 100644 index 0000000..c571e4e --- /dev/null +++ b/Sources/ViewTypes/View.swift @@ -0,0 +1,27 @@ +import SwiftUI + +// MARK: SwiftUI.View + +public struct ViewType: IntrospectableViewType { + public var scope: IntrospectionScope { [.receiver, .ancestor] } +} + +extension IntrospectableViewType where Self == ViewType { + public static var view: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} + +extension tvOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#endif diff --git a/SwiftUIIntrospect.podspec b/SwiftUIIntrospect.podspec new file mode 100644 index 0000000..cf3fa7c --- /dev/null +++ b/SwiftUIIntrospect.podspec @@ -0,0 +1,19 @@ +Pod::Spec.new do |spec| + spec.name = 'SwiftUIIntrospect' + spec.version = ENV['LIB_VERSION'] + spec.license = { type: 'MIT' } + spec.homepage = 'https://github.com/siteline/swiftui-introspect' + spec.author = 'David Roman' + spec.summary = 'Introspect underlying UIKit/AppKit components from SwiftUI.' + spec.source = { + git: 'https://github.com/siteline/swiftui-introspect.git', + tag: spec.version + } + + spec.source_files = 'Sources/**.swift' + + spec.swift_version = '5.7' + spec.ios.deployment_target = '13.0' + spec.tvos.deployment_target = '13.0' + spec.osx.deployment_target = '10.15' +end diff --git a/Tests/Package.swift b/Tests/Package.swift new file mode 100644 index 0000000..a700850 --- /dev/null +++ b/Tests/Package.swift @@ -0,0 +1,9 @@ +// swift-tools-version:5.5 + +import PackageDescription + +let package = Package( + name: "Tests", + products: [], + targets: [] +) diff --git a/Tests/Tests.xcodeproj/project.pbxproj b/Tests/Tests.xcodeproj/project.pbxproj new file mode 100644 index 0000000..a536361 --- /dev/null +++ b/Tests/Tests.xcodeproj/project.pbxproj @@ -0,0 +1,774 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 55; + objects = { + +/* Begin PBXBuildFile section */ + D50FFE8E2A17E2A400C32641 /* ScrollViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D50FFE8D2A17E2A400C32641 /* ScrollViewTests.swift */; }; + D55F448D2A1FF209003381E4 /* ListTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55F448C2A1FF209003381E4 /* ListTests.swift */; }; + D57506782A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506772A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift */; }; + D575067A2A27BF6C00A628E4 /* PickerWithMenuStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506792A27BF6C00A628E4 /* PickerWithMenuStyleTests.swift */; }; + D575067C2A27C24600A628E4 /* ListWithPlainStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D575067B2A27C24600A628E4 /* ListWithPlainStyleTests.swift */; }; + D575067E2A27C43400A628E4 /* ListWithGroupedStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D575067D2A27C43400A628E4 /* ListWithGroupedStyleTests.swift */; }; + D57506802A27C55600A628E4 /* ListWithInsetStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D575067F2A27C55600A628E4 /* ListWithInsetStyleTests.swift */; }; + D57506822A27C74600A628E4 /* ListWithInsetGroupedStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506812A27C74600A628E4 /* ListWithInsetGroupedStyleTests.swift */; }; + D57506842A27C8D400A628E4 /* ListWithSidebarStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506832A27C8D400A628E4 /* ListWithSidebarStyleTests.swift */; }; + D57506862A27CA4100A628E4 /* ListWithBorderedStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506852A27CA4100A628E4 /* ListWithBorderedStyleTests.swift */; }; + D57506882A27CB9800A628E4 /* FormTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506872A27CB9800A628E4 /* FormTests.swift */; }; + D575068A2A27CE7900A628E4 /* FormWithGroupedStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506892A27CE7900A628E4 /* FormWithGroupedStyleTests.swift */; }; + D575068C2A27D40500A628E4 /* ToggleWithSwitchStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D575068B2A27D40500A628E4 /* ToggleWithSwitchStyleTests.swift */; }; + D575068E2A27D4DC00A628E4 /* ToggleWithButtonStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D575068D2A27D4DC00A628E4 /* ToggleWithButtonStyleTests.swift */; }; + D57506902A27D69600A628E4 /* ToggleWithCheckboxStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D575068F2A27D69600A628E4 /* ToggleWithCheckboxStyleTests.swift */; }; + D57506922A27EE4700A628E4 /* DatePickerWithWheelStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506912A27EE4700A628E4 /* DatePickerWithWheelStyleTests.swift */; }; + D57506942A27EED200A628E4 /* DatePickerWithStepperFieldStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506932A27EED200A628E4 /* DatePickerWithStepperFieldStyleTests.swift */; }; + D57506962A27F0E200A628E4 /* DatePickerWithCompactFieldStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506952A27F0E200A628E4 /* DatePickerWithCompactFieldStyleTests.swift */; }; + D57506982A27F32800A628E4 /* DatePickerWithGraphicalStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506972A27F32800A628E4 /* DatePickerWithGraphicalStyleTests.swift */; }; + D575069A2A27F48D00A628E4 /* DatePickerWithFieldStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506992A27F48D00A628E4 /* DatePickerWithFieldStyleTests.swift */; }; + D575069C2A27F68700A628E4 /* ProgressViewWithCircularStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D575069B2A27F68700A628E4 /* ProgressViewWithCircularStyleTests.swift */; }; + D575069E2A27F80E00A628E4 /* ProgressViewWithLinearStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D575069D2A27F80E00A628E4 /* ProgressViewWithLinearStyleTests.swift */; }; + D57506A02A27FC0400A628E4 /* TableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D575069F2A27FC0400A628E4 /* TableTests.swift */; }; + D57506A22A281B9C00A628E4 /* SearchFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506A12A281B9C00A628E4 /* SearchFieldTests.swift */; }; + D58119C42A211B8A0081F853 /* ListCellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119C32A211B8A0081F853 /* ListCellTests.swift */; }; + D58119C62A227E930081F853 /* ViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119C52A227E930081F853 /* ViewTests.swift */; }; + D58119C82A22AC130081F853 /* ToggleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119C72A22AC130081F853 /* ToggleTests.swift */; }; + D58119CA2A239BAC0081F853 /* TextEditorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119C92A239BAC0081F853 /* TextEditorTests.swift */; }; + D58119CC2A239F100081F853 /* TabViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119CB2A239F100081F853 /* TabViewTests.swift */; }; + D58119CE2A23A4A70081F853 /* TabViewWithPageStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119CD2A23A4A70081F853 /* TabViewWithPageStyleTests.swift */; }; + D58119D02A23A62C0081F853 /* SliderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119CF2A23A62C0081F853 /* SliderTests.swift */; }; + D58119D22A23A77C0081F853 /* StepperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119D12A23A77C0081F853 /* StepperTests.swift */; }; + D58119D42A23AC100081F853 /* DatePickerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119D32A23AC100081F853 /* DatePickerTests.swift */; }; + D58119D62A23AED70081F853 /* PickerWithWheelStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119D52A23AED70081F853 /* PickerWithWheelStyleTests.swift */; }; + D58119D82A23B3B00081F853 /* ButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119D72A23B3B00081F853 /* ButtonTests.swift */; }; + D58119DA2A23B7700081F853 /* ColorPickerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119D92A23B7700081F853 /* ColorPickerTests.swift */; }; + D58547F82A1CDD740068ADF4 /* NavigationStackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58547F72A1CDD740068ADF4 /* NavigationStackTests.swift */; }; + D58547FA2A1D12270068ADF4 /* NavigationSplitViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58547F92A1D12270068ADF4 /* NavigationSplitViewTests.swift */; }; + D58CE15629C621B30081BFB0 /* SwiftUIIntrospect in Frameworks */ = {isa = PBXBuildFile; productRef = D58CE15529C621B30081BFB0 /* SwiftUIIntrospect */; }; + D58CE15829C621DD0081BFB0 /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58CE15729C621DD0081BFB0 /* TestUtils.swift */; }; + D5AD0D912A114B98003D8DEC /* TextFieldWithVerticalAxisTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AD0D902A114B98003D8DEC /* TextFieldWithVerticalAxisTests.swift */; }; + D5B67B842A0D318F007D5D9B /* TextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B67B832A0D318F007D5D9B /* TextFieldTests.swift */; }; + D5F0BE4D29C0DBE800AD95AB /* TestsHostApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F0BE4C29C0DBE800AD95AB /* TestsHostApp.swift */; }; + D5F0BE6A29C0DC4900AD95AB /* PlatformTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F0BE6729C0DC4900AD95AB /* PlatformTests.swift */; }; + D5F8D5ED2A1E7B490054E9AB /* NavigationViewWithStackStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F8D5EC2A1E7B490054E9AB /* NavigationViewWithStackStyleTests.swift */; }; + D5F8D5EF2A1E87950054E9AB /* NavigationViewWithColumnsStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F8D5EE2A1E87950054E9AB /* NavigationViewWithColumnsStyleTests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + D5F0BE6129C0DC0000AD95AB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D5F0BE3F29C0DB9700AD95AB /* Project object */; + proxyType = 1; + remoteGlobalIDString = D5F0BE4829C0DBE800AD95AB; + remoteInfo = TestsHostApp; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + D50FFE8D2A17E2A400C32641 /* ScrollViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewTests.swift; sourceTree = ""; }; + D55F448C2A1FF209003381E4 /* ListTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTests.swift; sourceTree = ""; }; + D57506772A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickerWithSegmentedStyleTests.swift; sourceTree = ""; }; + D57506792A27BF6C00A628E4 /* PickerWithMenuStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickerWithMenuStyleTests.swift; sourceTree = ""; }; + D575067B2A27C24600A628E4 /* ListWithPlainStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListWithPlainStyleTests.swift; sourceTree = ""; }; + D575067D2A27C43400A628E4 /* ListWithGroupedStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListWithGroupedStyleTests.swift; sourceTree = ""; }; + D575067F2A27C55600A628E4 /* ListWithInsetStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListWithInsetStyleTests.swift; sourceTree = ""; }; + D57506812A27C74600A628E4 /* ListWithInsetGroupedStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListWithInsetGroupedStyleTests.swift; sourceTree = ""; }; + D57506832A27C8D400A628E4 /* ListWithSidebarStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListWithSidebarStyleTests.swift; sourceTree = ""; }; + D57506852A27CA4100A628E4 /* ListWithBorderedStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListWithBorderedStyleTests.swift; sourceTree = ""; }; + D57506872A27CB9800A628E4 /* FormTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormTests.swift; sourceTree = ""; }; + D57506892A27CE7900A628E4 /* FormWithGroupedStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormWithGroupedStyleTests.swift; sourceTree = ""; }; + D575068B2A27D40500A628E4 /* ToggleWithSwitchStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleWithSwitchStyleTests.swift; sourceTree = ""; }; + D575068D2A27D4DC00A628E4 /* ToggleWithButtonStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleWithButtonStyleTests.swift; sourceTree = ""; }; + D575068F2A27D69600A628E4 /* ToggleWithCheckboxStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleWithCheckboxStyleTests.swift; sourceTree = ""; }; + D57506912A27EE4700A628E4 /* DatePickerWithWheelStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatePickerWithWheelStyleTests.swift; sourceTree = ""; }; + D57506932A27EED200A628E4 /* DatePickerWithStepperFieldStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatePickerWithStepperFieldStyleTests.swift; sourceTree = ""; }; + D57506952A27F0E200A628E4 /* DatePickerWithCompactFieldStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatePickerWithCompactFieldStyleTests.swift; sourceTree = ""; }; + D57506972A27F32800A628E4 /* DatePickerWithGraphicalStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatePickerWithGraphicalStyleTests.swift; sourceTree = ""; }; + D57506992A27F48D00A628E4 /* DatePickerWithFieldStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatePickerWithFieldStyleTests.swift; sourceTree = ""; }; + D575069B2A27F68700A628E4 /* ProgressViewWithCircularStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressViewWithCircularStyleTests.swift; sourceTree = ""; }; + D575069D2A27F80E00A628E4 /* ProgressViewWithLinearStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressViewWithLinearStyleTests.swift; sourceTree = ""; }; + D575069F2A27FC0400A628E4 /* TableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableTests.swift; sourceTree = ""; }; + D57506A12A281B9C00A628E4 /* SearchFieldTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchFieldTests.swift; sourceTree = ""; }; + D58119C32A211B8A0081F853 /* ListCellTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCellTests.swift; sourceTree = ""; }; + D58119C52A227E930081F853 /* ViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewTests.swift; sourceTree = ""; }; + D58119C72A22AC130081F853 /* ToggleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleTests.swift; sourceTree = ""; }; + D58119C92A239BAC0081F853 /* TextEditorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEditorTests.swift; sourceTree = ""; }; + D58119CB2A239F100081F853 /* TabViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabViewTests.swift; sourceTree = ""; }; + D58119CD2A23A4A70081F853 /* TabViewWithPageStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabViewWithPageStyleTests.swift; sourceTree = ""; }; + D58119CF2A23A62C0081F853 /* SliderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SliderTests.swift; sourceTree = ""; }; + D58119D12A23A77C0081F853 /* StepperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepperTests.swift; sourceTree = ""; }; + D58119D32A23AC100081F853 /* DatePickerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatePickerTests.swift; sourceTree = ""; }; + D58119D52A23AED70081F853 /* PickerWithWheelStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickerWithWheelStyleTests.swift; sourceTree = ""; }; + D58119D72A23B3B00081F853 /* ButtonTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonTests.swift; sourceTree = ""; }; + D58119D92A23B7700081F853 /* ColorPickerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickerTests.swift; sourceTree = ""; }; + D58547F72A1CDD740068ADF4 /* NavigationStackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationStackTests.swift; sourceTree = ""; }; + D58547F92A1D12270068ADF4 /* NavigationSplitViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSplitViewTests.swift; sourceTree = ""; }; + D58CE15729C621DD0081BFB0 /* TestUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestUtils.swift; sourceTree = ""; }; + D5AD0D902A114B98003D8DEC /* TextFieldWithVerticalAxisTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldWithVerticalAxisTests.swift; sourceTree = ""; }; + D5B67B832A0D318F007D5D9B /* TextFieldTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldTests.swift; sourceTree = ""; }; + D5F0BE4929C0DBE800AD95AB /* TestsHostApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestsHostApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; + D5F0BE4C29C0DBE800AD95AB /* TestsHostApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestsHostApp.swift; sourceTree = ""; }; + D5F0BE5D29C0DC0000AD95AB /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + D5F0BE6729C0DC4900AD95AB /* PlatformTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlatformTests.swift; sourceTree = ""; }; + D5F8D5EC2A1E7B490054E9AB /* NavigationViewWithStackStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationViewWithStackStyleTests.swift; sourceTree = ""; }; + D5F8D5EE2A1E87950054E9AB /* NavigationViewWithColumnsStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationViewWithColumnsStyleTests.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + D5F0BE4629C0DBE800AD95AB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D5F0BE5A29C0DC0000AD95AB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D58CE15629C621B30081BFB0 /* SwiftUIIntrospect in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + D5B67B852A0D3193007D5D9B /* ViewTypes */ = { + isa = PBXGroup; + children = ( + D58119D72A23B3B00081F853 /* ButtonTests.swift */, + D58119D92A23B7700081F853 /* ColorPickerTests.swift */, + D58119D32A23AC100081F853 /* DatePickerTests.swift */, + D57506952A27F0E200A628E4 /* DatePickerWithCompactFieldStyleTests.swift */, + D57506992A27F48D00A628E4 /* DatePickerWithFieldStyleTests.swift */, + D57506972A27F32800A628E4 /* DatePickerWithGraphicalStyleTests.swift */, + D57506932A27EED200A628E4 /* DatePickerWithStepperFieldStyleTests.swift */, + D57506912A27EE4700A628E4 /* DatePickerWithWheelStyleTests.swift */, + D57506872A27CB9800A628E4 /* FormTests.swift */, + D57506892A27CE7900A628E4 /* FormWithGroupedStyleTests.swift */, + D55F448C2A1FF209003381E4 /* ListTests.swift */, + D57506852A27CA4100A628E4 /* ListWithBorderedStyleTests.swift */, + D575067D2A27C43400A628E4 /* ListWithGroupedStyleTests.swift */, + D57506812A27C74600A628E4 /* ListWithInsetGroupedStyleTests.swift */, + D575067F2A27C55600A628E4 /* ListWithInsetStyleTests.swift */, + D57506832A27C8D400A628E4 /* ListWithSidebarStyleTests.swift */, + D575067B2A27C24600A628E4 /* ListWithPlainStyleTests.swift */, + D58119C32A211B8A0081F853 /* ListCellTests.swift */, + D58547F92A1D12270068ADF4 /* NavigationSplitViewTests.swift */, + D58547F72A1CDD740068ADF4 /* NavigationStackTests.swift */, + D5F8D5EE2A1E87950054E9AB /* NavigationViewWithColumnsStyleTests.swift */, + D5F8D5EC2A1E7B490054E9AB /* NavigationViewWithStackStyleTests.swift */, + D57506792A27BF6C00A628E4 /* PickerWithMenuStyleTests.swift */, + D57506772A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift */, + D58119D52A23AED70081F853 /* PickerWithWheelStyleTests.swift */, + D575069B2A27F68700A628E4 /* ProgressViewWithCircularStyleTests.swift */, + D575069D2A27F80E00A628E4 /* ProgressViewWithLinearStyleTests.swift */, + D50FFE8D2A17E2A400C32641 /* ScrollViewTests.swift */, + D58119CF2A23A62C0081F853 /* SliderTests.swift */, + D58119D12A23A77C0081F853 /* StepperTests.swift */, + D575069F2A27FC0400A628E4 /* TableTests.swift */, + D58119CB2A239F100081F853 /* TabViewTests.swift */, + D58119CD2A23A4A70081F853 /* TabViewWithPageStyleTests.swift */, + D58119C92A239BAC0081F853 /* TextEditorTests.swift */, + D5B67B832A0D318F007D5D9B /* TextFieldTests.swift */, + D57506A12A281B9C00A628E4 /* SearchFieldTests.swift */, + D5AD0D902A114B98003D8DEC /* TextFieldWithVerticalAxisTests.swift */, + D58119C72A22AC130081F853 /* ToggleTests.swift */, + D575068D2A27D4DC00A628E4 /* ToggleWithButtonStyleTests.swift */, + D575068F2A27D69600A628E4 /* ToggleWithCheckboxStyleTests.swift */, + D575068B2A27D40500A628E4 /* ToggleWithSwitchStyleTests.swift */, + D58119C52A227E930081F853 /* ViewTests.swift */, + ); + path = ViewTypes; + sourceTree = ""; + }; + D5F0BE3E29C0DB9700AD95AB = { + isa = PBXGroup; + children = ( + D5F0BE4B29C0DBE800AD95AB /* TestsHostApp */, + D5F0BE5E29C0DC0000AD95AB /* Tests */, + D5F0BE4A29C0DBE800AD95AB /* Products */, + D5F0BE7029C0E12300AD95AB /* Frameworks */, + ); + sourceTree = ""; + }; + D5F0BE4A29C0DBE800AD95AB /* Products */ = { + isa = PBXGroup; + children = ( + D5F0BE4929C0DBE800AD95AB /* TestsHostApp.app */, + D5F0BE5D29C0DC0000AD95AB /* Tests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + D5F0BE4B29C0DBE800AD95AB /* TestsHostApp */ = { + isa = PBXGroup; + children = ( + D5F0BE4C29C0DBE800AD95AB /* TestsHostApp.swift */, + ); + path = TestsHostApp; + sourceTree = ""; + }; + D5F0BE5E29C0DC0000AD95AB /* Tests */ = { + isa = PBXGroup; + children = ( + D5B67B852A0D3193007D5D9B /* ViewTypes */, + D5F0BE6729C0DC4900AD95AB /* PlatformTests.swift */, + D58CE15729C621DD0081BFB0 /* TestUtils.swift */, + ); + path = Tests; + sourceTree = ""; + }; + D5F0BE7029C0E12300AD95AB /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + D5F0BE4829C0DBE800AD95AB /* TestsHostApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = D5F0BE5829C0DBE900AD95AB /* Build configuration list for PBXNativeTarget "TestsHostApp" */; + buildPhases = ( + D5F0BE4529C0DBE800AD95AB /* Sources */, + D5F0BE4629C0DBE800AD95AB /* Frameworks */, + D5F0BE4729C0DBE800AD95AB /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TestsHostApp; + productName = TestsHostApp; + productReference = D5F0BE4929C0DBE800AD95AB /* TestsHostApp.app */; + productType = "com.apple.product-type.application"; + }; + D5F0BE5C29C0DC0000AD95AB /* Tests */ = { + isa = PBXNativeTarget; + buildConfigurationList = D5F0BE6329C0DC0000AD95AB /* Build configuration list for PBXNativeTarget "Tests" */; + buildPhases = ( + D5F0BE5929C0DC0000AD95AB /* Sources */, + D5F0BE5A29C0DC0000AD95AB /* Frameworks */, + D5F0BE5B29C0DC0000AD95AB /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + D5F0BE6229C0DC0000AD95AB /* PBXTargetDependency */, + ); + name = Tests; + packageProductDependencies = ( + D58CE15529C621B30081BFB0 /* SwiftUIIntrospect */, + ); + productName = Tests; + productReference = D5F0BE5D29C0DC0000AD95AB /* Tests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D5F0BE3F29C0DB9700AD95AB /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1420; + LastUpgradeCheck = 1420; + TargetAttributes = { + D5F0BE4829C0DBE800AD95AB = { + CreatedOnToolsVersion = 14.2; + }; + D5F0BE5C29C0DC0000AD95AB = { + CreatedOnToolsVersion = 14.2; + LastSwiftMigration = 1420; + TestTargetID = D5F0BE4829C0DBE800AD95AB; + }; + }; + }; + buildConfigurationList = D5F0BE4229C0DB9700AD95AB /* Build configuration list for PBXProject "Tests" */; + compatibilityVersion = "Xcode 13.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = D5F0BE3E29C0DB9700AD95AB; + productRefGroup = D5F0BE4A29C0DBE800AD95AB /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D5F0BE4829C0DBE800AD95AB /* TestsHostApp */, + D5F0BE5C29C0DC0000AD95AB /* Tests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + D5F0BE4729C0DBE800AD95AB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D5F0BE5B29C0DC0000AD95AB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + D5F0BE4529C0DBE800AD95AB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D5F0BE4D29C0DBE800AD95AB /* TestsHostApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D5F0BE5929C0DC0000AD95AB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D50FFE8E2A17E2A400C32641 /* ScrollViewTests.swift in Sources */, + D58547F82A1CDD740068ADF4 /* NavigationStackTests.swift in Sources */, + D57506982A27F32800A628E4 /* DatePickerWithGraphicalStyleTests.swift in Sources */, + D57506962A27F0E200A628E4 /* DatePickerWithCompactFieldStyleTests.swift in Sources */, + D57506902A27D69600A628E4 /* ToggleWithCheckboxStyleTests.swift in Sources */, + D58119CC2A239F100081F853 /* TabViewTests.swift in Sources */, + D57506802A27C55600A628E4 /* ListWithInsetStyleTests.swift in Sources */, + D575067A2A27BF6C00A628E4 /* PickerWithMenuStyleTests.swift in Sources */, + D57506922A27EE4700A628E4 /* DatePickerWithWheelStyleTests.swift in Sources */, + D57506822A27C74600A628E4 /* ListWithInsetGroupedStyleTests.swift in Sources */, + D575068A2A27CE7900A628E4 /* FormWithGroupedStyleTests.swift in Sources */, + D575067C2A27C24600A628E4 /* ListWithPlainStyleTests.swift in Sources */, + D58119CA2A239BAC0081F853 /* TextEditorTests.swift in Sources */, + D57506842A27C8D400A628E4 /* ListWithSidebarStyleTests.swift in Sources */, + D575069E2A27F80E00A628E4 /* ProgressViewWithLinearStyleTests.swift in Sources */, + D57506862A27CA4100A628E4 /* ListWithBorderedStyleTests.swift in Sources */, + D5F8D5ED2A1E7B490054E9AB /* NavigationViewWithStackStyleTests.swift in Sources */, + D57506942A27EED200A628E4 /* DatePickerWithStepperFieldStyleTests.swift in Sources */, + D5AD0D912A114B98003D8DEC /* TextFieldWithVerticalAxisTests.swift in Sources */, + D58119D02A23A62C0081F853 /* SliderTests.swift in Sources */, + D58119D82A23B3B00081F853 /* ButtonTests.swift in Sources */, + D55F448D2A1FF209003381E4 /* ListTests.swift in Sources */, + D58547FA2A1D12270068ADF4 /* NavigationSplitViewTests.swift in Sources */, + D5F8D5EF2A1E87950054E9AB /* NavigationViewWithColumnsStyleTests.swift in Sources */, + D57506882A27CB9800A628E4 /* FormTests.swift in Sources */, + D58119C82A22AC130081F853 /* ToggleTests.swift in Sources */, + D58119D22A23A77C0081F853 /* StepperTests.swift in Sources */, + D58119DA2A23B7700081F853 /* ColorPickerTests.swift in Sources */, + D575068E2A27D4DC00A628E4 /* ToggleWithButtonStyleTests.swift in Sources */, + D5F0BE6A29C0DC4900AD95AB /* PlatformTests.swift in Sources */, + D58CE15829C621DD0081BFB0 /* TestUtils.swift in Sources */, + D57506782A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift in Sources */, + D58119CE2A23A4A70081F853 /* TabViewWithPageStyleTests.swift in Sources */, + D575069A2A27F48D00A628E4 /* DatePickerWithFieldStyleTests.swift in Sources */, + D57506A02A27FC0400A628E4 /* TableTests.swift in Sources */, + D58119D42A23AC100081F853 /* DatePickerTests.swift in Sources */, + D575068C2A27D40500A628E4 /* ToggleWithSwitchStyleTests.swift in Sources */, + D58119C42A211B8A0081F853 /* ListCellTests.swift in Sources */, + D57506A22A281B9C00A628E4 /* SearchFieldTests.swift in Sources */, + D58119C62A227E930081F853 /* ViewTests.swift in Sources */, + D575067E2A27C43400A628E4 /* ListWithGroupedStyleTests.swift in Sources */, + D575069C2A27F68700A628E4 /* ProgressViewWithCircularStyleTests.swift in Sources */, + D58119D62A23AED70081F853 /* PickerWithWheelStyleTests.swift in Sources */, + D5B67B842A0D318F007D5D9B /* TextFieldTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + D5F0BE6229C0DC0000AD95AB /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D5F0BE4829C0DBE800AD95AB /* TestsHostApp */; + targetProxy = D5F0BE6129C0DC0000AD95AB /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + D5F0BE4329C0DB9700AD95AB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MACOSX_DEPLOYMENT_TARGET = 11.0; + SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator"; + TARGETED_DEVICE_FAMILY = "1,3,2,6"; + TVOS_DEPLOYMENT_TARGET = 13.0; + }; + name = Debug; + }; + D5F0BE4429C0DB9700AD95AB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MACOSX_DEPLOYMENT_TARGET = 11.0; + SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator"; + TARGETED_DEVICE_FAMILY = "1,3,2,6"; + TVOS_DEPLOYMENT_TARGET = 13.0; + }; + name = Release; + }; + D5F0BE5629C0DBE900AD95AB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + "ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = NO; + ENABLE_PREVIEWS = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + INFOPLIST_KEY_UILaunchStoryboardName = ""; + INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.siteline.TestsHostApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + D5F0BE5729C0DBE900AD95AB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + "ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_PREVIEWS = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + INFOPLIST_KEY_UILaunchStoryboardName = ""; + INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.siteline.TestsHostApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + D5F0BE6429C0DC0000AD95AB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Tests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestsHostApp.app/TestsHostApp"; + "TEST_HOST[sdk=macosx*]" = ""; + }; + name = Debug; + }; + D5F0BE6529C0DC0000AD95AB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Tests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestsHostApp.app/TestsHostApp"; + "TEST_HOST[sdk=macosx*]" = ""; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + D5F0BE4229C0DB9700AD95AB /* Build configuration list for PBXProject "Tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D5F0BE4329C0DB9700AD95AB /* Debug */, + D5F0BE4429C0DB9700AD95AB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D5F0BE5829C0DBE900AD95AB /* Build configuration list for PBXNativeTarget "TestsHostApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D5F0BE5629C0DBE900AD95AB /* Debug */, + D5F0BE5729C0DBE900AD95AB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D5F0BE6329C0DC0000AD95AB /* Build configuration list for PBXNativeTarget "Tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D5F0BE6429C0DC0000AD95AB /* Debug */, + D5F0BE6529C0DC0000AD95AB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCSwiftPackageProductDependency section */ + D58CE15529C621B30081BFB0 /* SwiftUIIntrospect */ = { + isa = XCSwiftPackageProductDependency; + productName = SwiftUIIntrospect; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = D5F0BE3F29C0DB9700AD95AB /* Project object */; +} diff --git a/Tests/Tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Tests/Tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Tests/Tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Tests/Tests.xcodeproj/xcshareddata/xcschemes/SwiftUIIntrospectTests.xcscheme b/Tests/Tests.xcodeproj/xcshareddata/xcschemes/SwiftUIIntrospectTests.xcscheme new file mode 100644 index 0000000..5368e40 --- /dev/null +++ b/Tests/Tests.xcodeproj/xcshareddata/xcschemes/SwiftUIIntrospectTests.xcscheme @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/Tests/PlatformTests.swift b/Tests/Tests/PlatformTests.swift new file mode 100644 index 0000000..a41c133 --- /dev/null +++ b/Tests/Tests/PlatformTests.swift @@ -0,0 +1,97 @@ +import SwiftUIIntrospect +import XCTest + +final class PlatformTests: XCTestCase { + func test_iOS() { + #if os(iOS) + if #available(iOS 16, *) { + XCTAssertEqual(iOSVersion.v16.isCurrent, true) + XCTAssertEqual(iOSVersion.v15.isCurrent, false) + XCTAssertEqual(iOSVersion.v14.isCurrent, false) + XCTAssertEqual(iOSVersion.v13.isCurrent, false) + } else if #available(iOS 15, *) { + XCTAssertEqual(iOSVersion.v16.isCurrent, false) + XCTAssertEqual(iOSVersion.v15.isCurrent, true) + XCTAssertEqual(iOSVersion.v14.isCurrent, false) + XCTAssertEqual(iOSVersion.v13.isCurrent, false) + } else if #available(iOS 14, *) { + XCTAssertEqual(iOSVersion.v16.isCurrent, false) + XCTAssertEqual(iOSVersion.v15.isCurrent, false) + XCTAssertEqual(iOSVersion.v14.isCurrent, true) + XCTAssertEqual(iOSVersion.v13.isCurrent, false) + } else if #available(iOS 13, *) { + XCTAssertEqual(iOSVersion.v16.isCurrent, false) + XCTAssertEqual(iOSVersion.v15.isCurrent, false) + XCTAssertEqual(iOSVersion.v14.isCurrent, false) + XCTAssertEqual(iOSVersion.v13.isCurrent, true) + } + #else + XCTAssertEqual(iOSVersion.v16.isCurrent, false) + XCTAssertEqual(iOSVersion.v15.isCurrent, false) + XCTAssertEqual(iOSVersion.v14.isCurrent, false) + XCTAssertEqual(iOSVersion.v13.isCurrent, false) + #endif + } + + func test_macOS() { + #if os(macOS) + if #available(macOS 13, *) { + XCTAssertEqual(macOSVersion.v13.isCurrent, true) + XCTAssertEqual(macOSVersion.v12.isCurrent, false) + XCTAssertEqual(macOSVersion.v11.isCurrent, false) + XCTAssertEqual(macOSVersion.v10_15.isCurrent, false) + } else if #available(macOS 12, *) { + XCTAssertEqual(macOSVersion.v13.isCurrent, false) + XCTAssertEqual(macOSVersion.v12.isCurrent, true) + XCTAssertEqual(macOSVersion.v11.isCurrent, false) + XCTAssertEqual(macOSVersion.v10_15.isCurrent, false) + } else if #available(macOS 11, *) { + XCTAssertEqual(macOSVersion.v13.isCurrent, false) + XCTAssertEqual(macOSVersion.v12.isCurrent, false) + XCTAssertEqual(macOSVersion.v11.isCurrent, true) + XCTAssertEqual(macOSVersion.v10_15.isCurrent, false) + } else if #available(macOS 10.15, *) { + XCTAssertEqual(macOSVersion.v13.isCurrent, false) + XCTAssertEqual(macOSVersion.v12.isCurrent, false) + XCTAssertEqual(macOSVersion.v11.isCurrent, false) + XCTAssertEqual(macOSVersion.v10_15.isCurrent, true) + } + #else + XCTAssertEqual(macOSVersion.v13.isCurrent, false) + XCTAssertEqual(macOSVersion.v12.isCurrent, false) + XCTAssertEqual(macOSVersion.v11.isCurrent, false) + XCTAssertEqual(macOSVersion.v10_15.isCurrent, false) + #endif + } + + func test_tvOS() { + #if os(tvOS) + if #available(tvOS 16, *) { + XCTAssertEqual(tvOSVersion.v16.isCurrent, true) + XCTAssertEqual(tvOSVersion.v15.isCurrent, false) + XCTAssertEqual(tvOSVersion.v14.isCurrent, false) + XCTAssertEqual(tvOSVersion.v13.isCurrent, false) + } else if #available(tvOS 15, *) { + XCTAssertEqual(tvOSVersion.v16.isCurrent, false) + XCTAssertEqual(tvOSVersion.v15.isCurrent, true) + XCTAssertEqual(tvOSVersion.v14.isCurrent, false) + XCTAssertEqual(tvOSVersion.v13.isCurrent, false) + } else if #available(tvOS 14, *) { + XCTAssertEqual(tvOSVersion.v16.isCurrent, false) + XCTAssertEqual(tvOSVersion.v15.isCurrent, false) + XCTAssertEqual(tvOSVersion.v14.isCurrent, true) + XCTAssertEqual(tvOSVersion.v13.isCurrent, false) + } else if #available(tvOS 13, *) { + XCTAssertEqual(tvOSVersion.v16.isCurrent, false) + XCTAssertEqual(tvOSVersion.v15.isCurrent, false) + XCTAssertEqual(tvOSVersion.v14.isCurrent, false) + XCTAssertEqual(tvOSVersion.v13.isCurrent, true) + } + #else + XCTAssertEqual(tvOSVersion.v16.isCurrent, false) + XCTAssertEqual(tvOSVersion.v15.isCurrent, false) + XCTAssertEqual(tvOSVersion.v14.isCurrent, false) + XCTAssertEqual(tvOSVersion.v13.isCurrent, false) + #endif + } +} diff --git a/Tests/Tests/TestUtils.swift b/Tests/Tests/TestUtils.swift new file mode 100644 index 0000000..79a8cf6 --- /dev/null +++ b/Tests/Tests/TestUtils.swift @@ -0,0 +1,130 @@ +import SwiftUI +import XCTest + +#if canImport(UIKit) +enum TestUtils { + enum Constants { + static let timeout: TimeInterval = 3 + } + + static func present(view: some View) { + let hostingController = UIHostingController(rootView: view) + + for window in UIApplication.shared.windows { + if let presentedViewController = window.rootViewController?.presentedViewController { + presentedViewController.dismiss(animated: false, completion: nil) + } + window.isHidden = true + } + + let window = UIWindow(frame: UIScreen.main.bounds) + window.layer.speed = 10 + + hostingController.beginAppearanceTransition(true, animated: false) + window.rootViewController = hostingController + window.makeKeyAndVisible() + window.layoutIfNeeded() + hostingController.endAppearanceTransition() + } +} +#elseif canImport(AppKit) +enum TestUtils { + enum Constants { + static let timeout: TimeInterval = 5 + } + + static func present(view: some View) { + let window = NSWindow( + contentRect: NSRect(x: 0, y: 0, width: 480, height: 300), + styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView], + backing: .buffered, + defer: true + ) + + window.center() + window.setFrameAutosaveName("Main Window") + window.contentView = NSHostingView(rootView: view) + window.makeKeyAndOrderFront(nil) + window.layoutIfNeeded() + } +} +#endif + +func XCTAssertViewIntrospection( + of type: PV.Type, + @ViewBuilder view: (Spies) -> V, + extraAssertions: ([PV]) -> Void = { _ in }, + file: StaticString = #file, + line: UInt = #line +) { + let spies = Spies() + let view = view(spies) + TestUtils.present(view: view) + XCTWaiter(delegate: spies).wait(for: spies.expectations.values.map(\.0), timeout: TestUtils.Constants.timeout) + extraAssertions(spies.objects.sorted(by: { $0.key < $1.key }).map(\.value)) +} + +final class Spies: NSObject, XCTWaiterDelegate { + private(set) var objects: [Int: PV] = [:] + private(set) var expectations: [ObjectIdentifier: (XCTestExpectation, StaticString, UInt)] = [:] + + subscript( + number: Int, + file: StaticString = #file, + line: UInt = #line + ) -> (PV) -> Void { + let expectation = XCTestExpectation() + expectations[ObjectIdentifier(expectation)] = (expectation, file, line) + return { [self] in + if let object = objects[number] { + XCTAssert(object === $0, "Found view was overriden by another view", file: file, line: line) + } + objects[number] = $0 + expectation.fulfill() + } + } + + func waiter( + _ waiter: XCTWaiter, + didTimeoutWithUnfulfilledExpectations unfulfilledExpectations: [XCTestExpectation] + ) { + for expectation in unfulfilledExpectations { + let (_, file, line) = expectations[ObjectIdentifier(expectation)]! + XCTFail("Spy not called", file: file, line: line) + } + } + + func nestedWaiter( + _ waiter: XCTWaiter, + wasInterruptedByTimedOutWaiter outerWaiter: XCTWaiter + ) { + XCTFail("wasInterruptedByTimedOutWaiter") + } + + func waiter( + _ waiter: XCTWaiter, + fulfillmentDidViolateOrderingConstraintsFor expectation: XCTestExpectation, + requiredExpectation: XCTestExpectation + ) { + XCTFail("fulfillmentDidViolateOrderingConstraintsFor") + } + + func waiter( + _ waiter: XCTWaiter, + didFulfillInvertedExpectation expectation: XCTestExpectation + ) { + XCTFail("didFulfillInvertedExpectation") + } +} + +extension Collection { + subscript(safe index: Index, file: StaticString = #file, line: UInt = #line) -> Element? { + get { + guard indices.contains(index) else { + XCTFail("Index \(index) is out of bounds", file: file, line: line) + return nil + } + return self[index] + } + } +} diff --git a/Tests/Tests/ViewTypes/ButtonTests.swift b/Tests/Tests/ViewTypes/ButtonTests.swift new file mode 100644 index 0000000..4bdbb06 --- /dev/null +++ b/Tests/Tests/ViewTypes/ButtonTests.swift @@ -0,0 +1,49 @@ +#if os(macOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class ButtonTests: XCTestCase { + #if canImport(AppKit) + typealias PlatformButton = NSButton + #endif + + func testButton() { + XCTAssertViewIntrospection(of: PlatformButton.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + let spy3 = spies[3] + + VStack { + Button("Button 0", action: {}) + .buttonStyle(.bordered) + #if os(macOS) + .introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + #endif + + Button("Button 1", action: {}) + .buttonStyle(.borderless) + #if os(macOS) + .introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + #endif + + Button("Button 2", action: {}) + .buttonStyle(.link) + #if os(macOS) + .introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + #endif + + Button("Button 3", action: {}) + #if os(macOS) + .introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy3) + #endif + } + } extraAssertions: { + #if canImport(AppKit) + XCTAssert(Set($0.map(ObjectIdentifier.init)).count == 4) + #endif + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/ColorPickerTests.swift b/Tests/Tests/ViewTypes/ColorPickerTests.swift new file mode 100644 index 0000000..87d5d26 --- /dev/null +++ b/Tests/Tests/ViewTypes/ColorPickerTests.swift @@ -0,0 +1,61 @@ +#if !os(tvOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +@available(iOS 14, macOS 11, *) +final class ColorPickerTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformColor = UIColor + typealias PlatformColorPicker = UIColorWell + #elseif canImport(AppKit) + typealias PlatformColor = NSColor + typealias PlatformColorPicker = NSColorWell + #endif + + func testColorPicker() throws { + guard #available(iOS 14, macOS 11, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformColorPicker.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + ColorPicker("", selection: .constant(PlatformColor.red.cgColor)) + #if os(iOS) + .introspect(.colorPicker, on: .iOS(.v14, .v15, .v16), customize: spy0) + #elseif os(macOS) + .introspect(.colorPicker, on: .macOS(.v11, .v12, .v13), customize: spy0) + #endif + + ColorPicker("", selection: .constant(PlatformColor.green.cgColor)) + #if os(iOS) + .introspect(.colorPicker, on: .iOS(.v14, .v15, .v16), customize: spy1) + #elseif os(macOS) + .introspect(.colorPicker, on: .macOS(.v11, .v12, .v13), customize: spy1) + #endif + + ColorPicker("", selection: .constant(PlatformColor.blue.cgColor)) + #if os(iOS) + .introspect(.colorPicker, on: .iOS(.v14, .v15, .v16), customize: spy2) + #elseif os(macOS) + .introspect(.colorPicker, on: .macOS(.v11, .v12, .v13), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(UIKit) + XCTAssertEqual($0[safe: 0]?.selectedColor, .red) + XCTAssertEqual($0[safe: 1]?.selectedColor, .green) + XCTAssertEqual($0[safe: 2]?.selectedColor, .blue) + #elseif canImport(AppKit) + XCTAssertEqual($0[safe: 0]?.color, .red) + XCTAssertEqual($0[safe: 1]?.color, .green) + XCTAssertEqual($0[safe: 2]?.color, .blue) + #endif + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/DatePickerTests.swift b/Tests/Tests/ViewTypes/DatePickerTests.swift new file mode 100644 index 0000000..e2ba293 --- /dev/null +++ b/Tests/Tests/ViewTypes/DatePickerTests.swift @@ -0,0 +1,60 @@ +#if os(iOS) || os(macOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class DatePickerTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformDatePicker = UIDatePicker + #elseif canImport(AppKit) + typealias PlatformDatePicker = NSDatePicker + #endif + + func testDatePicker() { + let date0 = Date(timeIntervalSince1970: 0) + let date1 = Date(timeIntervalSince1970: 5) + let date2 = Date(timeIntervalSince1970: 10) + + XCTAssertViewIntrospection(of: PlatformDatePicker.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + DatePicker("", selection: .constant(date0)) + #if os(iOS) + .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16), customize: spy0) + #elseif os(macOS) + .introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + #endif + .cornerRadius(8) + + DatePicker("", selection: .constant(date1)) + #if os(iOS) + .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16), customize: spy1) + #elseif os(macOS) + .introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + #endif + .cornerRadius(8) + + DatePicker("", selection: .constant(date2)) + #if os(iOS) + .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16), customize: spy2) + #elseif os(macOS) + .introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(UIKit) + XCTAssertEqual($0[safe: 0]?.date, date0) + XCTAssertEqual($0[safe: 1]?.date, date1) + XCTAssertEqual($0[safe: 2]?.date, date2) + #elseif canImport(AppKit) + XCTAssertEqual($0[safe: 0]?.dateValue, date0) + XCTAssertEqual($0[safe: 1]?.dateValue, date1) + XCTAssertEqual($0[safe: 2]?.dateValue, date2) + #endif + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/DatePickerWithCompactFieldStyleTests.swift b/Tests/Tests/ViewTypes/DatePickerWithCompactFieldStyleTests.swift new file mode 100644 index 0000000..9a8e3bb --- /dev/null +++ b/Tests/Tests/ViewTypes/DatePickerWithCompactFieldStyleTests.swift @@ -0,0 +1,68 @@ +#if os(iOS) || os(macOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +@available(iOS 14, macOS 10.15.4, *) +final class DatePickerWithCompactStyleTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformDatePickerWithCompactStyle = UIDatePicker + #elseif canImport(AppKit) + typealias PlatformDatePickerWithCompactStyle = NSDatePicker + #endif + + func testDatePickerWithCompactStyle() throws { + guard #available(iOS 14, macOS 10.15.4, *) else { + throw XCTSkip() + } + + let date0 = Date(timeIntervalSince1970: 0) + let date1 = Date(timeIntervalSince1970: 5) + let date2 = Date(timeIntervalSince1970: 10) + + XCTAssertViewIntrospection(of: PlatformDatePickerWithCompactStyle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + DatePicker("", selection: .constant(date0)) + .datePickerStyle(.compact) + #if os(iOS) + .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16), customize: spy0) + #elseif os(macOS) + .introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13), customize: spy0) + #endif + .cornerRadius(8) + + DatePicker("", selection: .constant(date1)) + .datePickerStyle(.compact) + #if os(iOS) + .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16), customize: spy1) + #elseif os(macOS) + .introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13), customize: spy1) + #endif + .cornerRadius(8) + + DatePicker("", selection: .constant(date2)) + .datePickerStyle(.compact) + #if os(iOS) + .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16), customize: spy2) + #elseif os(macOS) + .introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(UIKit) + XCTAssertEqual($0[safe: 0]?.date, date0) + XCTAssertEqual($0[safe: 1]?.date, date1) + XCTAssertEqual($0[safe: 2]?.date, date2) + #elseif canImport(AppKit) + XCTAssertEqual($0[safe: 0]?.dateValue, date0) + XCTAssertEqual($0[safe: 1]?.dateValue, date1) + XCTAssertEqual($0[safe: 2]?.dateValue, date2) + #endif + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/DatePickerWithFieldStyleTests.swift b/Tests/Tests/ViewTypes/DatePickerWithFieldStyleTests.swift new file mode 100644 index 0000000..a8824f6 --- /dev/null +++ b/Tests/Tests/ViewTypes/DatePickerWithFieldStyleTests.swift @@ -0,0 +1,51 @@ +#if os(macOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class DatePickerWithFieldStyleTests: XCTestCase { + #if canImport(AppKit) && !targetEnvironment(macCatalyst) + typealias PlatformDatePickerWithFieldStyle = NSDatePicker + #endif + + func testDatePickerWithFieldStyle() { + let date0 = Date(timeIntervalSince1970: 0) + let date1 = Date(timeIntervalSince1970: 5) + let date2 = Date(timeIntervalSince1970: 10) + + XCTAssertViewIntrospection(of: PlatformDatePickerWithFieldStyle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + DatePicker("", selection: .constant(date0)) + .datePickerStyle(.field) + #if os(macOS) + .introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + #endif + .cornerRadius(8) + + DatePicker("", selection: .constant(date1)) + .datePickerStyle(.field) + #if os(macOS) + .introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + #endif + .cornerRadius(8) + + DatePicker("", selection: .constant(date2)) + .datePickerStyle(.field) + #if os(macOS) + .introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(AppKit) && !targetEnvironment(macCatalyst) + XCTAssertEqual($0[safe: 0]?.dateValue, date0) + XCTAssertEqual($0[safe: 1]?.dateValue, date1) + XCTAssertEqual($0[safe: 2]?.dateValue, date2) + #endif + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/DatePickerWithGraphicalStyleTests.swift b/Tests/Tests/ViewTypes/DatePickerWithGraphicalStyleTests.swift new file mode 100644 index 0000000..04c9412 --- /dev/null +++ b/Tests/Tests/ViewTypes/DatePickerWithGraphicalStyleTests.swift @@ -0,0 +1,68 @@ +#if os(iOS) || os(macOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +@available(iOS 14, *) +final class DatePickerWithGraphicalStyleTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformDatePickerWithGraphicalStyle = UIDatePicker + #elseif canImport(AppKit) + typealias PlatformDatePickerWithGraphicalStyle = NSDatePicker + #endif + + func testDatePickerWithGraphicalStyle() throws { + guard #available(iOS 14, *) else { + throw XCTSkip() + } + + let date0 = Date(timeIntervalSince1970: 0) + let date1 = Date(timeIntervalSince1970: 3600 * 24 * 1) + let date2 = Date(timeIntervalSince1970: 3600 * 24 * 2) + + XCTAssertViewIntrospection(of: PlatformDatePickerWithGraphicalStyle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + DatePicker("", selection: .constant(date0)) + .datePickerStyle(.graphical) + #if os(iOS) + .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16), customize: spy0) + #elseif os(macOS) + .introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + #endif + .cornerRadius(8) + + DatePicker("", selection: .constant(date1)) + .datePickerStyle(.graphical) + #if os(iOS) + .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16), customize: spy1) + #elseif os(macOS) + .introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + #endif + .cornerRadius(8) + + DatePicker("", selection: .constant(date2)) + .datePickerStyle(.graphical) + #if os(iOS) + .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16), customize: spy2) + #elseif os(macOS) + .introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(UIKit) + XCTAssertEqual($0[safe: 0]?.date, date0) + XCTAssertEqual($0[safe: 1]?.date, date1) + XCTAssertEqual($0[safe: 2]?.date, date2) + #elseif canImport(AppKit) + XCTAssertEqual($0[safe: 0]?.dateValue, date0) + XCTAssertEqual($0[safe: 1]?.dateValue, date1) + XCTAssertEqual($0[safe: 2]?.dateValue, date2) + #endif + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/DatePickerWithStepperFieldStyleTests.swift b/Tests/Tests/ViewTypes/DatePickerWithStepperFieldStyleTests.swift new file mode 100644 index 0000000..d7ce689 --- /dev/null +++ b/Tests/Tests/ViewTypes/DatePickerWithStepperFieldStyleTests.swift @@ -0,0 +1,51 @@ +#if os(macOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class DatePickerWithWheelStyleTests: XCTestCase { + #if canImport(AppKit) && !targetEnvironment(macCatalyst) + typealias PlatformDatePickerWithWheelStyle = NSDatePicker + #endif + + func testDatePickerWithWheelStyle() { + let date0 = Date(timeIntervalSince1970: 0) + let date1 = Date(timeIntervalSince1970: 5) + let date2 = Date(timeIntervalSince1970: 10) + + XCTAssertViewIntrospection(of: PlatformDatePickerWithWheelStyle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + DatePicker("", selection: .constant(date0)) + .datePickerStyle(.stepperField) + #if os(macOS) + .introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + #endif + .cornerRadius(8) + + DatePicker("", selection: .constant(date1)) + .datePickerStyle(.stepperField) + #if os(macOS) + .introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + #endif + .cornerRadius(8) + + DatePicker("", selection: .constant(date2)) + .datePickerStyle(.stepperField) + #if os(macOS) + .introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(AppKit) && !targetEnvironment(macCatalyst) + XCTAssertEqual($0[safe: 0]?.dateValue, date0) + XCTAssertEqual($0[safe: 1]?.dateValue, date1) + XCTAssertEqual($0[safe: 2]?.dateValue, date2) + #endif + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/DatePickerWithWheelStyleTests.swift b/Tests/Tests/ViewTypes/DatePickerWithWheelStyleTests.swift new file mode 100644 index 0000000..7285072 --- /dev/null +++ b/Tests/Tests/ViewTypes/DatePickerWithWheelStyleTests.swift @@ -0,0 +1,51 @@ +#if os(iOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class DatePickerWithWheelStyleTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformDatePickerWithWheelStyle = UIDatePicker + #endif + + func testDatePickerWithWheelStyle() { + let date0 = Date(timeIntervalSince1970: 0) + let date1 = Date(timeIntervalSince1970: 5) + let date2 = Date(timeIntervalSince1970: 10) + + XCTAssertViewIntrospection(of: PlatformDatePickerWithWheelStyle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + DatePicker("", selection: .constant(date0)) + .datePickerStyle(.wheel) + #if os(iOS) + .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16), customize: spy0) + #endif + .cornerRadius(8) + + DatePicker("", selection: .constant(date1)) + .datePickerStyle(.wheel) + #if os(iOS) + .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16), customize: spy1) + #endif + .cornerRadius(8) + + DatePicker("", selection: .constant(date2)) + .datePickerStyle(.wheel) + #if os(iOS) + .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(UIKit) + XCTAssertEqual($0[safe: 0]?.date, date0) + XCTAssertEqual($0[safe: 1]?.date, date1) + XCTAssertEqual($0[safe: 2]?.date, date2) + #endif + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/FormTests.swift b/Tests/Tests/ViewTypes/FormTests.swift new file mode 100644 index 0000000..bca2770 --- /dev/null +++ b/Tests/Tests/ViewTypes/FormTests.swift @@ -0,0 +1,40 @@ +#if !os(macOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class FormTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformForm = UIScrollView // covers both UITableView and UICollectionView + #elseif canImport(AppKit) + typealias PlatformForm = NSScrollView + #endif + + func testForm() throws { + XCTAssertViewIntrospection(of: PlatformForm.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + + HStack { + Form { + Text("Item 1") + } + #if os(iOS) || os(tvOS) + .introspect(.form, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { spy0($0) } + .introspect(.form, on: .iOS(.v16)) { spy0($0) } + #endif + + Form { + Text("Item 1") + #if os(iOS) || os(tvOS) + .introspect(.form, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor) { spy1($0) } + .introspect(.form, on: .iOS(.v16), scope: .ancestor) { spy1($0) } + #endif + } + } + } extraAssertions: { + XCTAssert($0[safe: 0] !== $0[safe: 1]) + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/FormWithGroupedStyleTests.swift b/Tests/Tests/ViewTypes/FormWithGroupedStyleTests.swift new file mode 100644 index 0000000..6451e1a --- /dev/null +++ b/Tests/Tests/ViewTypes/FormWithGroupedStyleTests.swift @@ -0,0 +1,49 @@ +import SwiftUI +import SwiftUIIntrospect +import XCTest + +@available(iOS 16, tvOS 16, macOS 13, *) +final class FormWithGroupedStyleTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformFormWithGroupedStyle = UIScrollView // covers both UITableView and UICollectionView + #elseif canImport(AppKit) + typealias PlatformFormWithGroupedStyle = NSScrollView + #endif + + func testFormWithGroupedStyle() throws { + guard #available(iOS 16, tvOS 16, macOS 13, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformFormWithGroupedStyle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + + HStack { + Form { + Text("Item 1") + } + .formStyle(.grouped) + #if os(iOS) || os(tvOS) + .introspect(.form(style: .grouped), on: .iOS(.v16)) { spy0($0) } + .introspect(.form(style: .grouped), on: .tvOS(.v16)) { spy0($0) } + #elseif os(macOS) + .introspect(.form(style: .grouped), on: .macOS(.v13)) { spy0($0) } + #endif + + Form { + Text("Item 1") + #if os(iOS) || os(tvOS) + .introspect(.form(style: .grouped), on: .iOS(.v16), scope: .ancestor) { spy1($0) } + .introspect(.form(style: .grouped), on: .tvOS(.v16), scope: .ancestor) { spy1($0) } + #elseif os(macOS) + .introspect(.form(style: .grouped), on: .macOS(.v13), scope: .ancestor) { spy1($0) } + #endif + } + .formStyle(.grouped) + } + } extraAssertions: { + XCTAssert($0[safe: 0] !== $0[safe: 1]) + } + } +} diff --git a/Tests/Tests/ViewTypes/ListCellTests.swift b/Tests/Tests/ViewTypes/ListCellTests.swift new file mode 100644 index 0000000..b512f95 --- /dev/null +++ b/Tests/Tests/ViewTypes/ListCellTests.swift @@ -0,0 +1,46 @@ +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class ListCellTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformListCell = UIView // covers both UITableViewCell and UICollectionViewCell + #elseif canImport(AppKit) + typealias PlatformListCell = NSTableCellView + #endif + + func testListCell() { + XCTAssertViewIntrospection(of: PlatformListCell.self) { spies in + let spy = spies[0] + + List { + Text("Item 1") + #if os(iOS) || os(tvOS) + .introspect(.listCell, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { spy($0) } + .introspect(.listCell, on: .iOS(.v16)) { spy($0) } + #elseif os(macOS) + .introspect(.listCell, on: .macOS(.v10_15, .v11, .v12, .v13)) { spy($0) } + #endif + } + } + } + + func testMaskedListCell() { + XCTAssertViewIntrospection(of: PlatformListCell.self) { spies in + let spy = spies[0] + + List { + Text("Item 1") + #if os(iOS) || os(tvOS) + .introspect(.listCell, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { spy($0) } + .introspect(.listCell, on: .iOS(.v16)) { spy($0) } + #elseif os(macOS) + .introspect(.listCell, on: .macOS(.v10_15, .v11, .v12, .v13)) { spy($0) } + #endif + .clipped() + .clipShape(RoundedRectangle(cornerRadius: 20.0)) + .cornerRadius(2.0) + } + } + } +} diff --git a/Tests/Tests/ViewTypes/ListTests.swift b/Tests/Tests/ViewTypes/ListTests.swift new file mode 100644 index 0000000..f196abc --- /dev/null +++ b/Tests/Tests/ViewTypes/ListTests.swift @@ -0,0 +1,103 @@ +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class ListTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformList = UIScrollView // covers both UITableView and UICollectionView + #elseif canImport(AppKit) + typealias PlatformList = NSTableView + #endif + + func testList() { + XCTAssertViewIntrospection(of: PlatformList.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + + HStack { + List { + Text("Item 1") + } + #if os(iOS) || os(tvOS) + .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { spy0($0) } + .introspect(.list, on: .iOS(.v16)) { spy0($0) } + #elseif os(macOS) + .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13)) { spy0($0) } + #endif + + List { + Text("Item 1") + #if os(iOS) || os(tvOS) + .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor) { spy1($0) } + .introspect(.list, on: .iOS(.v16), scope: .ancestor) { spy1($0) } + #elseif os(macOS) + .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13), scope: .ancestor) { spy1($0) } + #endif + } + } + } extraAssertions: { + XCTAssert($0[safe: 0] !== $0[safe: 1]) + } + } + + #if !os(macOS) + func testNestedList() { + XCTAssertViewIntrospection(of: PlatformList.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + + List { + Text("Item 1") + + List { + Text("Item 1") + } + #if os(iOS) || os(tvOS) + .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { spy1($0) } + .introspect(.list, on: .iOS(.v16)) { spy1($0) } + #endif + } + #if os(iOS) || os(tvOS) + .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { spy0($0) } + .introspect(.list, on: .iOS(.v16)) { spy0($0) } + #endif + } extraAssertions: { + XCTAssert($0[safe: 0] !== $0[safe: 1]) + } + } + #endif + + func testMaskedList() { + XCTAssertViewIntrospection(of: PlatformList.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + + HStack { + List { + Text("Item 1") + } + #if os(iOS) || os(tvOS) + .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { spy0($0) } + .introspect(.list, on: .iOS(.v16)) { spy0($0) } + #elseif os(macOS) + .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13)) { spy0($0) } + #endif + .clipped() + .clipShape(RoundedRectangle(cornerRadius: 20.0)) + .cornerRadius(2.0) + + List { + Text("Item 1") + #if os(iOS) || os(tvOS) + .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor) { spy1($0) } + .introspect(.list, on: .iOS(.v16), scope: .ancestor) { spy1($0) } + #elseif os(macOS) + .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13), scope: .ancestor) { spy1($0) } + #endif + } + } + } extraAssertions: { + XCTAssert($0[safe: 0] !== $0[safe: 1]) + } + } +} diff --git a/Tests/Tests/ViewTypes/ListWithBorderedStyleTests.swift b/Tests/Tests/ViewTypes/ListWithBorderedStyleTests.swift new file mode 100644 index 0000000..0aede8f --- /dev/null +++ b/Tests/Tests/ViewTypes/ListWithBorderedStyleTests.swift @@ -0,0 +1,43 @@ +#if os(macOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +@available(macOS 12, *) +final class ListWithBorderedStyleTests: XCTestCase { + #if canImport(AppKit) + typealias PlatformListWithBorderedStyle = NSTableView + #endif + + func testListWithBorderedStyle() throws { + guard #available(macOS 12, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformListWithBorderedStyle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + + HStack { + List { + Text("Item 1") + } + .listStyle(.bordered) + #if os(macOS) + .introspect(.list(style: .bordered), on: .macOS(.v12, .v13)) { spy0($0) } + #endif + + List { + Text("Item 1") + #if os(macOS) + .introspect(.list(style: .bordered), on: .macOS(.v12, .v13), scope: .ancestor) { spy1($0) } + #endif + } + .listStyle(.bordered) + } + } extraAssertions: { + XCTAssert($0[safe: 0] !== $0[safe: 1]) + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/ListWithGroupedStyleTests.swift b/Tests/Tests/ViewTypes/ListWithGroupedStyleTests.swift new file mode 100644 index 0000000..ee388fe --- /dev/null +++ b/Tests/Tests/ViewTypes/ListWithGroupedStyleTests.swift @@ -0,0 +1,40 @@ +#if !os(macOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class ListWithGroupedStyleTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformListWithGroupedStyle = UIScrollView // covers both UITableView and UICollectionView + #endif + + func testListWithGroupedStyle() { + XCTAssertViewIntrospection(of: PlatformListWithGroupedStyle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + + HStack { + List { + Text("Item 1") + } + .listStyle(.grouped) + #if os(iOS) || os(tvOS) + .introspect(.list(style: .grouped), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { spy0($0) } + .introspect(.list(style: .grouped), on: .iOS(.v16)) { spy0($0) } + #endif + + List { + Text("Item 1") + #if os(iOS) || os(tvOS) + .introspect(.list(style: .grouped), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor) { spy1($0) } + .introspect(.list(style: .grouped), on: .iOS(.v16), scope: .ancestor) { spy1($0) } + #endif + } + .listStyle(.grouped) + } + } extraAssertions: { + XCTAssert($0[safe: 0] !== $0[safe: 1]) + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/ListWithInsetGroupedStyleTests.swift b/Tests/Tests/ViewTypes/ListWithInsetGroupedStyleTests.swift new file mode 100644 index 0000000..90f0389 --- /dev/null +++ b/Tests/Tests/ViewTypes/ListWithInsetGroupedStyleTests.swift @@ -0,0 +1,45 @@ +#if os(iOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +@available(iOS 14, *) +final class ListWithInsetGroupedStyleTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformListWithInsetGroupedStyle = UIScrollView // covers both UITableView and UICollectionView + #endif + + func testListWithInsetGroupedStyle() throws { + guard #available(iOS 14, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformListWithInsetGroupedStyle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + + HStack { + List { + Text("Item 1") + } + .listStyle(.insetGrouped) + #if os(iOS) + .introspect(.list(style: .insetGrouped), on: .iOS(.v14, .v15)) { spy0($0) } + .introspect(.list(style: .insetGrouped), on: .iOS(.v16)) { spy0($0) } + #endif + + List { + Text("Item 1") + #if os(iOS) + .introspect(.list(style: .insetGrouped), on: .iOS(.v14, .v15), scope: .ancestor) { spy1($0) } + .introspect(.list(style: .insetGrouped), on: .iOS(.v16), scope: .ancestor) { spy1($0) } + #endif + } + .listStyle(.insetGrouped) + } + } extraAssertions: { + XCTAssert($0[safe: 0] !== $0[safe: 1]) + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/ListWithInsetStyleTests.swift b/Tests/Tests/ViewTypes/ListWithInsetStyleTests.swift new file mode 100644 index 0000000..e7132b8 --- /dev/null +++ b/Tests/Tests/ViewTypes/ListWithInsetStyleTests.swift @@ -0,0 +1,51 @@ +#if !os(tvOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +@available(iOS 14, macOS 11, *) +final class ListWithInsetStyleTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformListWithInsetStyle = UIScrollView // covers both UITableView and UICollectionView + #elseif canImport(AppKit) + typealias PlatformListWithInsetStyle = NSTableView + #endif + + func testListWithInsetStyle() throws { + guard #available(iOS 14, macOS 11, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformListWithInsetStyle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + + HStack { + List { + Text("Item 1") + } + .listStyle(.inset) + #if os(iOS) + .introspect(.list(style: .inset), on: .iOS(.v14, .v15)) { spy0($0) } + .introspect(.list(style: .inset), on: .iOS(.v16)) { spy0($0) } + #elseif os(macOS) + .introspect(.list(style: .inset), on: .macOS(.v11, .v12, .v13)) { spy0($0) } + #endif + + List { + Text("Item 1") + #if os(iOS) + .introspect(.list(style: .inset), on: .iOS(.v14, .v15), scope: .ancestor) { spy1($0) } + .introspect(.list(style: .inset), on: .iOS(.v16), scope: .ancestor) { spy1($0) } + #elseif os(macOS) + .introspect(.list(style: .inset), on: .macOS(.v11, .v12, .v13), scope: .ancestor) { spy1($0) } + #endif + } + .listStyle(.inset) + } + } extraAssertions: { + XCTAssert($0[safe: 0] !== $0[safe: 1]) + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/ListWithPlainStyleTests.swift b/Tests/Tests/ViewTypes/ListWithPlainStyleTests.swift new file mode 100644 index 0000000..87fb267 --- /dev/null +++ b/Tests/Tests/ViewTypes/ListWithPlainStyleTests.swift @@ -0,0 +1,44 @@ +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class ListWithPlainStyleTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformListWithPlainStyle = UIScrollView // covers both UITableView and UICollectionView + #elseif canImport(AppKit) + typealias PlatformListWithPlainStyle = NSTableView + #endif + + func testListWithPlainStyle() { + XCTAssertViewIntrospection(of: PlatformListWithPlainStyle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + + HStack { + List { + Text("Item 1") + } + .listStyle(.plain) + #if os(iOS) || os(tvOS) + .introspect(.list(style: .plain), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { spy0($0) } + .introspect(.list(style: .plain), on: .iOS(.v16)) { spy0($0) } + #elseif os(macOS) + .introspect(.list(style: .plain), on: .macOS(.v10_15, .v11, .v12, .v13)) { spy0($0) } + #endif + + List { + Text("Item 1") + #if os(iOS) || os(tvOS) + .introspect(.list(style: .plain), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor) { spy1($0) } + .introspect(.list(style: .plain), on: .iOS(.v16), scope: .ancestor) { spy1($0) } + #elseif os(macOS) + .introspect(.list(style: .plain), on: .macOS(.v10_15, .v11, .v12, .v13), scope: .ancestor) { spy1($0) } + #endif + } + .listStyle(.plain) + } + } extraAssertions: { + XCTAssert($0[safe: 0] !== $0[safe: 1]) + } + } +} diff --git a/Tests/Tests/ViewTypes/ListWithSidebarStyleTests.swift b/Tests/Tests/ViewTypes/ListWithSidebarStyleTests.swift new file mode 100644 index 0000000..b112e95 --- /dev/null +++ b/Tests/Tests/ViewTypes/ListWithSidebarStyleTests.swift @@ -0,0 +1,51 @@ +#if !os(tvOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +@available(iOS 14, macOS 10.15, *) +final class ListWithSidebarStyleTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformListWithSidebarStyle = UIScrollView // covers both UITableView and UICollectionView + #elseif canImport(AppKit) + typealias PlatformListWithSidebarStyle = NSTableView + #endif + + func testListWithSidebarStyle() throws { + guard #available(iOS 14, macOS 10.15, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformListWithSidebarStyle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + + HStack { + List { + Text("Item 1") + } + .listStyle(.sidebar) + #if os(iOS) + .introspect(.list(style: .sidebar), on: .iOS(.v14, .v15)) { spy0($0) } + .introspect(.list(style: .sidebar), on: .iOS(.v16)) { spy0($0) } + #elseif os(macOS) + .introspect(.list(style: .sidebar), on: .macOS(.v10_15, .v11, .v12, .v13)) { spy0($0) } + #endif + + List { + Text("Item 1") + #if os(iOS) + .introspect(.list(style: .sidebar), on: .iOS(.v14, .v15), scope: .ancestor) { spy1($0) } + .introspect(.list(style: .sidebar), on: .iOS(.v16), scope: .ancestor) { spy1($0) } + #elseif os(macOS) + .introspect(.list(style: .sidebar), on: .macOS(.v10_15, .v11, .v12, .v13), scope: .ancestor) { spy1($0) } + #endif + } + .listStyle(.sidebar) + } + } extraAssertions: { + XCTAssert($0[safe: 0] !== $0[safe: 1]) + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/NavigationSplitViewTests.swift b/Tests/Tests/ViewTypes/NavigationSplitViewTests.swift new file mode 100644 index 0000000..07cb69a --- /dev/null +++ b/Tests/Tests/ViewTypes/NavigationSplitViewTests.swift @@ -0,0 +1,71 @@ +#if !LEGACY_MACOS_SDK +import SwiftUI +import SwiftUIIntrospect +import XCTest + +@available(iOS 16, tvOS 16, macOS 13, *) +final class NavigationSplitViewTests: XCTestCase { + #if canImport(UIKit) && os(iOS) + typealias PlatformNavigationSplitView = UISplitViewController + #elseif canImport(UIKit) && os(tvOS) + typealias PlatformNavigationSplitView = UINavigationController + #elseif canImport(AppKit) + typealias PlatformNavigationSplitView = NSSplitView + #endif + + func testNavigationSplitView() throws { + guard #available(iOS 16, tvOS 16, macOS 13, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformNavigationSplitView.self) { spies in + let spy = spies[0] + + NavigationSplitView { + ZStack { + Color.red + Text("Something") + } + } detail: { + ZStack { + Color.red + Text("Detail") + } + } + #if os(iOS) + .introspect(.navigationSplitView, on: .iOS(.v16), customize: spy) + #elseif os(tvOS) + .introspect(.navigationSplitView, on: .tvOS(.v16), customize: spy) + #elseif os(macOS) + .introspect(.navigationSplitView, on: .macOS(.v13), customize: spy) + #endif + } + } + + func testNavigationSplitViewAsAncestor() throws { + guard #available(iOS 16, tvOS 16, macOS 13, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformNavigationSplitView.self) { spies in + let spy = spies[0] + + NavigationSplitView { + ZStack { + Color.red + Text("Sidebar") + #if os(iOS) + .introspect(.navigationSplitView, on: .iOS(.v16), scope: .ancestor, customize: spy) + #elseif os(tvOS) + .introspect(.navigationSplitView, on: .tvOS(.v16), scope: .ancestor, customize: spy) + #elseif os(macOS) + .introspect(.navigationSplitView, on: .macOS(.v13), scope: .ancestor, customize: spy) + #endif + } + } detail: { + Text("Detail") + } + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/NavigationStackTests.swift b/Tests/Tests/ViewTypes/NavigationStackTests.swift new file mode 100644 index 0000000..a37bb83 --- /dev/null +++ b/Tests/Tests/ViewTypes/NavigationStackTests.swift @@ -0,0 +1,52 @@ +#if !os(macOS) && !LEGACY_MACOS_SDK +import SwiftUI +import SwiftUIIntrospect +import XCTest + +@available(iOS 16, tvOS 16, *) +final class NavigationStackTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformNavigationStack = UINavigationController + #endif + + func testNavigationStack() throws { + guard #available(iOS 16, tvOS 16, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformNavigationStack.self) { spies in + let spy = spies[0] + + NavigationStack { + ZStack { + Color.red + Text("Something") + } + } + #if os(iOS) || os(tvOS) + .introspect(.navigationStack, on: .iOS(.v16), .tvOS(.v16), customize: spy) + #endif + } + } + + func testNavigationStackAsAncestor() throws { + guard #available(iOS 16, tvOS 16, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformNavigationStack.self) { spies in + let spy = spies[0] + + NavigationStack { + ZStack { + Color.red + Text("Something") + #if os(iOS) || os(tvOS) + .introspect(.navigationStack, on: .iOS(.v16), .tvOS(.v16), scope: .ancestor, customize: spy) + #endif + } + } + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/NavigationViewWithColumnsStyleTests.swift b/Tests/Tests/ViewTypes/NavigationViewWithColumnsStyleTests.swift new file mode 100644 index 0000000..2b35df7 --- /dev/null +++ b/Tests/Tests/ViewTypes/NavigationViewWithColumnsStyleTests.swift @@ -0,0 +1,55 @@ +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class NavigationViewWithColumnsStyleTests: XCTestCase { + #if canImport(UIKit) && os(iOS) + typealias PlatformNavigationViewWithColumnsStyle = UISplitViewController + #elseif canImport(UIKit) && os(tvOS) + typealias PlatformNavigationViewWithColumnsStyle = UINavigationController + #elseif canImport(AppKit) + typealias PlatformNavigationViewWithColumnsStyle = NSSplitView + #endif + + func testNavigationViewWithColumnsStyle() { + XCTAssertViewIntrospection(of: PlatformNavigationViewWithColumnsStyle.self) { spies in + let spy = spies[0] + + NavigationView { + ZStack { + Color.red + Text("Something") + } + } + .navigationViewStyle(DoubleColumnNavigationViewStyle()) + #if os(iOS) + .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16), customize: spy) + #elseif os(tvOS) + .introspect(.navigationView(style: .columns), on: .tvOS(.v13, .v14, .v15, .v16), customize: spy) + #elseif os(macOS) + .introspect(.navigationView(style: .columns), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy) + #endif + } + } + + func testNavigationViewWithColumnsStyleAsAncestor() { + XCTAssertViewIntrospection(of: PlatformNavigationViewWithColumnsStyle.self) { spies in + let spy = spies[0] + + NavigationView { + ZStack { + Color.red + Text("Something") + #if os(iOS) + .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16), scope: .ancestor, customize: spy) + #elseif os(tvOS) + .introspect(.navigationView(style: .columns), on: .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor, customize: spy) + #elseif os(macOS) + .introspect(.navigationView(style: .columns), on: .macOS(.v10_15, .v11, .v12, .v13), scope: .ancestor, customize: spy) + #endif + } + } + .navigationViewStyle(DoubleColumnNavigationViewStyle()) + } + } +} diff --git a/Tests/Tests/ViewTypes/NavigationViewWithStackStyleTests.swift b/Tests/Tests/ViewTypes/NavigationViewWithStackStyleTests.swift new file mode 100644 index 0000000..6e89763 --- /dev/null +++ b/Tests/Tests/ViewTypes/NavigationViewWithStackStyleTests.swift @@ -0,0 +1,45 @@ +#if !os(macOS) && !LEGACY_MACOS_SDK +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class NavigationViewWithStackStyleTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformNavigationViewWithStackStyle = UINavigationController + #endif + + func testNavigationViewWithStackStyle() { + XCTAssertViewIntrospection(of: PlatformNavigationViewWithStackStyle.self) { spies in + let spy = spies[0] + + NavigationView { + ZStack { + Color.red + Text("Something") + } + } + .navigationViewStyle(.stack) + #if os(iOS) || os(tvOS) + .introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy) + #endif + } + } + + func testNavigationViewWithStackStyleAsAncestor() { + XCTAssertViewIntrospection(of: PlatformNavigationViewWithStackStyle.self) { spies in + let spy = spies[0] + + NavigationView { + ZStack { + Color.red + Text("Something") + #if os(iOS) || os(tvOS) + .introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor, customize: spy) + #endif + } + } + .navigationViewStyle(.stack) + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/PickerWithMenuStyleTests.swift b/Tests/Tests/ViewTypes/PickerWithMenuStyleTests.swift new file mode 100644 index 0000000..ae17cba --- /dev/null +++ b/Tests/Tests/ViewTypes/PickerWithMenuStyleTests.swift @@ -0,0 +1,56 @@ +#if os(macOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class PickerWithMenuStyleTests: XCTestCase { + #if canImport(AppKit) && !targetEnvironment(macCatalyst) + typealias PlatformPickerWithMenuStyle = NSPopUpButton + #endif + + func testPickerWithMenuStyle() { + XCTAssertViewIntrospection(of: PlatformPickerWithMenuStyle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + Picker("Pick", selection: .constant("1")) { + Text("1").tag("1") + } + .pickerStyle(.menu) + #if os(macOS) + .introspect(.picker(style: .menu), on: .macOS(.v11, .v12, .v13), customize: spy0) + #endif + .cornerRadius(8) + + Picker("Pick", selection: .constant("1")) { + Text("1").tag("1") + Text("2").tag("2") + } + .pickerStyle(.menu) + #if os(macOS) + .introspect(.picker(style: .menu), on: .macOS(.v11, .v12, .v13), customize: spy1) + #endif + .cornerRadius(8) + + Picker("Pick", selection: .constant("1")) { + Text("1").tag("1") + Text("2").tag("2") + Text("3").tag("3") + } + .pickerStyle(.menu) + #if os(macOS) + .introspect(.picker(style: .menu), on: .macOS(.v11, .v12, .v13), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(AppKit) && !targetEnvironment(macCatalyst) + XCTAssertEqual($0[safe: 0]?.numberOfItems, 1) + XCTAssertEqual($0[safe: 1]?.numberOfItems, 2) + XCTAssertEqual($0[safe: 2]?.numberOfItems, 3) + #endif + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/PickerWithSegmentedStyleTests.swift b/Tests/Tests/ViewTypes/PickerWithSegmentedStyleTests.swift new file mode 100644 index 0000000..ae9a97c --- /dev/null +++ b/Tests/Tests/ViewTypes/PickerWithSegmentedStyleTests.swift @@ -0,0 +1,66 @@ +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class PickerWithSegmentedStyleTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformPickerWithSegmentedStyle = UISegmentedControl + #elseif canImport(AppKit) + typealias PlatformPickerWithSegmentedStyle = NSSegmentedControl + #endif + + func testPickerWithSegmentedStyle() { + XCTAssertViewIntrospection(of: PlatformPickerWithSegmentedStyle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + Picker("Pick", selection: .constant("1")) { + Text("1").tag("1") + } + .pickerStyle(.segmented) + #if os(iOS) || os(tvOS) + .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy0) + #elseif os(macOS) + .introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + #endif + .cornerRadius(8) + + Picker("Pick", selection: .constant("1")) { + Text("1").tag("1") + Text("2").tag("2") + } + .pickerStyle(.segmented) + #if os(iOS) || os(tvOS) + .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy1) + #elseif os(macOS) + .introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + #endif + .cornerRadius(8) + + Picker("Pick", selection: .constant("1")) { + Text("1").tag("1") + Text("2").tag("2") + Text("3").tag("3") + } + .pickerStyle(.segmented) + #if os(iOS) || os(tvOS) + .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy2) + #elseif os(macOS) + .introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(UIKit) + XCTAssertEqual($0[safe: 0]?.numberOfSegments, 1) + XCTAssertEqual($0[safe: 1]?.numberOfSegments, 2) + XCTAssertEqual($0[safe: 2]?.numberOfSegments, 3) + #elseif canImport(AppKit) + XCTAssertEqual($0[safe: 0]?.segmentCount, 1) + XCTAssertEqual($0[safe: 1]?.segmentCount, 2) + XCTAssertEqual($0[safe: 2]?.segmentCount, 3) + #endif + } + } +} diff --git a/Tests/Tests/ViewTypes/PickerWithWheelStyleTests.swift b/Tests/Tests/ViewTypes/PickerWithWheelStyleTests.swift new file mode 100644 index 0000000..5915c6f --- /dev/null +++ b/Tests/Tests/ViewTypes/PickerWithWheelStyleTests.swift @@ -0,0 +1,56 @@ +#if os(iOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class PickerWithWheelStyleTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformPickerWithWheelStyle = UIPickerView + #endif + + func testPickerWithWheelStyle() { + XCTAssertViewIntrospection(of: PlatformPickerWithWheelStyle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + Picker("Pick", selection: .constant("1")) { + Text("1").tag("1") + } + .pickerStyle(.wheel) + #if os(iOS) + .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16), customize: spy0) + #endif + .cornerRadius(8) + + Picker("Pick", selection: .constant("1")) { + Text("1").tag("1") + Text("2").tag("2") + } + .pickerStyle(.wheel) + #if os(iOS) + .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16), customize: spy1) + #endif + .cornerRadius(8) + + Picker("Pick", selection: .constant("1")) { + Text("1").tag("1") + Text("2").tag("2") + Text("3").tag("3") + } + .pickerStyle(.wheel) + #if os(iOS) + .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(UIKit) + XCTAssertEqual($0[safe: 0]?.numberOfRows(inComponent: 0), 1) + XCTAssertEqual($0[safe: 1]?.numberOfRows(inComponent: 0), 2) + XCTAssertEqual($0[safe: 2]?.numberOfRows(inComponent: 0), 3) + #endif + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/ProgressViewWithCircularStyleTests.swift b/Tests/Tests/ViewTypes/ProgressViewWithCircularStyleTests.swift new file mode 100644 index 0000000..e990ea7 --- /dev/null +++ b/Tests/Tests/ViewTypes/ProgressViewWithCircularStyleTests.swift @@ -0,0 +1,55 @@ +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class ProgressViewWithCircularStyleTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformProgressViewWithCircularStyle = UIActivityIndicatorView + #elseif canImport(AppKit) + typealias PlatformProgressViewWithCircularStyle = NSProgressIndicator + #endif + + func testProgressViewWithCircularStyle() throws { + guard #available(iOS 14, tvOS 14, macOS 11, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformProgressViewWithCircularStyle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + ProgressView(value: 0.25) + .progressViewStyle(.circular) + #if os(iOS) || os(tvOS) + .introspect(.progressView(style: .circular), on: .iOS(.v14, .v15, .v16), .tvOS(.v14, .v15, .v16), customize: spy0) + #elseif os(macOS) + .introspect(.progressView(style: .circular), on: .macOS(.v11, .v12, .v13), customize: spy0) + #endif + + ProgressView(value: 0.5) + .progressViewStyle(.circular) + #if os(iOS) || os(tvOS) + .introspect(.progressView(style: .circular), on: .iOS(.v14, .v15, .v16), .tvOS(.v14, .v15, .v16), customize: spy1) + #elseif os(macOS) + .introspect(.progressView(style: .circular), on: .macOS(.v11, .v12, .v13), customize: spy1) + #endif + + ProgressView(value: 0.75) + .progressViewStyle(.circular) + #if os(iOS) || os(tvOS) + .introspect(.progressView(style: .circular), on: .iOS(.v14, .v15, .v16), .tvOS(.v14, .v15, .v16), customize: spy2) + #elseif os(macOS) + .introspect(.progressView(style: .circular), on: .macOS(.v11, .v12, .v13), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(AppKit) && !targetEnvironment(macCatalyst) + XCTAssertEqual($0[safe: 0]?.doubleValue, 0.25) + XCTAssertEqual($0[safe: 1]?.doubleValue, 0.5) + XCTAssertEqual($0[safe: 2]?.doubleValue, 0.75) + #endif + } + } +} diff --git a/Tests/Tests/ViewTypes/ProgressViewWithLinearStyleTests.swift b/Tests/Tests/ViewTypes/ProgressViewWithLinearStyleTests.swift new file mode 100644 index 0000000..6041a84 --- /dev/null +++ b/Tests/Tests/ViewTypes/ProgressViewWithLinearStyleTests.swift @@ -0,0 +1,59 @@ +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class ProgressViewWithLinearStyleTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformProgressViewWithLinearStyle = UIProgressView + #elseif canImport(AppKit) + typealias PlatformProgressViewWithLinearStyle = NSProgressIndicator + #endif + + func testProgressViewWithLinearStyle() throws { + guard #available(iOS 14, tvOS 14, macOS 11, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformProgressViewWithLinearStyle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + ProgressView(value: 0.25) + .progressViewStyle(.linear) + #if os(iOS) || os(tvOS) + .introspect(.progressView(style: .linear), on: .iOS(.v14, .v15, .v16), .tvOS(.v14, .v15, .v16), customize: spy0) + #elseif os(macOS) + .introspect(.progressView(style: .linear), on: .macOS(.v11, .v12, .v13), customize: spy0) + #endif + + ProgressView(value: 0.5) + .progressViewStyle(.linear) + #if os(iOS) || os(tvOS) + .introspect(.progressView(style: .linear), on: .iOS(.v14, .v15, .v16), .tvOS(.v14, .v15, .v16), customize: spy1) + #elseif os(macOS) + .introspect(.progressView(style: .linear), on: .macOS(.v11, .v12, .v13), customize: spy1) + #endif + + ProgressView(value: 0.75) + .progressViewStyle(.linear) + #if os(iOS) || os(tvOS) + .introspect(.progressView(style: .linear), on: .iOS(.v14, .v15, .v16), .tvOS(.v14, .v15, .v16), customize: spy2) + #elseif os(macOS) + .introspect(.progressView(style: .linear), on: .macOS(.v11, .v12, .v13), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(UIKit) + XCTAssertEqual($0[safe: 0]?.progress, 0.25) + XCTAssertEqual($0[safe: 1]?.progress, 0.5) + XCTAssertEqual($0[safe: 2]?.progress, 0.75) + #elseif canImport(AppKit) && !targetEnvironment(macCatalyst) + XCTAssertEqual($0[safe: 0]?.doubleValue, 0.25) + XCTAssertEqual($0[safe: 1]?.doubleValue, 0.5) + XCTAssertEqual($0[safe: 2]?.doubleValue, 0.75) + #endif + } + } +} diff --git a/Tests/Tests/ViewTypes/ScrollViewTests.swift b/Tests/Tests/ViewTypes/ScrollViewTests.swift new file mode 100644 index 0000000..682bf71 --- /dev/null +++ b/Tests/Tests/ViewTypes/ScrollViewTests.swift @@ -0,0 +1,132 @@ +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class ScrollViewTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformScrollView = UIScrollView + #elseif canImport(AppKit) + typealias PlatformScrollView = NSScrollView + #endif + + func testScrollView() { + XCTAssertViewIntrospection(of: PlatformScrollView.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + + HStack { + ScrollView(showsIndicators: false) { + Text("Item 1") + } + #if os(iOS) || os(tvOS) + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy0) + #elseif os(macOS) + .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + #endif + + ScrollView(showsIndicators: true) { + Text("Item 1") + #if os(iOS) || os(tvOS) + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor, customize: spy1) + #elseif os(macOS) + .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13), scope: .ancestor, customize: spy1) + #endif + } + } + } extraAssertions: { + #if canImport(UIKit) + XCTAssertEqual($0[safe: 0]?.showsVerticalScrollIndicator, false) + XCTAssertEqual($0[safe: 1]?.showsVerticalScrollIndicator, true) + #elseif canImport(AppKit) + // FIXME: these assertions don't pass on macOS 12, not sure why... maybe callback is too premature in relation to view lifecycle? + if #available(macOS 13, *) { + XCTAssert($0[safe: 0]?.verticalScroller == nil) + XCTAssert($0[safe: 1]?.verticalScroller != nil) + } + #endif + + XCTAssert($0[safe: 0] !== $0[safe: 1]) + } + } + + func testNestedScrollView() { + XCTAssertViewIntrospection(of: PlatformScrollView.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + + ScrollView(showsIndicators: true) { + Text("Item 1") + + ScrollView(showsIndicators: false) { + Text("Item 1") + } + #if os(iOS) || os(tvOS) + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy1) + #elseif os(macOS) + .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + #endif + } + #if os(iOS) || os(tvOS) + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy0) + #elseif os(macOS) + .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + #endif + } extraAssertions: { + #if canImport(UIKit) + XCTAssertEqual($0[safe: 0]?.showsVerticalScrollIndicator, true) + XCTAssertEqual($0[safe: 1]?.showsVerticalScrollIndicator, false) + #elseif canImport(AppKit) + // FIXME: these assertions don't pass on macOS 12, not sure why... maybe callback is too premature in relation to view lifecycle? + if #available(macOS 13, *) { + XCTAssert($0[safe: 0]?.verticalScroller != nil) + XCTAssert($0[safe: 1]?.verticalScroller == nil) + } + #endif + + XCTAssert($0[safe: 0] !== $0[safe: 1]) + } + } + + func testMaskedScrollView() { + XCTAssertViewIntrospection(of: PlatformScrollView.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + + HStack { + ScrollView(showsIndicators: false) { + Text("Item 1") + } + #if os(iOS) || os(tvOS) + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy0) + #elseif os(macOS) + .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + #endif + .clipped() + .clipShape(RoundedRectangle(cornerRadius: 20.0)) + .cornerRadius(2.0) + + ScrollView(showsIndicators: true) { + Text("Item 1") + #if os(iOS) || os(tvOS) + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor, customize: spy1) + #elseif os(macOS) + .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13), scope: .ancestor, customize: spy1) + #endif + } + } + } extraAssertions: { + #if canImport(UIKit) + XCTAssertEqual($0[safe: 0]?.showsVerticalScrollIndicator, false) + XCTAssertEqual($0[safe: 1]?.showsVerticalScrollIndicator, true) + #elseif canImport(AppKit) + // FIXME: these assertions don't pass on macOS 12, not sure why... maybe callback is too premature in relation to view lifecycle? + if #available(macOS 13, *) { + XCTAssert($0[safe: 0]?.verticalScroller == nil) + XCTAssert($0[safe: 1]?.verticalScroller != nil) + } + #endif + + XCTAssert($0[safe: 0] !== $0[safe: 1]) + } + } +} diff --git a/Tests/Tests/ViewTypes/SearchFieldTests.swift b/Tests/Tests/ViewTypes/SearchFieldTests.swift new file mode 100644 index 0000000..b847aab --- /dev/null +++ b/Tests/Tests/ViewTypes/SearchFieldTests.swift @@ -0,0 +1,31 @@ +#if os(iOS) || os(tvOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +@available(iOS 15, tvOS 15, *) +final class SearchFieldTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformSearchField = UISearchBar + #endif + + func testSearchField() throws { + guard #available(iOS 15, tvOS 15, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformSearchField.self) { spies in + let spy = spies[0] + + NavigationView { + Text("Customized") + .searchable(text: .constant("")) + } + .navigationViewStyle(.stack) + #if os(iOS) || os(tvOS) + .introspect(.searchField, on: .iOS(.v15, .v16), .tvOS(.v15, .v16), customize: spy) + #endif + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/SliderTests.swift b/Tests/Tests/ViewTypes/SliderTests.swift new file mode 100644 index 0000000..3221464 --- /dev/null +++ b/Tests/Tests/ViewTypes/SliderTests.swift @@ -0,0 +1,56 @@ +#if !os(tvOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class SliderTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformSlider = UISlider + #elseif canImport(AppKit) + typealias PlatformSlider = NSSlider + #endif + + func testSlider() { + XCTAssertViewIntrospection(of: PlatformSlider.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + Slider(value: .constant(0.2), in: 0...1) + #if os(iOS) + .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16), customize: spy0) + #elseif os(macOS) + .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + #endif + .cornerRadius(8) + + Slider(value: .constant(0.5), in: 0...1) + #if os(iOS) + .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16), customize: spy1) + #elseif os(macOS) + .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + #endif + .cornerRadius(8) + + Slider(value: .constant(0.8), in: 0...1) + #if os(iOS) + .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16), customize: spy2) + #elseif os(macOS) + .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(UIKit) + XCTAssertEqual($0[safe: 0]?.value, 0.2) + XCTAssertEqual($0[safe: 1]?.value, 0.5) + XCTAssertEqual($0[safe: 2]?.value, 0.8) + #elseif canImport(AppKit) + XCTAssertEqual($0[safe: 0]?.floatValue, 0.2) + XCTAssertEqual($0[safe: 1]?.floatValue, 0.5) + XCTAssertEqual($0[safe: 2]?.floatValue, 0.8) + #endif + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/StepperTests.swift b/Tests/Tests/ViewTypes/StepperTests.swift new file mode 100644 index 0000000..211f834 --- /dev/null +++ b/Tests/Tests/ViewTypes/StepperTests.swift @@ -0,0 +1,48 @@ +#if !os(tvOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class StepperTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformStepper = UIStepper + #elseif canImport(AppKit) + typealias PlatformStepper = NSStepper + #endif + + func testStepper() { + XCTAssertViewIntrospection(of: PlatformStepper.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + Stepper("", value: .constant(0), in: 0...10) + #if os(iOS) + .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16), customize: spy0) + #elseif os(macOS) + .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + #endif + .cornerRadius(8) + + Stepper("", value: .constant(0), in: 0...10) + #if os(iOS) + .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16), customize: spy1) + #elseif os(macOS) + .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + #endif + .cornerRadius(8) + + Stepper("", value: .constant(0), in: 0...10) + #if os(iOS) + .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16), customize: spy2) + #elseif os(macOS) + .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + #endif + } + } extraAssertions: { + XCTAssert(Set($0.map(ObjectIdentifier.init)).count == 3) + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/TabViewTests.swift b/Tests/Tests/ViewTypes/TabViewTests.swift new file mode 100644 index 0000000..c17e28a --- /dev/null +++ b/Tests/Tests/ViewTypes/TabViewTests.swift @@ -0,0 +1,49 @@ +#if !LEGACY_MACOS_SDK +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class TabViewTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformTabView = UITabBarController + #elseif canImport(AppKit) + typealias PlatformTabView = NSTabView + #endif + + func testTabView() { + XCTAssertViewIntrospection(of: PlatformTabView.self) { spies in + let spy = spies[0] + + TabView { + ZStack { + Color.red + Text("Something") + } + } + #if os(iOS) || os(tvOS) + .introspect(.tabView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy) + #elseif os(macOS) + .introspect(.tabView, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy) + #endif + } + } + + func testTabViewAsAncestor() { + XCTAssertViewIntrospection(of: PlatformTabView.self) { spies in + let spy = spies[0] + + TabView { + ZStack { + Color.red + Text("Something") + #if os(iOS) || os(tvOS) + .introspect(.tabView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor, customize: spy) + #elseif os(macOS) + .introspect(.tabView, on: .macOS(.v10_15, .v11, .v12, .v13), scope: .ancestor, customize: spy) + #endif + } + } + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/TabViewWithPageStyleTests.swift b/Tests/Tests/ViewTypes/TabViewWithPageStyleTests.swift new file mode 100644 index 0000000..51ac089 --- /dev/null +++ b/Tests/Tests/ViewTypes/TabViewWithPageStyleTests.swift @@ -0,0 +1,52 @@ +#if !os(macOS) && !LEGACY_MACOS_SDK +import SwiftUI +import SwiftUIIntrospect +import XCTest + +@available(iOS 14, tvOS 14, *) +final class TabViewWithPageStyleTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformTabViewWithPageStyle = UICollectionView + #endif + + func testTabViewWithPageStyle() throws { + guard #available(iOS 14, tvOS 14, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformTabViewWithPageStyle.self) { spies in + let spy = spies[0] + + TabView { + ZStack { + Color.red + Text("Something") + } + } + .tabViewStyle(.page) + #if os(iOS) || os(tvOS) + .introspect(.tabView(style: .page), on: .iOS(.v14, .v15, .v16), .tvOS(.v14, .v15, .v16), customize: spy) + #endif + } + } + + func testTabViewWithPageStyleAsAncestor() throws { + guard #available(iOS 14, tvOS 14, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformTabViewWithPageStyle.self) { spies in + let spy = spies[0] + + TabView { + ZStack { Color.red; Text("1") } + #if os(iOS) || os(tvOS) + .introspect(.tabView(style: .page), on: .iOS(.v14, .v15, .v16), .tvOS(.v14, .v15, .v16), scope: .ancestor, customize: spy) + #endif + ZStack { Color.green; Text("2") } + } + .tabViewStyle(.page) + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/TableTests.swift b/Tests/Tests/ViewTypes/TableTests.swift new file mode 100644 index 0000000..7b55c03 --- /dev/null +++ b/Tests/Tests/ViewTypes/TableTests.swift @@ -0,0 +1,152 @@ +#if os(iOS) || os(macOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +@available(iOS 16, macOS 12, *) +final class TableTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformTable = UICollectionView + #elseif canImport(AppKit) + typealias PlatformTable = NSTableView + #endif + + func testTable() throws { + guard #available(iOS 16, macOS 12, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformTable.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + TipTable() + #if os(iOS) + .introspect(.table, on: .iOS(.v16), customize: spy0) + #elseif os(macOS) + .introspect(.table, on: .macOS(.v12, .v13), customize: spy0) + #endif + + TipTable() + #if os(iOS) + .introspect(.table, on: .iOS(.v16), customize: spy1) + #elseif os(macOS) + .introspect(.table, on: .macOS(.v12, .v13), customize: spy1) + #endif + + TipTable() + #if os(iOS) + .introspect(.table, on: .iOS(.v16), customize: spy2) + #elseif os(macOS) + .introspect(.table, on: .macOS(.v12, .v13), customize: spy2) + #endif + } + } + } + + func testTableWithInsetStyle() throws { + guard #available(iOS 16, macOS 12, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformTable.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + TipTable() + .tableStyle(.inset) + #if os(iOS) + .introspect(.table, on: .iOS(.v16), customize: spy0) + #elseif os(macOS) + .introspect(.table, on: .macOS(.v12, .v13), customize: spy0) + #endif + + TipTable() + .tableStyle(.inset) + #if os(iOS) + .introspect(.table, on: .iOS(.v16), customize: spy1) + #elseif os(macOS) + .introspect(.table, on: .macOS(.v12, .v13), customize: spy1) + #endif + + TipTable() + .tableStyle(.inset) + #if os(iOS) + .introspect(.table, on: .iOS(.v16), customize: spy2) + #elseif os(macOS) + .introspect(.table, on: .macOS(.v12, .v13), customize: spy2) + #endif + } + } + } + + #if os(macOS) + func testTableWithBorderedStyle() throws { + guard #available(macOS 12, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformTable.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + TipTable() + .tableStyle(.bordered) + #if os(macOS) + .introspect(.table, on: .macOS(.v12, .v13), customize: spy0) + #endif + + TipTable() + .tableStyle(.bordered) + #if os(macOS) + .introspect(.table, on: .macOS(.v12, .v13), customize: spy1) + #endif + + TipTable() + .tableStyle(.bordered) + #if os(macOS) + .introspect(.table, on: .macOS(.v12, .v13), customize: spy2) + #endif + } + } + } + #endif +} + +@available(iOS 16, macOS 12, *) +extension TableTests { + struct TipTable: View { + struct Purchase: Identifiable { + let price: Decimal + let id = UUID() + } + + var body: some View { + Table(of: Purchase.self) { + TableColumn("Base price") { purchase in + Text(purchase.price, format: .currency(code: "USD")) + } + TableColumn("With 15% tip") { purchase in + Text(purchase.price * 1.15, format: .currency(code: "USD")) + } + TableColumn("With 20% tip") { purchase in + Text(purchase.price * 1.2, format: .currency(code: "USD")) + } + TableColumn("With 25% tip") { purchase in + Text(purchase.price * 1.25, format: .currency(code: "USD")) + } + } rows: { + TableRow(Purchase(price: 20)) + TableRow(Purchase(price: 50)) + TableRow(Purchase(price: 75)) + } + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/TextEditorTests.swift b/Tests/Tests/ViewTypes/TextEditorTests.swift new file mode 100644 index 0000000..b89047d --- /dev/null +++ b/Tests/Tests/ViewTypes/TextEditorTests.swift @@ -0,0 +1,61 @@ +#if !os(tvOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +@available(iOS 14, macOS 11, *) +final class TextEditorTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformTextEditor = UITextView + #elseif canImport(AppKit) + typealias PlatformTextEditor = NSTextView + #endif + + func testTextEditor() throws { + guard #available(iOS 14, macOS 11, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformTextEditor.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + TextEditor(text: .constant("Text Field 0")) + #if os(iOS) + .introspect(.textEditor, on: .iOS(.v14, .v15, .v16), customize: spy0) + #elseif os(macOS) + .introspect(.textEditor, on: .macOS(.v11, .v12, .v13), customize: spy0) + #endif + .cornerRadius(8) + + TextEditor(text: .constant("Text Field 1")) + #if os(iOS) + .introspect(.textEditor, on: .iOS(.v14, .v15, .v16), customize: spy1) + #elseif os(macOS) + .introspect(.textEditor, on: .macOS(.v11, .v12, .v13), customize: spy1) + #endif + .cornerRadius(8) + + TextEditor(text: .constant("Text Field 2")) + #if os(iOS) + .introspect(.textEditor, on: .iOS(.v14, .v15, .v16), customize: spy2) + #elseif os(macOS) + .introspect(.textEditor, on: .macOS(.v11, .v12, .v13), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(UIKit) + XCTAssertEqual($0[safe: 0]?.text, "Text Field 0") + XCTAssertEqual($0[safe: 1]?.text, "Text Field 1") + XCTAssertEqual($0[safe: 2]?.text, "Text Field 2") + #elseif canImport(AppKit) + XCTAssertEqual($0[safe: 0]?.string, "Text Field 0") + XCTAssertEqual($0[safe: 1]?.string, "Text Field 1") + XCTAssertEqual($0[safe: 2]?.string, "Text Field 2") + #endif + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/TextFieldTests.swift b/Tests/Tests/ViewTypes/TextFieldTests.swift new file mode 100644 index 0000000..b5126db --- /dev/null +++ b/Tests/Tests/ViewTypes/TextFieldTests.swift @@ -0,0 +1,95 @@ +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class TextFieldTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformTextField = UITextField + #elseif canImport(AppKit) + typealias PlatformTextField = NSTextField + #endif + + func testTextField() { + XCTAssertViewIntrospection(of: PlatformTextField.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + TextField("", text: .constant("Text Field 0")) + #if os(iOS) || os(tvOS) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy0) + #elseif os(macOS) + .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + #endif + .cornerRadius(8) + + TextField("", text: .constant("Text Field 1")) + #if os(iOS) || os(tvOS) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy1) + #elseif os(macOS) + .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + #endif + .cornerRadius(8) + + TextField("", text: .constant("Text Field 2")) + #if os(iOS) || os(tvOS) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy2) + #elseif os(macOS) + .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(UIKit) + XCTAssertEqual($0[safe: 0]?.text, "Text Field 0") + XCTAssertEqual($0[safe: 1]?.text, "Text Field 1") + XCTAssertEqual($0[safe: 2]?.text, "Text Field 2") + #elseif canImport(AppKit) + XCTAssertEqual($0[safe: 0]?.stringValue, "Text Field 0") + XCTAssertEqual($0[safe: 1]?.stringValue, "Text Field 1") + XCTAssertEqual($0[safe: 2]?.stringValue, "Text Field 2") + #endif + } + } + + func testTextFieldsEmbeddedInList() { + XCTAssertViewIntrospection(of: PlatformTextField.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + List { + TextField("", text: .constant("Text Field 0")) + #if os(iOS) || os(tvOS) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy0) + #elseif os(macOS) + .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + #endif + + TextField("", text: .constant("Text Field 1")) + #if os(iOS) || os(tvOS) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy1) + #elseif os(macOS) + .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + #endif + + TextField("", text: .constant("Text Field 2")) + #if os(iOS) || os(tvOS) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy2) + #elseif os(macOS) + .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(UIKit) + XCTAssertEqual($0[safe: 0]?.text, "Text Field 0") + XCTAssertEqual($0[safe: 1]?.text, "Text Field 1") + XCTAssertEqual($0[safe: 2]?.text, "Text Field 2") + #elseif canImport(AppKit) + XCTAssertEqual($0[safe: 0]?.stringValue, "Text Field 0") + XCTAssertEqual($0[safe: 1]?.stringValue, "Text Field 1") + XCTAssertEqual($0[safe: 2]?.stringValue, "Text Field 2") + #endif + } + } +} diff --git a/Tests/Tests/ViewTypes/TextFieldWithVerticalAxisTests.swift b/Tests/Tests/ViewTypes/TextFieldWithVerticalAxisTests.swift new file mode 100644 index 0000000..cd877fa --- /dev/null +++ b/Tests/Tests/ViewTypes/TextFieldWithVerticalAxisTests.swift @@ -0,0 +1,69 @@ +#if !LEGACY_MACOS_SDK +import SwiftUI +import SwiftUIIntrospect +import XCTest + +@available(iOS 16, tvOS 16, macOS 13, *) +final class TextFieldWithVerticalAxisTests: XCTestCase { + #if canImport(UIKit) && os(iOS) + typealias PlatformTextField = UITextView + #elseif canImport(UIKit) && os(tvOS) + typealias PlatformTextField = UITextField + #elseif canImport(AppKit) + typealias PlatformTextField = NSTextField + #endif + + func testTextFieldWithVerticalAxis() throws { + guard #available(iOS 16, tvOS 16, macOS 13, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformTextField.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + TextField("", text: .constant("Text Field 1"), axis: .vertical) + #if os(iOS) + .introspect(.textField(axis: .vertical), on: .iOS(.v16), customize: spy0) + #elseif os(tvOS) + .introspect(.textField(axis: .vertical), on: .tvOS(.v16), customize: spy0) + #elseif os(macOS) + .introspect(.textField(axis: .vertical), on: .macOS(.v13), customize: spy0) + #endif + .cornerRadius(8) + + TextField("", text: .constant("Text Field 2"), axis: .vertical) + #if os(iOS) + .introspect(.textField(axis: .vertical), on: .iOS(.v16), customize: spy1) + #elseif os(tvOS) + .introspect(.textField(axis: .vertical), on: .tvOS(.v16), customize: spy1) + #elseif os(macOS) + .introspect(.textField(axis: .vertical), on: .macOS(.v13), customize: spy1) + #endif + .cornerRadius(8) + + TextField("", text: .constant("Text Field 3"), axis: .vertical) + #if os(iOS) + .introspect(.textField(axis: .vertical), on: .iOS(.v16), customize: spy2) + #elseif os(tvOS) + .introspect(.textField(axis: .vertical), on: .tvOS(.v16), customize: spy2) + #elseif os(macOS) + .introspect(.textField(axis: .vertical), on: .macOS(.v13), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(UIKit) + XCTAssertEqual($0[safe: 0]?.text, "Text Field 1") + XCTAssertEqual($0[safe: 1]?.text, "Text Field 2") + XCTAssertEqual($0[safe: 2]?.text, "Text Field 3") + #elseif canImport(AppKit) + XCTAssertEqual($0[safe: 0]?.stringValue, "Text Field 1") + XCTAssertEqual($0[safe: 1]?.stringValue, "Text Field 2") + XCTAssertEqual($0[safe: 2]?.stringValue, "Text Field 3") + #endif + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/ToggleTests.swift b/Tests/Tests/ViewTypes/ToggleTests.swift new file mode 100644 index 0000000..8ffb641 --- /dev/null +++ b/Tests/Tests/ViewTypes/ToggleTests.swift @@ -0,0 +1,54 @@ +#if !os(tvOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class ToggleTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformToggle = UISwitch + #elseif canImport(AppKit) + typealias PlatformToggle = NSButton + #endif + + func testToggle() { + XCTAssertViewIntrospection(of: PlatformToggle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + Toggle("", isOn: .constant(true)) + #if os(iOS) + .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16), customize: spy0) + #elseif os(macOS) + .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + #endif + + Toggle("", isOn: .constant(false)) + #if os(iOS) + .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16), customize: spy1) + #elseif os(macOS) + .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + #endif + + Toggle("", isOn: .constant(true)) + #if os(iOS) + .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16), customize: spy2) + #elseif os(macOS) + .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(UIKit) + XCTAssertEqual($0[safe: 0]?.isOn, true) + XCTAssertEqual($0[safe: 1]?.isOn, false) + XCTAssertEqual($0[safe: 2]?.isOn, true) + #elseif canImport(AppKit) + XCTAssertEqual($0[safe: 0]?.state, .on) + XCTAssertEqual($0[safe: 1]?.state, .off) + XCTAssertEqual($0[safe: 2]?.state, .on) + #endif + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/ToggleWithButtonStyleTests.swift b/Tests/Tests/ViewTypes/ToggleWithButtonStyleTests.swift new file mode 100644 index 0000000..9636d93 --- /dev/null +++ b/Tests/Tests/ViewTypes/ToggleWithButtonStyleTests.swift @@ -0,0 +1,50 @@ +#if os(macOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +@available(macOS 12, *) +final class ToggleWithButtonStyleTests: XCTestCase { + #if canImport(AppKit) && !targetEnvironment(macCatalyst) + typealias PlatformToggleWithButtonStyle = NSButton + #endif + + func testToggleWithButtonStyle() throws { + guard #available(macOS 12, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformToggleWithButtonStyle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + Toggle("", isOn: .constant(true)) + .toggleStyle(.button) + #if os(macOS) + .introspect(.toggle(style: .button), on: .macOS(.v12, .v13), customize: spy0) + #endif + + Toggle("", isOn: .constant(false)) + .toggleStyle(.button) + #if os(macOS) + .introspect(.toggle(style: .button), on: .macOS(.v12, .v13), customize: spy1) + #endif + + Toggle("", isOn: .constant(true)) + .toggleStyle(.button) + #if os(macOS) + .introspect(.toggle(style: .button), on: .macOS(.v12, .v13), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(AppKit) && !targetEnvironment(macCatalyst) + XCTAssertEqual($0[safe: 0]?.state, .on) + XCTAssertEqual($0[safe: 1]?.state, .off) + XCTAssertEqual($0[safe: 2]?.state, .on) + #endif + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/ToggleWithCheckboxStyleTests.swift b/Tests/Tests/ViewTypes/ToggleWithCheckboxStyleTests.swift new file mode 100644 index 0000000..855ebf3 --- /dev/null +++ b/Tests/Tests/ViewTypes/ToggleWithCheckboxStyleTests.swift @@ -0,0 +1,45 @@ +#if os(macOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class ToggleWithCheckboxStyleTests: XCTestCase { + #if canImport(AppKit) && !targetEnvironment(macCatalyst) + typealias PlatformToggleWithCheckboxStyle = NSButton + #endif + + func testToggleWithCheckboxStyle() throws { + XCTAssertViewIntrospection(of: PlatformToggleWithCheckboxStyle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + Toggle("", isOn: .constant(true)) + .toggleStyle(.checkbox) + #if os(macOS) + .introspect(.toggle(style: .checkbox), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + #endif + + Toggle("", isOn: .constant(false)) + .toggleStyle(.checkbox) + #if os(macOS) + .introspect(.toggle(style: .checkbox), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + #endif + + Toggle("", isOn: .constant(true)) + .toggleStyle(.checkbox) + #if os(macOS) + .introspect(.toggle(style: .checkbox), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(AppKit) && !targetEnvironment(macCatalyst) + XCTAssertEqual($0[safe: 0]?.state, .on) + XCTAssertEqual($0[safe: 1]?.state, .off) + XCTAssertEqual($0[safe: 2]?.state, .on) + #endif + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/ToggleWithSwitchStyleTests.swift b/Tests/Tests/ViewTypes/ToggleWithSwitchStyleTests.swift new file mode 100644 index 0000000..a1daddf --- /dev/null +++ b/Tests/Tests/ViewTypes/ToggleWithSwitchStyleTests.swift @@ -0,0 +1,57 @@ +#if !os(tvOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class ToggleWithSwitchStyleTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformToggleWithSwitchStyle = UISwitch + #elseif canImport(AppKit) + typealias PlatformToggleWithSwitchStyle = NSSwitch + #endif + + func testToggleWithSwitchStyle() { + XCTAssertViewIntrospection(of: PlatformToggleWithSwitchStyle.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + Toggle("", isOn: .constant(true)) + .toggleStyle(.switch) + #if os(iOS) + .introspect(.toggle(style: .switch), on: .iOS(.v13, .v14, .v15, .v16), customize: spy0) + #elseif os(macOS) + .introspect(.toggle(style: .switch), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + #endif + + Toggle("", isOn: .constant(false)) + .toggleStyle(.switch) + #if os(iOS) + .introspect(.toggle(style: .switch), on: .iOS(.v13, .v14, .v15, .v16), customize: spy1) + #elseif os(macOS) + .introspect(.toggle(style: .switch), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + #endif + + Toggle("", isOn: .constant(true)) + .toggleStyle(.switch) + #if os(iOS) + .introspect(.toggle(style: .switch), on: .iOS(.v13, .v14, .v15, .v16), customize: spy2) + #elseif os(macOS) + .introspect(.toggle(style: .switch), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(UIKit) + XCTAssertEqual($0[safe: 0]?.isOn, true) + XCTAssertEqual($0[safe: 1]?.isOn, false) + XCTAssertEqual($0[safe: 2]?.isOn, true) + #elseif canImport(AppKit) + XCTAssertEqual($0[safe: 0]?.state, .on) + XCTAssertEqual($0[safe: 1]?.state, .off) + XCTAssertEqual($0[safe: 2]?.state, .on) + #endif + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/ViewTests.swift b/Tests/Tests/ViewTypes/ViewTests.swift new file mode 100644 index 0000000..5114597 --- /dev/null +++ b/Tests/Tests/ViewTypes/ViewTests.swift @@ -0,0 +1,32 @@ +#if !os(macOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class ViewTests: XCTestCase { + func testView() { + XCTAssertViewIntrospection(of: PlatformViewController.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + + VStack { + NavigationView { + Text("Item 0") + #if os(iOS) || os(tvOS) + .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy0) + #endif + } + + NavigationView { + Text("Item 1") + #if os(iOS) || os(tvOS) + .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy1) + #endif + } + } + } extraAssertions: { + XCTAssert($0[safe: 0] !== $0[safe: 1]) + } + } +} +#endif diff --git a/Tests/TestsHostApp/TestsHostApp.swift b/Tests/TestsHostApp/TestsHostApp.swift new file mode 100644 index 0000000..02cd701 --- /dev/null +++ b/Tests/TestsHostApp/TestsHostApp.swift @@ -0,0 +1,40 @@ +import SwiftUI + +#if os(iOS) || os(tvOS) +@UIApplicationMain +final class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + window = UIWindow(frame: UIScreen.main.bounds) + window?.rootViewController = UIHostingController(rootView: AppView()) + window?.makeKeyAndVisible() + return true + } +} +#elseif os(macOS) +@main +struct App: SwiftUI.App { + var body: some Scene { + WindowGroup { + AppView() + } + } +} +#endif + +struct AppView: View { + var body: some View { + VStack(spacing: 20) { + Text("Host App for Tests").bold() + Text("This is just an app target to run tests against, needed for iOS 13 compatibility.") + Text("If iOS 13 support is dropped in the future, this target can and should be removed and tests should be ran using SPM instead.") + } + .multilineTextAlignment(.center) + .padding() + #if os(macOS) + .fixedSize() + #endif + } +} diff --git a/docs/SwiftUIIntrospect.md b/docs/SwiftUIIntrospect.md new file mode 100644 index 0000000..a8e0650 --- /dev/null +++ b/docs/SwiftUIIntrospect.md @@ -0,0 +1,201 @@ +SwiftUIIntrospect +================= + +[![CI Status Badge](https://github.com/siteline/SwiftUI-Introspect/actions/workflows/ci.yml/badge.svg)](https://github.com/siteline/SwiftUI-Introspect/actions/workflows/ci.yml) +[![Platform Compatibility Badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fsiteline%2FSwiftUI-Introspect%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/siteline/SwiftUI-Introspect) + +> **Note** +> +> `SwiftUIIntrospect` is an all-new module based off the original `Introspect` module that improves on stability, predictability, and ergonomics. +> +> Both modules currently live together under this repo, but the plan is to ultimately obsolete `Introspect` in favor of `SwiftUIIntrospect` as part of a 1.0 release. +> +> While `Introspect` supports Swift 5.5 or higher, `SwiftUIIntrospect` requires Swift 5.7 or higher due to the use of more recent language features which partially enable the aforementioned improvements over the original. + +SwiftUIIntrospect allows you to get the underlying UIKit or AppKit element of a SwiftUI view. + +For instance, with SwiftUIIntrospect you can access `UITableView` to modify separators, or `UINavigationController` to customize the tab bar. + +How it works +------------ + +SwiftUIIntrospect works by adding an invisible `IntrospectionView` on top of the selected view, and an invisible "anchor" view underneath it, then looking through the UIKit/AppKit view hierarchy between the two to find the relevant view. + +For instance, when introspecting a `ScrollView`... + +```swift +ScrollView { + Text("Item 1") +} +.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { scrollView in + // do something with UIScrollView +} +``` + +... it will: + +- Add `IntrospectionView` as an overlay of `TextField` +- Add `IntrospectionAnchorView` as the background of `TextField`. +- Traverse through all the subviews between both views until a `UIScrollView` instance (if any) is found. + +> **Warning** +> Although the introspection method itself is very solid and unlikely to break in SwiftUI releases, future OS releases require explicit opt-in for introspection (`.iOS(.vXYZ)`), given differences between major OS versions which might not use the same UIKit/AppKit elements that are being looked for in previous OS versions. + +By default, `.introspect` works directly on its _receiver_. This means calling `.introspect` from inside the view you're trying to introspect won't have any effect. This is different to the original `Introspect` module in which some views would implicitly allow introspection from within. This is because most of the time it's more stable and predictable to introspect views directly, but there are times when it's not possible or simply too inflexible for library developers. You **can** introspect an _ancestor_ with `SwiftUIIntrospect`, but you must opt into this explicitly by overriding the introspection `scope`: + +```swift +ScrollView { + Text("Item 1") + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor) { scrollView in + // do something with UIScrollView + } +} +``` + +### Usage in production + +`SwiftUIIntrospect` is meant to be used in production. It does not use any private API. It only inspects the view hierarchy using publicly available methods. The library takes a defensive approach to inspecting the view hierarchy: there is no hard assumption that elements are laid out a certain way, there is no force-cast to UIKit/AppKit classes, and the `introspect()` methods are simply ignored if UIKit/AppKit views cannot be found. + +Install +------- + +### Swift Package Manager + +```swift +let package = Package( + dependencies: [ + .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.4.0"), + ], + targets: [ + .target(name: <#Target Name#>, dependencies: [ + .product(name: "SwiftUIIntrospect", package: "swiftui-introspect"), + ]), + ] +) +``` + +### CocoaPods + +```ruby +pod 'SwiftUIIntrospect' +``` + +Introspection +------------- + +### Implemented + +_WIP_ + +`SwiftUIIntrospect` already supports all the view types that `Introspect` supports, and more (e.g. `ProgressView`, `Table`). However, listing them all in a table is an arduous task that I'm still thinking of how to best accomplish (perhaps it's possible to automate via SwiftSyntax?). For now, I suggest diving into the desired view type's code file to figure out which platforms and underlying views are supported. I also suggest checking out the showcase app and tests for example use cases. + +**Missing an element?** Please [create an issue](https://github.com/timbersoftware/SwiftUI-Introspect/issues). As a temporary solution, you can [implement your own introspectable view type](#implement-your-own-view-type). + +### Cannot implement + +SwiftUI | Affected Frameworks | Why +--- | --- | --- +Text | UIKit, AppKit | Not a UILabel / NSLabel +Image | UIKit, AppKit | Not a UIImageView / NSImageView +Button | UIKit | Not a UIButton + +Examples +-------- + +### List + +```swift +List { + Text("Item") +} +.introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { tableView in + tableView.backgroundView = UIView() + tableView.backgroundColor = .cyan +} +.introspect(.list, on: .iOS(.v16)) { collectionView in + collectionView.backgroundView = UIView() + collectionView.subviews.dropFirst(1).first?.backgroundColor = .cyan +} +``` + +### ScrollView + +```swift +ScrollView { + Text("Item") +} +.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { scrollView in + scrollView.refreshControl = UIRefreshControl() +} +``` + +### NavigationView + +```swift +NavigationView { + Text("Item") +} +.navigationViewStyle(.stack) +.introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { navigationController in + navigationController.navigationBar.backgroundColor = .cyan +} +``` + +### TextField + +```swift +TextField("Text Field", text: <#Binding#>) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { textField in + textField.backgroundColor = .red + } +``` + +Implement your own selector +--------------------------- + +**Missing an element?** Please [create an issue](https://github.com/timbersoftware/SwiftUI-Introspect/issues). + +In case SwiftUIIntrospect doesn't support the SwiftUI element that you're looking for, you can implement your own selector. For example, to introspect a `TextField`: + +```swift +public struct TextFieldType: IntrospectableViewType {} + +extension IntrospectableViewType where Self == TextFieldType { + public static var textField: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} + +extension tvOSViewVersion { + public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) + public static let v15 = Self(for: .v15) + public static let v16 = Self(for: .v16) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + public static let v10_15 = Self(for: .v10_15) + public static let v11 = Self(for: .v11) + public static let v12 = Self(for: .v12) + public static let v13 = Self(for: .v13) +} +#endif +``` + +Releasing +--------- + +1. Update changelog with new version +2. PR as 'Bump to X.Y.Z' and merge it +3. Tag new version: + + ```sh + $ git tag X.Y.Z + $ git push origin --tags + ``` diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 90d8c25..0c35de1 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -1,61 +1,96 @@ skip_docs -lane :test do |options| +devices = { + "ios" => { + 13 => ["iPhone 11 (13.7)", "iPad Pro (9.7-inch) (13.7)"], + 14 => ["iPhone 12 (14.5)", "iPad Pro (9.7-inch) (14.5)"], + 15 => ["iPhone SE (3rd generation) (15.5)", "iPad Air (5th generation) (15.5)",], + 16 => ["iPhone 14 (16.4)", "iPad Pro (11-inch) (4th generation) (16.4)"], + }, + "tvos" => { + 13 => ["Apple TV (13.4)"], + 14 => ["Apple TV (14.5)"], + 15 => ["Apple TV (15.4)"], + 16 => ["Apple TV (16.4)"], + }, +} + +lane :build do |options| platform = options[:platform].to_s version = options[:version].to_i + scheme = options[:scheme].to_s - case platform - when "macos" - spm( - command: "test", - ) + unless scheme == "Showcase" + raise "Unsupported scheme: #{scheme}" next - when "ios" - devices = case version - when 14 - [ - "iPhone 11 Pro (14.5)", - "iPad Pro (11-inch) (3rd generation) (14.5)", - ] - when 15 - [ - "iPhone 11 Pro (15.5)", - "iPad Pro (11-inch) (3rd generation) (15.5)", - ] - when 16 - [ - "iPhone 14 Pro (16.2)", - "iPad Pro (11-inch) (4th generation) (16.2)", - ] - else - raise "Unsupported iOS version: #{version}" - end - when "tvos" - devices = case version - when 14 - [ - "Apple TV (14.5)", - ] - when 15 - [ - "Apple TV (15.4)", - ] - when 16 - [ - "Apple TV (16.1)", - ] - else - raise "Unsupported tvOS version: #{version}" - end - else - raise "Unsupported platform: #{platform}" end - run_tests( - scheme: "Introspect", - devices: devices, - ensure_devices_found: true, - force_quit_simulator: true, - disable_concurrent_testing: true, - ) + if platform == "macos" + for destination in ["platform=macOS", "platform=macOS,variant=Mac Catalyst"] + build_app( + scheme: scheme, + destination: destination, + skip_archive: true, + skip_codesigning: true, + skip_package_ipa: true, + skip_profile_detection: true, + ) + end + else + run_tests( + configuration: "Debug", + build_for_testing: true, + scheme: scheme, + devices: devices[platform][version], + prelaunch_simulator: false, + ensure_devices_found: true, + force_quit_simulator: true, + disable_concurrent_testing: true, + ) + end +end + +lane :test do |options| + configuration = (options[:configuration] || "Debug").to_s + platform = options[:platform].to_s + version = options[:version].to_i + scheme = options[:scheme].to_s + + if platform == "macos" + case scheme + when "Introspect" + spm( + command: "test", + configuration: configuration.downcase, + ) + when "SwiftUIIntrospectTests" + for destination in ["platform=macOS", "platform=macOS,variant=Mac Catalyst"] + run_tests( + configuration: configuration, + build_for_testing: (destination.include? "Catalyst"), # build-only on Mac Catalyst since tests crash because app isn't launched for some reason. error: Thread 1: "NSApplication has not been created yet. Consider using -[UINSApplicationDelegate runBlockWhenSharedDelegateBecomesAvailable:] to avoid premature instantiation." + scheme: scheme, + destination: [destination], + catalyst_platform: "macos", + disable_slide_to_type: false, + prelaunch_simulator: false, + ensure_devices_found: true, + force_quit_simulator: false, + disable_concurrent_testing: true, + xcargs: version == 11 ? 'SWIFT_ACTIVE_COMPILATION_CONDITIONS="$(inherited) LEGACY_MACOS_SDK"' : nil, + ) + end + else + raise "Unsupported scheme: #{scheme}" + end + else + run_tests( + configuration: configuration, + scheme: scheme, + devices: devices[platform][version], + prelaunch_simulator: true, + ensure_devices_found: true, + force_quit_simulator: true, + disable_concurrent_testing: true, + ) + end end From f17535bedacba845350076269824f47fbb786bb4 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Thu, 1 Jun 2023 20:11:16 +0100 Subject: [PATCH 07/18] Bump to 0.4.0 (#226) --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bcf7f0..45cbfe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ Changelog ## master +## [0.4.0] + +- Added: all-new implementation, API, and module (#207) + ## [0.3.1] - Fixed: wrong Swift version in podspec (#220) From a84dfbfb2842b64ba6847cbafdee444edde8ebac Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 2 Jun 2023 10:25:34 +0100 Subject: [PATCH 08/18] Add explicit SPI import in docs [skip ci] (#229) --- docs/SwiftUIIntrospect.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/SwiftUIIntrospect.md b/docs/SwiftUIIntrospect.md index a8e0650..8d3cf76 100644 --- a/docs/SwiftUIIntrospect.md +++ b/docs/SwiftUIIntrospect.md @@ -158,6 +158,8 @@ Implement your own selector In case SwiftUIIntrospect doesn't support the SwiftUI element that you're looking for, you can implement your own selector. For example, to introspect a `TextField`: ```swift +@_spi(Internals) import SwiftUIIntrospect + public struct TextFieldType: IntrospectableViewType {} extension IntrospectableViewType where Self == TextFieldType { From 43a8b9cfe8b3804fade140b31f7da4bb15ef6069 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sat, 3 Jun 2023 00:31:07 +0100 Subject: [PATCH 09/18] Unify `introspect` modifiers (#232) --- Sources/Introspect.swift | 233 +++++++++++++++----------------- Sources/IntrospectionView.swift | 105 ++++++++------ Sources/PlatformView.swift | 30 ++-- Sources/Voodoo.swift | 9 ++ 4 files changed, 201 insertions(+), 176 deletions(-) create mode 100644 Sources/Voodoo.swift diff --git a/Sources/Introspect.swift b/Sources/Introspect.swift index 08012ba..f78bec6 100644 --- a/Sources/Introspect.swift +++ b/Sources/Introspect.swift @@ -13,14 +13,14 @@ public struct IntrospectionScope: OptionSet { extension View { @ViewBuilder - public func introspect( + public func introspect( _ viewType: SwiftUIViewType, - on platforms: (PlatformViewVersions)..., + on platforms: (PlatformViewVersions)..., scope: IntrospectionScope? = nil, - customize: @escaping (PlatformSpecificView) -> Void + customize: @escaping (PlatformSpecificEntity) -> Void ) -> some View { if platforms.contains(where: \.isCurrent) { - let id = UUID() + let id = IntrospectionAnchorID() self.background( IntrospectionAnchorView( id: id @@ -29,17 +29,17 @@ extension View { ) .overlay( IntrospectionView( - selector: { (view: PlatformView) in + selector: { entity in let scope = scope ?? viewType.scope if scope.contains(.receiver), - let target = view.receiver(ofType: PlatformSpecificView.self, anchorID: id) + let target = entity.receiver(ofType: PlatformSpecificEntity.self, anchorID: id) { return target } if scope.contains(.ancestor), - let target = view.ancestor(ofType: PlatformSpecificView.self) + let target = entity.ancestor(ofType: PlatformSpecificEntity.self) { return target } @@ -53,149 +53,138 @@ extension View { self } } - - @ViewBuilder - public func introspect( - _ viewType: SwiftUIViewType, - on platforms: (PlatformViewVersions)..., - scope: IntrospectionScope? = nil, - customize: @escaping (PlatformSpecificViewController) -> Void - ) -> some View { - if platforms.contains(where: \.isCurrent) { - self.overlay( - IntrospectionView( - selector: { (viewController: PlatformViewController) in - let scope = scope ?? viewType.scope - if - scope.contains(.receiver), - let target = viewController.receiver(ofType: PlatformSpecificViewController.self) - { - return target - } - if - scope.contains(.ancestor), - let target = viewController.ancestor(ofType: PlatformSpecificViewController.self) - { - return target - } - return nil - }, - customize: customize - ) - .frame(width: 0, height: 0) - ) - } else { - self - } - } } -extension PlatformView { - fileprivate func receiver( - ofType type: PlatformSpecificView.Type, - anchorID: IntrospectionAnchorView.ID - ) -> PlatformSpecificView? { - let frontView = self - guard - let backView = Array(frontView.superviews).last?.viewWithTag(anchorID.hashValue), - let superview = backView.nearestCommonSuperviewWith(frontView) - else { - return nil - } +public protocol PlatformEntity: AnyObject { + associatedtype Base: PlatformEntity - return superview - .subviewsBetween(backView, and: frontView) - .compactMap { $0 as? PlatformSpecificView } - .first - } + @_spi(Internals) + var ancestor: Base? { get } - fileprivate func ancestor( - ofType type: PlatformSpecificView.Type - ) -> PlatformSpecificView? { - self.superviews.lazy.compactMap { $0 as? PlatformSpecificView }.first - } + @_spi(Internals) + var descendants: [Base] { get } + + @_spi(Internals) + func isDescendant(of other: Base) -> Bool + + @_spi(Internals) + func entityWithTag(_ tag: Int) -> Base? } -extension PlatformView { - private var superviews: some Sequence { - sequence(first: self, next: \.superview).dropFirst() +extension PlatformEntity { + @_spi(Internals) + public var ancestors: some Sequence { + sequence(first: self~, next: { $0.ancestor~ }).dropFirst() } - private func nearestCommonSuperviewWith(_ other: PlatformView) -> PlatformView? { - var nearestAncestor: PlatformView? = self + @_spi(Internals) + public var allDescendants: [Base] { + self.descendants.reduce([self~]) { $0 + $1.allDescendants~ } + } - while let currentView = nearestAncestor, !other.isDescendant(of: currentView) { - nearestAncestor = currentView.superview + @_spi(Internals) + public func nearestCommonAncestor(with other: Base) -> Base? { + var nearestAncestor: Base? = self~ + + while let currentEntity = nearestAncestor, !other.isDescendant(of: currentEntity~) { + nearestAncestor = currentEntity.ancestor~ } return nearestAncestor } - private func subviewsBetween(_ bottomView: PlatformView, and topView: PlatformView) -> [PlatformView] { + @_spi(Internals) + public func descendantsBetween(_ bottomEntity: Base, and topEntity: Base) -> [Base] { var entered = false - var result: [PlatformView] = [] + var result: [Base] = [] - for subview in self.allSubviews { - if subview === bottomView { + for descendant in self.allDescendants { + if descendant === bottomEntity { entered = true continue } - if subview === topView { + if descendant === topEntity { return result } if entered { - result.append(subview) + result.append(descendant) } } return result } - private var allSubviews: [PlatformView] { - self.subviews.reduce([self]) { $0 + $1.allSubviews } - } -} - -extension PlatformViewController { - fileprivate func receiver( - ofType type: PlatformSpecificViewController.Type - ) -> PlatformSpecificViewController? { - self.hostingView? - .allChildren(ofType: PlatformSpecificViewController.self) - .filter { !($0 is IntrospectionPlatformViewController) } - .first - } - - fileprivate func ancestor( - ofType type: PlatformSpecificViewController.Type - ) -> PlatformSpecificViewController? { - self.parents - .lazy - .filter { !($0 is IntrospectionPlatformViewController) } - .compactMap { $0 as? PlatformSpecificViewController } - .first - } -} - -extension PlatformViewController { - private var parents: some Sequence { - sequence(first: self, next: \.parent).dropFirst() - } - - private var hostingView: PlatformViewController? { - self.parents.first(where: { - let type = String(reflecting: type(of: $0)) - return type.hasPrefix("SwiftUI.") && type.contains("Hosting") - }) - } - - private func allChildren( - ofType type: PlatformSpecificViewController.Type - ) -> [PlatformSpecificViewController] { - var result = self.children.compactMap { $0 as? PlatformSpecificViewController } - for subview in self.children { - result.append(contentsOf: subview.allChildren(ofType: type)) + fileprivate func receiver( + ofType type: PlatformSpecificEntity.Type, + anchorID: IntrospectionAnchorID + ) -> PlatformSpecificEntity? { + let frontEntity = self + guard + let backEntity = Array(frontEntity.ancestors).last?.entityWithTag(anchorID.hashValue), + let commonAncestor = backEntity.nearestCommonAncestor(with: frontEntity~) + else { + return nil } - return result + + return commonAncestor + .descendantsBetween(backEntity~, and: frontEntity~) + .compactMap { $0 as? PlatformSpecificEntity } + .first + } + + fileprivate func ancestor( + ofType type: PlatformSpecificEntity.Type + ) -> PlatformSpecificEntity? { + self.ancestors + .lazy + .compactMap { $0 as? PlatformSpecificEntity } + .first + } +} + +extension PlatformView: PlatformEntity { + @_spi(Internals) + public var ancestor: PlatformView? { + superview + } + + @_spi(Internals) + public var descendants: [PlatformView] { + subviews + } + + @_spi(Internals) + public func entityWithTag(_ tag: Int) -> PlatformView? { + viewWithTag(tag) + } +} + +extension PlatformViewController: PlatformEntity { + @_spi(Internals) + public var ancestor: PlatformViewController? { + parent + } + + @_spi(Internals) + public var descendants: [PlatformViewController] { + children + } + + @_spi(Internals) + public func isDescendant(of other: PlatformViewController) -> Bool { + self.ancestors.contains(other) + } + + @_spi(Internals) + public func entityWithTag(_ tag: Int) -> PlatformViewController? { + if self.view.tag == tag { + return self + } + for child in children { + if let childWithTag = child.entityWithTag(tag) { + return childWithTag + } + } + return nil } } diff --git a/Sources/IntrospectionView.swift b/Sources/IntrospectionView.swift index 2997007..28cacba 100644 --- a/Sources/IntrospectionView.swift +++ b/Sources/IntrospectionView.swift @@ -1,44 +1,76 @@ import SwiftUI +typealias IntrospectionAnchorID = UUID + /// ⚓️ -struct IntrospectionAnchorView: PlatformViewRepresentable { - typealias ID = UUID +struct IntrospectionAnchorView: PlatformViewControllerRepresentable { + #if canImport(UIKit) + typealias UIViewControllerType = IntrospectionAnchorPlatformViewController + #elseif canImport(AppKit) + typealias NSViewControllerType = IntrospectionAnchorPlatformViewController + #endif @Binding private var observed: Void // workaround for state changes not triggering view updates - let id: ID + let id: IntrospectionAnchorID - init(id: ID) { + init(id: IntrospectionAnchorID) { self._observed = .constant(()) self.id = id } - #if canImport(UIKit) - func makeUIView(context: Context) -> UIView { - let view = UIView() - view.tag = id.hashValue - return view + func makePlatformViewController(context: Context) -> IntrospectionAnchorPlatformViewController { + IntrospectionAnchorPlatformViewController(id: id) + } + + func updatePlatformViewController(_ controller: IntrospectionAnchorPlatformViewController, context: Context) {} + + static func dismantlePlatformViewController(_ controller: IntrospectionAnchorPlatformViewController, coordinator: Coordinator) {} +} + +final class IntrospectionAnchorPlatformViewController: PlatformViewController { + let id: IntrospectionAnchorID + + init(id: IntrospectionAnchorID) { + self.id = id + super.init(nibName: nil, bundle: nil) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + #if canImport(UIKit) + override func viewDidLoad() { + super.viewDidLoad() + view.tag = id.hashValue } - func updateUIView(_ controller: UIView, context: Context) {} #elseif canImport(AppKit) - func makeNSView(context: Context) -> NSView { - final class TaggableView: NSView { - private var _tag: Int? - override var tag: Int { - get { _tag ?? super.tag } - set { _tag = newValue } - } + final class TaggableView: NSView { + private var _tag: Int? + override var tag: Int { + get { _tag ?? super.tag } + set { _tag = newValue } } + } + + override func loadView() { let view = TaggableView() view.tag = id.hashValue - return view + self.view = view } - func updateNSView(_ controller: NSView, context: Context) {} #endif } -struct IntrospectionView: PlatformViewControllerRepresentable { +struct IntrospectionView: PlatformViewControllerRepresentable { + #if canImport(UIKit) + typealias UIViewControllerType = IntrospectionPlatformViewController + #elseif canImport(AppKit) + typealias NSViewControllerType = IntrospectionPlatformViewController + #endif + final class TargetCache { weak var target: Target? } @@ -49,34 +81,29 @@ struct IntrospectionView: PlatformViewControllerRepresentable private let customize: (Target) -> Void init( - selector: @escaping (PlatformView) -> Target?, + selector: @escaping (any PlatformEntity) -> Target?, customize: @escaping (Target) -> Void ) { self._observed = .constant(()) - self.selector = { introspectionViewController in - #if canImport(UIKit) - if let introspectionView = introspectionViewController.viewIfLoaded { - return selector(introspectionView) + self.selector = { introspectionController in + if Target.Base.self == PlatformView.self { + #if canImport(UIKit) + if let introspectionView = introspectionController.viewIfLoaded { + return selector(introspectionView) + } + #elseif canImport(AppKit) + if introspectionController.isViewLoaded { + return selector(introspectionController.view) + } + #endif + } else if Target.Base.self == PlatformViewController.self { + return selector(introspectionController) } - #elseif canImport(AppKit) - if introspectionViewController.isViewLoaded { - return selector(introspectionViewController.view) - } - #endif return nil } self.customize = customize } - init( - selector: @escaping (PlatformViewController) -> Target?, - customize: @escaping (Target) -> Void - ) { - self._observed = .constant(()) - self.selector = { selector($0) } - self.customize = customize - } - func makeCoordinator() -> TargetCache { TargetCache() } diff --git a/Sources/PlatformView.swift b/Sources/PlatformView.swift index a89c412..0863186 100644 --- a/Sources/PlatformView.swift +++ b/Sources/PlatformView.swift @@ -6,12 +6,6 @@ public typealias PlatformView = UIView public typealias PlatformView = NSView #endif -#if canImport(UIKit) -typealias PlatformViewRepresentable = UIViewRepresentable -#elseif canImport(AppKit) -typealias PlatformViewRepresentable = NSViewRepresentable -#endif - #if canImport(UIKit) public typealias PlatformViewController = UIViewController #elseif canImport(AppKit) @@ -25,30 +19,36 @@ typealias _PlatformViewControllerRepresentable = NSViewControllerRepresentable #endif protocol PlatformViewControllerRepresentable: _PlatformViewControllerRepresentable { - func makePlatformViewController(context: Context) -> IntrospectionPlatformViewController - func updatePlatformViewController(_ controller: IntrospectionPlatformViewController, context: Context) - static func dismantlePlatformViewController(_ controller: IntrospectionPlatformViewController, coordinator: Coordinator) + #if canImport(UIKit) + typealias ViewController = UIViewControllerType + #elseif canImport(AppKit) + typealias ViewController = NSViewControllerType + #endif + + func makePlatformViewController(context: Context) -> ViewController + func updatePlatformViewController(_ controller: ViewController, context: Context) + static func dismantlePlatformViewController(_ controller: ViewController, coordinator: Coordinator) } extension PlatformViewControllerRepresentable { #if canImport(UIKit) - func makeUIViewController(context: Context) -> IntrospectionPlatformViewController { + func makeUIViewController(context: Context) -> ViewController { makePlatformViewController(context: context) } - func updateUIViewController(_ controller: IntrospectionPlatformViewController, context: Context) { + func updateUIViewController(_ controller: ViewController, context: Context) { updatePlatformViewController(controller, context: context) } - static func dismantleUIViewController(_ controller: IntrospectionPlatformViewController, coordinator: Coordinator) { + static func dismantleUIViewController(_ controller: ViewController, coordinator: Coordinator) { dismantlePlatformViewController(controller, coordinator: coordinator) } #elseif canImport(AppKit) - func makeNSViewController(context: Context) -> IntrospectionPlatformViewController { + func makeNSViewController(context: Context) -> ViewController { makePlatformViewController(context: context) } - func updateNSViewController(_ controller: IntrospectionPlatformViewController, context: Context) { + func updateNSViewController(_ controller: ViewController, context: Context) { updatePlatformViewController(controller, context: context) } - static func dismantleNSViewController(_ controller: IntrospectionPlatformViewController, coordinator: Coordinator) { + static func dismantleNSViewController(_ controller: ViewController, coordinator: Coordinator) { dismantlePlatformViewController(controller, coordinator: coordinator) } #endif diff --git a/Sources/Voodoo.swift b/Sources/Voodoo.swift new file mode 100644 index 0000000..d6df935 --- /dev/null +++ b/Sources/Voodoo.swift @@ -0,0 +1,9 @@ +postfix operator ~ + +postfix func ~ (lhs: LHS) -> T { + lhs as! T +} + +postfix func ~ (lhs: LHS?) -> T? { + lhs as? T +} From f327069e9cf65b441f488f28e9fb24520d159146 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sat, 3 Jun 2023 14:47:02 +0100 Subject: [PATCH 10/18] Custom selectors (#233) --- Sources/Introspect.swift | 68 ++++++++++------------------- Sources/IntrospectionSelector.swift | 57 ++++++++++++++++++++++++ Sources/IntrospectionView.swift | 19 +------- Sources/PlatformViewVersion.swift | 45 +++++++++++++------ 4 files changed, 114 insertions(+), 75 deletions(-) create mode 100644 Sources/IntrospectionSelector.swift diff --git a/Sources/Introspect.swift b/Sources/Introspect.swift index f78bec6..d0979fe 100644 --- a/Sources/Introspect.swift +++ b/Sources/Introspect.swift @@ -19,36 +19,23 @@ extension View { scope: IntrospectionScope? = nil, customize: @escaping (PlatformSpecificEntity) -> Void ) -> some View { - if platforms.contains(where: \.isCurrent) { - let id = IntrospectionAnchorID() + if let platform = platforms.first(where: \.isCurrent) { + let anchorID = IntrospectionAnchorID() self.background( - IntrospectionAnchorView( - id: id - ) - .frame(width: 0, height: 0) + IntrospectionAnchorView( + id: anchorID ) - .overlay( - IntrospectionView( - selector: { entity in - let scope = scope ?? viewType.scope - if - scope.contains(.receiver), - let target = entity.receiver(ofType: PlatformSpecificEntity.self, anchorID: id) - { - return target - } - if - scope.contains(.ancestor), - let target = entity.ancestor(ofType: PlatformSpecificEntity.self) - { - return target - } - return nil - }, - customize: customize - ) - .frame(width: 0, height: 0) + .frame(width: 0, height: 0) + ) + .overlay( + IntrospectionView( + selector: { entity in + (platform.selector ?? .default)(entity, scope ?? viewType.scope, anchorID) + }, + customize: customize ) + .frame(width: 0, height: 0) + ) } else { self } @@ -72,18 +59,15 @@ public protocol PlatformEntity: AnyObject { } extension PlatformEntity { - @_spi(Internals) - public var ancestors: some Sequence { + var ancestors: some Sequence { sequence(first: self~, next: { $0.ancestor~ }).dropFirst() } - @_spi(Internals) - public var allDescendants: [Base] { + var allDescendants: [Base] { self.descendants.reduce([self~]) { $0 + $1.allDescendants~ } } - @_spi(Internals) - public func nearestCommonAncestor(with other: Base) -> Base? { + func nearestCommonAncestor(with other: Base) -> Base? { var nearestAncestor: Base? = self~ while let currentEntity = nearestAncestor, !other.isDescendant(of: currentEntity~) { @@ -93,20 +77,16 @@ extension PlatformEntity { return nearestAncestor } - @_spi(Internals) - public func descendantsBetween(_ bottomEntity: Base, and topEntity: Base) -> [Base] { - var entered = false + func descendantsBetween(_ bottomEntity: Base, and topEntity: Base) -> [Base] { var result: [Base] = [] + var entered = false for descendant in self.allDescendants { if descendant === bottomEntity { entered = true - continue - } - if descendant === topEntity { - return result - } - if entered { + } else if descendant === topEntity { + break + } else if entered { result.append(descendant) } } @@ -114,7 +94,7 @@ extension PlatformEntity { return result } - fileprivate func receiver( + func receiver( ofType type: PlatformSpecificEntity.Type, anchorID: IntrospectionAnchorID ) -> PlatformSpecificEntity? { @@ -132,7 +112,7 @@ extension PlatformEntity { .first } - fileprivate func ancestor( + func ancestor( ofType type: PlatformSpecificEntity.Type ) -> PlatformSpecificEntity? { self.ancestors diff --git a/Sources/IntrospectionSelector.swift b/Sources/IntrospectionSelector.swift new file mode 100644 index 0000000..ea691aa --- /dev/null +++ b/Sources/IntrospectionSelector.swift @@ -0,0 +1,57 @@ +@_spi(Internals) +public struct IntrospectionSelector { + private let selector: (IntrospectionPlatformViewController, IntrospectionScope, IntrospectionAnchorID) -> Target? + + static var `default`: Self { .from(Target.self, selector: { $0 }) } + + @_spi(Internals) + public static func from(_ entryType: Entry.Type, selector: @escaping (Entry) -> Target?) -> Self { + .init { controller, scope, anchorID in + guard let entity = { () -> (any PlatformEntity)? in + if Entry.Base.self == PlatformView.self { + #if canImport(UIKit) + if let introspectionView = controller.viewIfLoaded { + return introspectionView + } + #elseif canImport(AppKit) + if controller.isViewLoaded { + return controller.view + } + #endif + } else if Entry.Base.self == PlatformViewController.self { + return controller + } + return nil + }() else { + return nil + } + if + scope.contains(.receiver), + let entry = entity.receiver(ofType: Entry.self, anchorID: anchorID), + let target = selector(entry) + { + return target + } + if + scope.contains(.ancestor), + let entry = entity.ancestor(ofType: Entry.self), + let target = selector(entry) + { + return target + } + return nil + } + } + + init(_ selector: @escaping (IntrospectionPlatformViewController, IntrospectionScope, IntrospectionAnchorID) -> Target?) { + self.selector = selector + } + + func callAsFunction( + _ controller: IntrospectionPlatformViewController, + _ scope: IntrospectionScope, + _ anchorID: IntrospectionAnchorID + ) -> Target? { + selector(controller, scope, anchorID) + } +} diff --git a/Sources/IntrospectionView.swift b/Sources/IntrospectionView.swift index 28cacba..0965098 100644 --- a/Sources/IntrospectionView.swift +++ b/Sources/IntrospectionView.swift @@ -81,26 +81,11 @@ struct IntrospectionView: PlatformViewControllerRepresen private let customize: (Target) -> Void init( - selector: @escaping (any PlatformEntity) -> Target?, + selector: @escaping (IntrospectionPlatformViewController) -> Target?, customize: @escaping (Target) -> Void ) { self._observed = .constant(()) - self.selector = { introspectionController in - if Target.Base.self == PlatformView.self { - #if canImport(UIKit) - if let introspectionView = introspectionController.viewIfLoaded { - return selector(introspectionView) - } - #elseif canImport(AppKit) - if introspectionController.isViewLoaded { - return selector(introspectionController.view) - } - #endif - } else if Target.Base.self == PlatformViewController.self { - return selector(introspectionController) - } - return nil - } + self.selector = selector self.customize = customize } diff --git a/Sources/PlatformViewVersion.swift b/Sources/PlatformViewVersion.swift index 09f883d..9684a55 100644 --- a/Sources/PlatformViewVersion.swift +++ b/Sources/PlatformViewVersion.swift @@ -1,32 +1,49 @@ import SwiftUI -public struct PlatformViewVersions { +public struct PlatformViewVersions { let isCurrent: Bool + let selector: IntrospectionSelector? - public static func iOS(_ versions: (iOSViewVersion)...) -> Self { - Self(isCurrent: versions.contains(where: \.isCurrent)) + private init( + _ versions: [PlatformViewVersion] + ) { + if let currentVersion = versions.first(where: \.isCurrent) { + self.isCurrent = true + self.selector = currentVersion.selector + } else { + self.isCurrent = false + self.selector = nil + } } - public static func tvOS(_ versions: (tvOSViewVersion)...) -> Self { - Self(isCurrent: versions.contains(where: \.isCurrent)) + public static func iOS(_ versions: (iOSViewVersion)...) -> Self { + Self(versions) } - public static func macOS(_ versions: (macOSViewVersion)...) -> Self { - Self(isCurrent: versions.contains(where: \.isCurrent)) + public static func tvOS(_ versions: (tvOSViewVersion)...) -> Self { + Self(versions) + } + + public static func macOS(_ versions: (macOSViewVersion)...) -> Self { + Self(versions) } } -public typealias iOSViewVersion = PlatformViewVersion -public typealias tvOSViewVersion = PlatformViewVersion -public typealias macOSViewVersion = PlatformViewVersion +public typealias iOSViewVersion = + PlatformViewVersion +public typealias tvOSViewVersion = + PlatformViewVersion +public typealias macOSViewVersion = + PlatformViewVersion -public struct PlatformViewVersion { +public struct PlatformViewVersion { let isCurrent: Bool + let selector: IntrospectionSelector? } extension PlatformViewVersion { - @_spi(Internals) public init(for version: Version) { - self.init(isCurrent: version.isCurrent) + @_spi(Internals) public init(for version: Version, selector: IntrospectionSelector? = nil) { + self.init(isCurrent: version.isCurrent, selector: selector) } @_spi(Internals) public static func unavailable(file: StaticString = #file, line: UInt = #line) -> Self { @@ -43,6 +60,6 @@ extension PlatformViewVersion { https://github.com/siteline/swiftui-introspect/issues/new?title=`\(fileName):\(line)`+should+be+marked+unavailable """ ) - return Self(isCurrent: false) + return Self(isCurrent: false, selector: nil) } } From 1627b93fed41d2641f7ff2b8040a901c844f4ea8 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sat, 3 Jun 2023 21:00:16 +0100 Subject: [PATCH 11/18] Fix `searchField` introspection (#234) --- Examples/Showcase/Showcase/ContentView.swift | 6 +- Sources/Introspect.swift | 6 +- Sources/ViewTypes/SearchField.swift | 20 +- .../LegacyTestsHostApp.swift | 14 + Tests/Tests.xcodeproj/project.pbxproj | 568 +++++++++++++++++- .../LegacySwiftUIIntrospectTests.xcscheme | 71 +++ Tests/Tests/TestUtils.swift | 40 +- .../ViewTypes/NavigationSplitViewTests.swift | 3 +- .../NavigationViewWithColumnsStyleTests.swift | 6 + Tests/Tests/ViewTypes/SearchFieldTests.swift | 71 ++- Tests/Tests/ViewTypes/ViewTests.swift | 2 + Tests/TestsHostApp/TestsHostApp.swift | 32 +- fastlane/Fastfile | 9 + 13 files changed, 773 insertions(+), 75 deletions(-) create mode 100644 Tests/LegacyTestsHostApp/LegacyTestsHostApp.swift create mode 100644 Tests/Tests.xcodeproj/xcshareddata/xcschemes/LegacySwiftUIIntrospectTests.xcscheme diff --git a/Examples/Showcase/Showcase/ContentView.swift b/Examples/Showcase/Showcase/ContentView.swift index dafb151..70032fc 100644 --- a/Examples/Showcase/Showcase/ContentView.swift +++ b/Examples/Showcase/Showcase/ContentView.swift @@ -186,10 +186,10 @@ struct NavigationShowcase: View { .introspect(.navigationView(style: .columns), on: .tvOS(.v13, .v14, .v15, .v16)) { navigationController in navigationController.navigationBar.backgroundColor = .cyan } - .introspect(.searchField, on: .iOS(.v15, .v16), .tvOS(.v15, .v16)) { searchField in - searchField.backgroundColor = .red + .introspect(.searchField, on: .iOS(.v15, .v16), .tvOS(.v15, .v16)) { searchBar in + searchBar.backgroundColor = .red #if os(iOS) - searchField.searchTextField.backgroundColor = .purple + searchBar.searchTextField.backgroundColor = .purple #endif } #endif diff --git a/Sources/Introspect.swift b/Sources/Introspect.swift index d0979fe..953f999 100644 --- a/Sources/Introspect.swift +++ b/Sources/Introspect.swift @@ -59,11 +59,13 @@ public protocol PlatformEntity: AnyObject { } extension PlatformEntity { - var ancestors: some Sequence { + @_spi(Internals) + public var ancestors: some Sequence { sequence(first: self~, next: { $0.ancestor~ }).dropFirst() } - var allDescendants: [Base] { + @_spi(Internals) + public var allDescendants: [Base] { self.descendants.reduce([self~]) { $0 + $1.allDescendants~ } } diff --git a/Sources/ViewTypes/SearchField.swift b/Sources/ViewTypes/SearchField.swift index 0b7a06d..1ee82d7 100644 --- a/Sources/ViewTypes/SearchField.swift +++ b/Sources/ViewTypes/SearchField.swift @@ -14,8 +14,14 @@ extension iOSViewVersion { public static let v13 = Self.unavailable() @available(*, unavailable, message: ".searchable isn't available on iOS 14") public static let v14 = Self.unavailable() - public static let v15 = Self(for: .v15) - public static let v16 = Self(for: .v16) + public static let v15 = Self(for: .v15, selector: selector) + public static let v16 = Self(for: .v16, selector: selector) + + private static var selector: IntrospectionSelector { + .from(UINavigationController.self) { + $0.viewIfLoaded?.allDescendants.lazy.compactMap { $0 as? UISearchBar }.first + } + } } extension tvOSViewVersion { @@ -23,7 +29,13 @@ extension tvOSViewVersion { public static let v13 = Self.unavailable() @available(*, unavailable, message: ".searchable isn't available on tvOS 14") public static let v14 = Self.unavailable() - public static let v15 = Self(for: .v15) - public static let v16 = Self(for: .v16) + public static let v15 = Self(for: .v15, selector: selector) + public static let v16 = Self(for: .v16, selector: selector) + + private static var selector: IntrospectionSelector { + .from(UINavigationController.self) { + $0.viewIfLoaded?.allDescendants.lazy.compactMap { $0 as? UISearchBar }.first + } + } } #endif diff --git a/Tests/LegacyTestsHostApp/LegacyTestsHostApp.swift b/Tests/LegacyTestsHostApp/LegacyTestsHostApp.swift new file mode 100644 index 0000000..226f578 --- /dev/null +++ b/Tests/LegacyTestsHostApp/LegacyTestsHostApp.swift @@ -0,0 +1,14 @@ +import SwiftUI + +@main +final class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + window = UIWindow(frame: UIScreen.main.bounds) + window?.rootViewController = UIHostingController(rootView: EmptyView()) + window?.makeKeyAndVisible() + return true + } +} diff --git a/Tests/Tests.xcodeproj/project.pbxproj b/Tests/Tests.xcodeproj/project.pbxproj index a536361..5d95579 100644 --- a/Tests/Tests.xcodeproj/project.pbxproj +++ b/Tests/Tests.xcodeproj/project.pbxproj @@ -7,6 +7,52 @@ objects = { /* Begin PBXBuildFile section */ + D50E2F582A2B9EFB00BAFB03 /* LegacyTestsHostApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D50E2F572A2B9EFB00BAFB03 /* LegacyTestsHostApp.swift */; }; + D50E2F5E2A2B9F6600BAFB03 /* ScrollViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D50FFE8D2A17E2A400C32641 /* ScrollViewTests.swift */; }; + D50E2F5F2A2B9F6600BAFB03 /* NavigationStackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58547F72A1CDD740068ADF4 /* NavigationStackTests.swift */; }; + D50E2F602A2B9F6600BAFB03 /* DatePickerWithGraphicalStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506972A27F32800A628E4 /* DatePickerWithGraphicalStyleTests.swift */; }; + D50E2F612A2B9F6600BAFB03 /* DatePickerWithCompactFieldStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506952A27F0E200A628E4 /* DatePickerWithCompactFieldStyleTests.swift */; }; + D50E2F622A2B9F6600BAFB03 /* ToggleWithCheckboxStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D575068F2A27D69600A628E4 /* ToggleWithCheckboxStyleTests.swift */; }; + D50E2F632A2B9F6600BAFB03 /* TabViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119CB2A239F100081F853 /* TabViewTests.swift */; }; + D50E2F642A2B9F6600BAFB03 /* ListWithInsetStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D575067F2A27C55600A628E4 /* ListWithInsetStyleTests.swift */; }; + D50E2F652A2B9F6600BAFB03 /* PickerWithMenuStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506792A27BF6C00A628E4 /* PickerWithMenuStyleTests.swift */; }; + D50E2F662A2B9F6600BAFB03 /* DatePickerWithWheelStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506912A27EE4700A628E4 /* DatePickerWithWheelStyleTests.swift */; }; + D50E2F672A2B9F6600BAFB03 /* ListWithInsetGroupedStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506812A27C74600A628E4 /* ListWithInsetGroupedStyleTests.swift */; }; + D50E2F682A2B9F6600BAFB03 /* FormWithGroupedStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506892A27CE7900A628E4 /* FormWithGroupedStyleTests.swift */; }; + D50E2F692A2B9F6600BAFB03 /* ListWithPlainStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D575067B2A27C24600A628E4 /* ListWithPlainStyleTests.swift */; }; + D50E2F6A2A2B9F6600BAFB03 /* TextEditorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119C92A239BAC0081F853 /* TextEditorTests.swift */; }; + D50E2F6B2A2B9F6600BAFB03 /* ListWithSidebarStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506832A27C8D400A628E4 /* ListWithSidebarStyleTests.swift */; }; + D50E2F6C2A2B9F6600BAFB03 /* ProgressViewWithLinearStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D575069D2A27F80E00A628E4 /* ProgressViewWithLinearStyleTests.swift */; }; + D50E2F6D2A2B9F6600BAFB03 /* ListWithBorderedStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506852A27CA4100A628E4 /* ListWithBorderedStyleTests.swift */; }; + D50E2F6E2A2B9F6600BAFB03 /* NavigationViewWithStackStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F8D5EC2A1E7B490054E9AB /* NavigationViewWithStackStyleTests.swift */; }; + D50E2F6F2A2B9F6600BAFB03 /* DatePickerWithStepperFieldStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506932A27EED200A628E4 /* DatePickerWithStepperFieldStyleTests.swift */; }; + D50E2F702A2B9F6600BAFB03 /* TextFieldWithVerticalAxisTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AD0D902A114B98003D8DEC /* TextFieldWithVerticalAxisTests.swift */; }; + D50E2F712A2B9F6600BAFB03 /* SliderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119CF2A23A62C0081F853 /* SliderTests.swift */; }; + D50E2F722A2B9F6600BAFB03 /* ButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119D72A23B3B00081F853 /* ButtonTests.swift */; }; + D50E2F732A2B9F6600BAFB03 /* ListTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55F448C2A1FF209003381E4 /* ListTests.swift */; }; + D50E2F742A2B9F6600BAFB03 /* NavigationSplitViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58547F92A1D12270068ADF4 /* NavigationSplitViewTests.swift */; }; + D50E2F752A2B9F6600BAFB03 /* NavigationViewWithColumnsStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F8D5EE2A1E87950054E9AB /* NavigationViewWithColumnsStyleTests.swift */; }; + D50E2F762A2B9F6600BAFB03 /* FormTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506872A27CB9800A628E4 /* FormTests.swift */; }; + D50E2F772A2B9F6600BAFB03 /* ToggleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119C72A22AC130081F853 /* ToggleTests.swift */; }; + D50E2F782A2B9F6600BAFB03 /* StepperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119D12A23A77C0081F853 /* StepperTests.swift */; }; + D50E2F792A2B9F6600BAFB03 /* ColorPickerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119D92A23B7700081F853 /* ColorPickerTests.swift */; }; + D50E2F7A2A2B9F6600BAFB03 /* ToggleWithButtonStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D575068D2A27D4DC00A628E4 /* ToggleWithButtonStyleTests.swift */; }; + D50E2F7B2A2B9F6600BAFB03 /* PlatformTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F0BE6729C0DC4900AD95AB /* PlatformTests.swift */; }; + D50E2F7C2A2B9F6600BAFB03 /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58CE15729C621DD0081BFB0 /* TestUtils.swift */; }; + D50E2F7D2A2B9F6600BAFB03 /* PickerWithSegmentedStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506772A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift */; }; + D50E2F7E2A2B9F6600BAFB03 /* TabViewWithPageStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119CD2A23A4A70081F853 /* TabViewWithPageStyleTests.swift */; }; + D50E2F7F2A2B9F6600BAFB03 /* DatePickerWithFieldStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506992A27F48D00A628E4 /* DatePickerWithFieldStyleTests.swift */; }; + D50E2F802A2B9F6600BAFB03 /* TableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D575069F2A27FC0400A628E4 /* TableTests.swift */; }; + D50E2F812A2B9F6600BAFB03 /* DatePickerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119D32A23AC100081F853 /* DatePickerTests.swift */; }; + D50E2F822A2B9F6600BAFB03 /* ToggleWithSwitchStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D575068B2A27D40500A628E4 /* ToggleWithSwitchStyleTests.swift */; }; + D50E2F832A2B9F6600BAFB03 /* ListCellTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119C32A211B8A0081F853 /* ListCellTests.swift */; }; + D50E2F842A2B9F6600BAFB03 /* SearchFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506A12A281B9C00A628E4 /* SearchFieldTests.swift */; }; + D50E2F852A2B9F6600BAFB03 /* ViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119C52A227E930081F853 /* ViewTests.swift */; }; + D50E2F862A2B9F6600BAFB03 /* ListWithGroupedStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D575067D2A27C43400A628E4 /* ListWithGroupedStyleTests.swift */; }; + D50E2F872A2B9F6600BAFB03 /* ProgressViewWithCircularStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D575069B2A27F68700A628E4 /* ProgressViewWithCircularStyleTests.swift */; }; + D50E2F882A2B9F6600BAFB03 /* PickerWithWheelStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58119D52A23AED70081F853 /* PickerWithWheelStyleTests.swift */; }; + D50E2F892A2B9F6600BAFB03 /* TextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B67B832A0D318F007D5D9B /* TextFieldTests.swift */; }; + D50E2F8B2A2B9F6600BAFB03 /* SwiftUIIntrospect in Frameworks */ = {isa = PBXBuildFile; productRef = D50E2F5C2A2B9F6600BAFB03 /* SwiftUIIntrospect */; }; D50FFE8E2A17E2A400C32641 /* ScrollViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D50FFE8D2A17E2A400C32641 /* ScrollViewTests.swift */; }; D55F448D2A1FF209003381E4 /* ListTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55F448C2A1FF209003381E4 /* ListTests.swift */; }; D57506782A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506772A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift */; }; @@ -56,6 +102,20 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + D50E2F5B2A2B9F6600BAFB03 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D5F0BE3F29C0DB9700AD95AB /* Project object */; + proxyType = 1; + remoteGlobalIDString = D5F0BE4829C0DBE800AD95AB; + remoteInfo = TestsHostApp; + }; + D50E2F912A2B9FDE00BAFB03 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D5F0BE3F29C0DB9700AD95AB /* Project object */; + proxyType = 1; + remoteGlobalIDString = D50E2F4D2A2B9DEE00BAFB03; + remoteInfo = LegacyTestsHostApp; + }; D5F0BE6129C0DC0000AD95AB /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D5F0BE3F29C0DB9700AD95AB /* Project object */; @@ -66,6 +126,9 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + D50E2F552A2B9DEE00BAFB03 /* LegacyTestsHostApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LegacyTestsHostApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; + D50E2F572A2B9EFB00BAFB03 /* LegacyTestsHostApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyTestsHostApp.swift; sourceTree = ""; }; + D50E2F902A2B9F6600BAFB03 /* LegacyTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LegacyTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; D50FFE8D2A17E2A400C32641 /* ScrollViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewTests.swift; sourceTree = ""; }; D55F448C2A1FF209003381E4 /* ListTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTests.swift; sourceTree = ""; }; D57506772A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickerWithSegmentedStyleTests.swift; sourceTree = ""; }; @@ -116,6 +179,21 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + D50E2F502A2B9DEE00BAFB03 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D50E2F8A2A2B9F6600BAFB03 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D50E2F8B2A2B9F6600BAFB03 /* SwiftUIIntrospect in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; D5F0BE4629C0DBE800AD95AB /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -134,6 +212,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + D50E2F562A2B9EB600BAFB03 /* LegacyTestsHostApp */ = { + isa = PBXGroup; + children = ( + D50E2F572A2B9EFB00BAFB03 /* LegacyTestsHostApp.swift */, + ); + path = LegacyTestsHostApp; + sourceTree = ""; + }; D5B67B852A0D3193007D5D9B /* ViewTypes */ = { isa = PBXGroup; children = ( @@ -187,6 +273,7 @@ isa = PBXGroup; children = ( D5F0BE4B29C0DBE800AD95AB /* TestsHostApp */, + D50E2F562A2B9EB600BAFB03 /* LegacyTestsHostApp */, D5F0BE5E29C0DC0000AD95AB /* Tests */, D5F0BE4A29C0DBE800AD95AB /* Products */, D5F0BE7029C0E12300AD95AB /* Frameworks */, @@ -198,6 +285,8 @@ children = ( D5F0BE4929C0DBE800AD95AB /* TestsHostApp.app */, D5F0BE5D29C0DC0000AD95AB /* Tests.xctest */, + D50E2F552A2B9DEE00BAFB03 /* LegacyTestsHostApp.app */, + D50E2F902A2B9F6600BAFB03 /* LegacyTests.xctest */, ); name = Products; sourceTree = ""; @@ -230,6 +319,45 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + D50E2F4D2A2B9DEE00BAFB03 /* LegacyTestsHostApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = D50E2F522A2B9DEE00BAFB03 /* Build configuration list for PBXNativeTarget "LegacyTestsHostApp" */; + buildPhases = ( + D50E2F4E2A2B9DEE00BAFB03 /* Sources */, + D50E2F502A2B9DEE00BAFB03 /* Frameworks */, + D50E2F512A2B9DEE00BAFB03 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = LegacyTestsHostApp; + productName = TestsHostApp; + productReference = D50E2F552A2B9DEE00BAFB03 /* LegacyTestsHostApp.app */; + productType = "com.apple.product-type.application"; + }; + D50E2F592A2B9F6600BAFB03 /* LegacyTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = D50E2F8D2A2B9F6600BAFB03 /* Build configuration list for PBXNativeTarget "LegacyTests" */; + buildPhases = ( + D50E2F5D2A2B9F6600BAFB03 /* Sources */, + D50E2F8A2A2B9F6600BAFB03 /* Frameworks */, + D50E2F8C2A2B9F6600BAFB03 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + D50E2F5A2A2B9F6600BAFB03 /* PBXTargetDependency */, + D50E2F922A2B9FDE00BAFB03 /* PBXTargetDependency */, + ); + name = LegacyTests; + packageProductDependencies = ( + D50E2F5C2A2B9F6600BAFB03 /* SwiftUIIntrospect */, + ); + productName = Tests; + productReference = D50E2F902A2B9F6600BAFB03 /* LegacyTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; D5F0BE4829C0DBE800AD95AB /* TestsHostApp */ = { isa = PBXNativeTarget; buildConfigurationList = D5F0BE5829C0DBE900AD95AB /* Build configuration list for PBXNativeTarget "TestsHostApp" */; @@ -278,6 +406,9 @@ LastSwiftUpdateCheck = 1420; LastUpgradeCheck = 1420; TargetAttributes = { + D50E2F592A2B9F6600BAFB03 = { + TestTargetID = D50E2F4D2A2B9DEE00BAFB03; + }; D5F0BE4829C0DBE800AD95AB = { CreatedOnToolsVersion = 14.2; }; @@ -303,11 +434,27 @@ targets = ( D5F0BE4829C0DBE800AD95AB /* TestsHostApp */, D5F0BE5C29C0DC0000AD95AB /* Tests */, + D50E2F4D2A2B9DEE00BAFB03 /* LegacyTestsHostApp */, + D50E2F592A2B9F6600BAFB03 /* LegacyTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + D50E2F512A2B9DEE00BAFB03 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D50E2F8C2A2B9F6600BAFB03 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; D5F0BE4729C0DBE800AD95AB /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -325,6 +472,65 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + D50E2F4E2A2B9DEE00BAFB03 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D50E2F582A2B9EFB00BAFB03 /* LegacyTestsHostApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D50E2F5D2A2B9F6600BAFB03 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D50E2F5E2A2B9F6600BAFB03 /* ScrollViewTests.swift in Sources */, + D50E2F5F2A2B9F6600BAFB03 /* NavigationStackTests.swift in Sources */, + D50E2F602A2B9F6600BAFB03 /* DatePickerWithGraphicalStyleTests.swift in Sources */, + D50E2F612A2B9F6600BAFB03 /* DatePickerWithCompactFieldStyleTests.swift in Sources */, + D50E2F622A2B9F6600BAFB03 /* ToggleWithCheckboxStyleTests.swift in Sources */, + D50E2F632A2B9F6600BAFB03 /* TabViewTests.swift in Sources */, + D50E2F642A2B9F6600BAFB03 /* ListWithInsetStyleTests.swift in Sources */, + D50E2F652A2B9F6600BAFB03 /* PickerWithMenuStyleTests.swift in Sources */, + D50E2F662A2B9F6600BAFB03 /* DatePickerWithWheelStyleTests.swift in Sources */, + D50E2F672A2B9F6600BAFB03 /* ListWithInsetGroupedStyleTests.swift in Sources */, + D50E2F682A2B9F6600BAFB03 /* FormWithGroupedStyleTests.swift in Sources */, + D50E2F692A2B9F6600BAFB03 /* ListWithPlainStyleTests.swift in Sources */, + D50E2F6A2A2B9F6600BAFB03 /* TextEditorTests.swift in Sources */, + D50E2F6B2A2B9F6600BAFB03 /* ListWithSidebarStyleTests.swift in Sources */, + D50E2F6C2A2B9F6600BAFB03 /* ProgressViewWithLinearStyleTests.swift in Sources */, + D50E2F6D2A2B9F6600BAFB03 /* ListWithBorderedStyleTests.swift in Sources */, + D50E2F6E2A2B9F6600BAFB03 /* NavigationViewWithStackStyleTests.swift in Sources */, + D50E2F6F2A2B9F6600BAFB03 /* DatePickerWithStepperFieldStyleTests.swift in Sources */, + D50E2F702A2B9F6600BAFB03 /* TextFieldWithVerticalAxisTests.swift in Sources */, + D50E2F712A2B9F6600BAFB03 /* SliderTests.swift in Sources */, + D50E2F722A2B9F6600BAFB03 /* ButtonTests.swift in Sources */, + D50E2F732A2B9F6600BAFB03 /* ListTests.swift in Sources */, + D50E2F742A2B9F6600BAFB03 /* NavigationSplitViewTests.swift in Sources */, + D50E2F752A2B9F6600BAFB03 /* NavigationViewWithColumnsStyleTests.swift in Sources */, + D50E2F762A2B9F6600BAFB03 /* FormTests.swift in Sources */, + D50E2F772A2B9F6600BAFB03 /* ToggleTests.swift in Sources */, + D50E2F782A2B9F6600BAFB03 /* StepperTests.swift in Sources */, + D50E2F792A2B9F6600BAFB03 /* ColorPickerTests.swift in Sources */, + D50E2F7A2A2B9F6600BAFB03 /* ToggleWithButtonStyleTests.swift in Sources */, + D50E2F7B2A2B9F6600BAFB03 /* PlatformTests.swift in Sources */, + D50E2F7C2A2B9F6600BAFB03 /* TestUtils.swift in Sources */, + D50E2F7D2A2B9F6600BAFB03 /* PickerWithSegmentedStyleTests.swift in Sources */, + D50E2F7E2A2B9F6600BAFB03 /* TabViewWithPageStyleTests.swift in Sources */, + D50E2F7F2A2B9F6600BAFB03 /* DatePickerWithFieldStyleTests.swift in Sources */, + D50E2F802A2B9F6600BAFB03 /* TableTests.swift in Sources */, + D50E2F812A2B9F6600BAFB03 /* DatePickerTests.swift in Sources */, + D50E2F822A2B9F6600BAFB03 /* ToggleWithSwitchStyleTests.swift in Sources */, + D50E2F832A2B9F6600BAFB03 /* ListCellTests.swift in Sources */, + D50E2F842A2B9F6600BAFB03 /* SearchFieldTests.swift in Sources */, + D50E2F852A2B9F6600BAFB03 /* ViewTests.swift in Sources */, + D50E2F862A2B9F6600BAFB03 /* ListWithGroupedStyleTests.swift in Sources */, + D50E2F872A2B9F6600BAFB03 /* ProgressViewWithCircularStyleTests.swift in Sources */, + D50E2F882A2B9F6600BAFB03 /* PickerWithWheelStyleTests.swift in Sources */, + D50E2F892A2B9F6600BAFB03 /* TextFieldTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; D5F0BE4529C0DBE800AD95AB /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -387,6 +593,16 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + D50E2F5A2A2B9F6600BAFB03 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D5F0BE4829C0DBE800AD95AB /* TestsHostApp */; + targetProxy = D50E2F5B2A2B9F6600BAFB03 /* PBXContainerItemProxy */; + }; + D50E2F922A2B9FDE00BAFB03 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D50E2F4D2A2B9DEE00BAFB03 /* LegacyTestsHostApp */; + targetProxy = D50E2F912A2B9FDE00BAFB03 /* PBXContainerItemProxy */; + }; D5F0BE6229C0DC0000AD95AB /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = D5F0BE4829C0DBE800AD95AB /* TestsHostApp */; @@ -395,6 +611,324 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + D50E2F532A2B9DEE00BAFB03 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + "ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = NO; + ENABLE_PREVIEWS = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + INFOPLIST_KEY_UILaunchStoryboardName = ""; + INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.siteline.TestsHostApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + D50E2F542A2B9DEE00BAFB03 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + "ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_PREVIEWS = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + INFOPLIST_KEY_UILaunchStoryboardName = ""; + INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.siteline.TestsHostApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SUPPORTS_MACCATALYST = YES; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + D50E2F8E2A2B9F6600BAFB03 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Tests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3,6"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LegacyTestsHostApp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/LegacyTestsHostApp"; + "TEST_HOST[sdk=macosx*]" = ""; + }; + name = Debug; + }; + D50E2F8F2A2B9F6600BAFB03 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Tests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3,6"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LegacyTestsHostApp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/LegacyTestsHostApp"; + "TEST_HOST[sdk=macosx*]" = ""; + }; + name = Release; + }; D5F0BE4329C0DB9700AD95AB /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -487,7 +1021,8 @@ "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MARKETING_VERSION = 1.0; @@ -503,6 +1038,7 @@ SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + TVOS_DEPLOYMENT_TARGET = 14.0; }; name = Debug; }; @@ -568,7 +1104,8 @@ "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MARKETING_VERSION = 1.0; @@ -583,6 +1120,7 @@ SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; + TVOS_DEPLOYMENT_TARGET = 14.0; }; name = Release; }; @@ -640,6 +1178,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -659,6 +1198,7 @@ SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestsHostApp.app/TestsHostApp"; "TEST_HOST[sdk=macosx*]" = ""; + TVOS_DEPLOYMENT_TARGET = 14.0; }; name = Debug; }; @@ -710,6 +1250,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -728,12 +1269,31 @@ SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestsHostApp.app/TestsHostApp"; "TEST_HOST[sdk=macosx*]" = ""; + TVOS_DEPLOYMENT_TARGET = 14.0; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + D50E2F522A2B9DEE00BAFB03 /* Build configuration list for PBXNativeTarget "LegacyTestsHostApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D50E2F532A2B9DEE00BAFB03 /* Debug */, + D50E2F542A2B9DEE00BAFB03 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D50E2F8D2A2B9F6600BAFB03 /* Build configuration list for PBXNativeTarget "LegacyTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D50E2F8E2A2B9F6600BAFB03 /* Debug */, + D50E2F8F2A2B9F6600BAFB03 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; D5F0BE4229C0DB9700AD95AB /* Build configuration list for PBXProject "Tests" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -764,6 +1324,10 @@ /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ + D50E2F5C2A2B9F6600BAFB03 /* SwiftUIIntrospect */ = { + isa = XCSwiftPackageProductDependency; + productName = SwiftUIIntrospect; + }; D58CE15529C621B30081BFB0 /* SwiftUIIntrospect */ = { isa = XCSwiftPackageProductDependency; productName = SwiftUIIntrospect; diff --git a/Tests/Tests.xcodeproj/xcshareddata/xcschemes/LegacySwiftUIIntrospectTests.xcscheme b/Tests/Tests.xcodeproj/xcshareddata/xcschemes/LegacySwiftUIIntrospectTests.xcscheme new file mode 100644 index 0000000..9804502 --- /dev/null +++ b/Tests/Tests.xcodeproj/xcshareddata/xcschemes/LegacySwiftUIIntrospectTests.xcscheme @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/Tests/TestUtils.swift b/Tests/Tests/TestUtils.swift index 79a8cf6..1570ad9 100644 --- a/Tests/Tests/TestUtils.swift +++ b/Tests/Tests/TestUtils.swift @@ -3,46 +3,24 @@ import XCTest #if canImport(UIKit) enum TestUtils { - enum Constants { - static let timeout: TimeInterval = 3 - } + private static let window = UIWindow(frame: UIScreen.main.bounds) static func present(view: some View) { - let hostingController = UIHostingController(rootView: view) - - for window in UIApplication.shared.windows { - if let presentedViewController = window.rootViewController?.presentedViewController { - presentedViewController.dismiss(animated: false, completion: nil) - } - window.isHidden = true - } - - let window = UIWindow(frame: UIScreen.main.bounds) - window.layer.speed = 10 - - hostingController.beginAppearanceTransition(true, animated: false) - window.rootViewController = hostingController + window.rootViewController = UIHostingController(rootView: view) window.makeKeyAndVisible() window.layoutIfNeeded() - hostingController.endAppearanceTransition() } } #elseif canImport(AppKit) enum TestUtils { - enum Constants { - static let timeout: TimeInterval = 5 - } + private static let window = NSWindow( + contentRect: NSRect(x: 0, y: 0, width: 480, height: 300), + styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView], + backing: .buffered, + defer: true + ) static func present(view: some View) { - let window = NSWindow( - contentRect: NSRect(x: 0, y: 0, width: 480, height: 300), - styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView], - backing: .buffered, - defer: true - ) - - window.center() - window.setFrameAutosaveName("Main Window") window.contentView = NSHostingView(rootView: view) window.makeKeyAndOrderFront(nil) window.layoutIfNeeded() @@ -60,7 +38,7 @@ func XCTAssertViewIntrospection( let spies = Spies() let view = view(spies) TestUtils.present(view: view) - XCTWaiter(delegate: spies).wait(for: spies.expectations.values.map(\.0), timeout: TestUtils.Constants.timeout) + XCTWaiter(delegate: spies).wait(for: spies.expectations.values.map(\.0), timeout: 3) extraAssertions(spies.objects.sorted(by: { $0.key < $1.key }).map(\.value)) } diff --git a/Tests/Tests/ViewTypes/NavigationSplitViewTests.swift b/Tests/Tests/ViewTypes/NavigationSplitViewTests.swift index 07cb69a..77ff258 100644 --- a/Tests/Tests/ViewTypes/NavigationSplitViewTests.swift +++ b/Tests/Tests/ViewTypes/NavigationSplitViewTests.swift @@ -50,7 +50,8 @@ final class NavigationSplitViewTests: XCTestCase { XCTAssertViewIntrospection(of: PlatformNavigationSplitView.self) { spies in let spy = spies[0] - NavigationSplitView { + // NB: columnVisibility is explicitly set here for ancestor introspection to work, because initially on iPad the sidebar is hidden, so the introspection modifier isn't triggered until the user makes the sidebar appear. This is why ancestor introspection is discouraged for most situations and it's opt-in. + NavigationSplitView(columnVisibility: .constant(.all)) { ZStack { Color.red Text("Sidebar") diff --git a/Tests/Tests/ViewTypes/NavigationViewWithColumnsStyleTests.swift b/Tests/Tests/ViewTypes/NavigationViewWithColumnsStyleTests.swift index 2b35df7..5902b78 100644 --- a/Tests/Tests/ViewTypes/NavigationViewWithColumnsStyleTests.swift +++ b/Tests/Tests/ViewTypes/NavigationViewWithColumnsStyleTests.swift @@ -50,6 +50,12 @@ final class NavigationViewWithColumnsStyleTests: XCTestCase { } } .navigationViewStyle(DoubleColumnNavigationViewStyle()) + #if os(iOS) + // NB: this is necessary for ancestor introspection to work, because initially on iPad the "Customized" text isn't shown as it's hidden in the sidebar. This is why ancestor introspection is discouraged for most situations and it's opt-in. + .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16)) { + $0.preferredDisplayMode = .oneOverSecondary + } + #endif } } } diff --git a/Tests/Tests/ViewTypes/SearchFieldTests.swift b/Tests/Tests/ViewTypes/SearchFieldTests.swift index b847aab..b188356 100644 --- a/Tests/Tests/ViewTypes/SearchFieldTests.swift +++ b/Tests/Tests/ViewTypes/SearchFieldTests.swift @@ -9,7 +9,7 @@ final class SearchFieldTests: XCTestCase { typealias PlatformSearchField = UISearchBar #endif - func testSearchField() throws { + func testSearchFieldInNavigationStack() throws { guard #available(iOS 15, tvOS 15, *) else { throw XCTSkip() } @@ -27,5 +27,74 @@ final class SearchFieldTests: XCTestCase { #endif } } + + func testSearchFieldInNavigationStackAsAncestor() throws { + guard #available(iOS 15, tvOS 15, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformSearchField.self) { spies in + let spy = spies[0] + + NavigationView { + Text("Customized") + .searchable(text: .constant("")) + #if os(iOS) || os(tvOS) + .introspect(.searchField, on: .iOS(.v15, .v16), .tvOS(.v15, .v16), scope: .ancestor, customize: spy) + #endif + } + .navigationViewStyle(.stack) + } + } + + func testSearchFieldInNavigationSplitView() throws { + guard #available(iOS 15, tvOS 15, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformSearchField.self) { spies in + let spy = spies[0] + + NavigationView { + Text("Customized") + .searchable(text: .constant("")) + } + .navigationViewStyle(DoubleColumnNavigationViewStyle()) + #if os(iOS) || os(tvOS) + .introspect(.searchField, on: .iOS(.v15, .v16), .tvOS(.v15, .v16), customize: spy) + #endif + #if os(iOS) + // NB: this is necessary for introspection to work, because on iPad the search field is in the sidebar, which is initially hidden. + .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16)) { + $0.preferredDisplayMode = .oneOverSecondary + } + #endif + } + } + + func testSearchFieldInNavigationSplitViewAsAncestor() throws { + guard #available(iOS 15, tvOS 15, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformSearchField.self) { spies in + let spy = spies[0] + + NavigationView { + Text("Customized") + .searchable(text: .constant("")) + #if os(iOS) || os(tvOS) + .introspect(.searchField, on: .iOS(.v15, .v16), .tvOS(.v15, .v16), scope: .ancestor, customize: spy) + #endif + } + .navigationViewStyle(DoubleColumnNavigationViewStyle()) + #if os(iOS) + // NB: this is necessary for introspection to work, because on iPad the search field is in the sidebar, which is initially hidden. + .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16)) { + $0.preferredDisplayMode = .oneOverSecondary + } + #endif + } + } } #endif diff --git a/Tests/Tests/ViewTypes/ViewTests.swift b/Tests/Tests/ViewTypes/ViewTests.swift index 5114597..4cf9a3a 100644 --- a/Tests/Tests/ViewTypes/ViewTests.swift +++ b/Tests/Tests/ViewTypes/ViewTests.swift @@ -16,6 +16,7 @@ final class ViewTests: XCTestCase { .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy0) #endif } + .navigationViewStyle(.stack) NavigationView { Text("Item 1") @@ -23,6 +24,7 @@ final class ViewTests: XCTestCase { .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy1) #endif } + .navigationViewStyle(.stack) } } extraAssertions: { XCTAssert($0[safe: 0] !== $0[safe: 1]) diff --git a/Tests/TestsHostApp/TestsHostApp.swift b/Tests/TestsHostApp/TestsHostApp.swift index 02cd701..050f417 100644 --- a/Tests/TestsHostApp/TestsHostApp.swift +++ b/Tests/TestsHostApp/TestsHostApp.swift @@ -1,40 +1,10 @@ import SwiftUI -#if os(iOS) || os(tvOS) -@UIApplicationMain -final class AppDelegate: UIResponder, UIApplicationDelegate { - - var window: UIWindow? - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - window = UIWindow(frame: UIScreen.main.bounds) - window?.rootViewController = UIHostingController(rootView: AppView()) - window?.makeKeyAndVisible() - return true - } -} -#elseif os(macOS) @main struct App: SwiftUI.App { var body: some Scene { WindowGroup { - AppView() + EmptyView() } } } -#endif - -struct AppView: View { - var body: some View { - VStack(spacing: 20) { - Text("Host App for Tests").bold() - Text("This is just an app target to run tests against, needed for iOS 13 compatibility.") - Text("If iOS 13 support is dropped in the future, this target can and should be removed and tests should be ran using SPM instead.") - } - .multilineTextAlignment(.center) - .padding() - #if os(macOS) - .fixedSize() - #endif - } -} diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 0c35de1..987b4c2 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -83,6 +83,15 @@ lane :test do |options| raise "Unsupported scheme: #{scheme}" end else + is_legacy_sdk = (platform == "ios" && version == 13) || (platform == "tvos" && version == 13) + scheme = case scheme + when "Introspect" + "Introspect" + when "SwiftUIIntrospectTests" + is_legacy_sdk ? "LegacySwiftUIIntrospectTests" : "SwiftUIIntrospectTests" + else + raise "Unsupported scheme: #{scheme}" + end run_tests( configuration: configuration, scheme: scheme, From 95c4382161d8140ed9508bea028ea4bea125921a Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sat, 3 Jun 2023 21:08:17 +0100 Subject: [PATCH 12/18] Bump to 0.5.0 (#236) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45cbfe4..ae5c247 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ Changelog ## master +## [0.5.0] + +- Added: support for custom selectors (#233) +- Changed: unified introspect modifiers into one (#232) +- Fixed: `searchField` introspection (#234) +- Documentation: added explicit SPI import (#229) + ## [0.4.0] - Added: all-new implementation, API, and module (#207) From a6ced7277207e14e2a275a46b98b4d1383dc6e57 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sat, 3 Jun 2023 22:02:44 +0100 Subject: [PATCH 13/18] Fix `SwiftUIIntrospect.podspec` (#237) --- SwiftUIIntrospect.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SwiftUIIntrospect.podspec b/SwiftUIIntrospect.podspec index cf3fa7c..d947ab7 100644 --- a/SwiftUIIntrospect.podspec +++ b/SwiftUIIntrospect.podspec @@ -10,7 +10,7 @@ Pod::Spec.new do |spec| tag: spec.version } - spec.source_files = 'Sources/**.swift' + spec.source_files = 'Sources/**/*.swift' spec.swift_version = '5.7' spec.ios.deployment_target = '13.0' From 94ab0068fcc81e3e98894256ee6793e317bc4641 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sat, 3 Jun 2023 22:04:22 +0100 Subject: [PATCH 14/18] Bump to 0.5.1 (#238) --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae5c247..fc11223 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ Changelog ## master +## [0.5.1] + +- Fixed: SwiftUIIntrospect.podspec (#237) + ## [0.5.0] - Added: support for custom selectors (#233) From 23b08e51fc5d8ee98beb33a79f8e93e9612e94da Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 5 Jun 2023 01:00:14 +0100 Subject: [PATCH 15/18] Selector overrides (#239) --- Sources/IntrospectionSelector.swift | 100 +++++++++++++++++----------- Sources/IntrospectionView.swift | 3 +- 2 files changed, 62 insertions(+), 41 deletions(-) diff --git a/Sources/IntrospectionSelector.swift b/Sources/IntrospectionSelector.swift index ea691aa..742ed13 100644 --- a/Sources/IntrospectionSelector.swift +++ b/Sources/IntrospectionSelector.swift @@ -1,50 +1,43 @@ @_spi(Internals) public struct IntrospectionSelector { - private let selector: (IntrospectionPlatformViewController, IntrospectionScope, IntrospectionAnchorID) -> Target? - - static var `default`: Self { .from(Target.self, selector: { $0 }) } + @_spi(Internals) + public static var `default`: Self { .from(Target.self, selector: { $0 }) } @_spi(Internals) public static func from(_ entryType: Entry.Type, selector: @escaping (Entry) -> Target?) -> Self { - .init { controller, scope, anchorID in - guard let entity = { () -> (any PlatformEntity)? in - if Entry.Base.self == PlatformView.self { - #if canImport(UIKit) - if let introspectionView = controller.viewIfLoaded { - return introspectionView - } - #elseif canImport(AppKit) - if controller.isViewLoaded { - return controller.view - } - #endif - } else if Entry.Base.self == PlatformViewController.self { - return controller - } - return nil - }() else { - return nil + .init( + receiverSelector: { controller, anchorID in + controller.as(Entry.self)?.receiver(ofType: Entry.self, anchorID: anchorID).flatMap(selector) + }, + ancestorSelector: { controller in + controller.as(Entry.self)?.ancestor(ofType: Entry.self).flatMap(selector) } - if - scope.contains(.receiver), - let entry = entity.receiver(ofType: Entry.self, anchorID: anchorID), - let target = selector(entry) - { - return target - } - if - scope.contains(.ancestor), - let entry = entity.ancestor(ofType: Entry.self), - let target = selector(entry) - { - return target - } - return nil - } + ) } - init(_ selector: @escaping (IntrospectionPlatformViewController, IntrospectionScope, IntrospectionAnchorID) -> Target?) { - self.selector = selector + private var receiverSelector: (IntrospectionPlatformViewController, IntrospectionAnchorID) -> Target? + private var ancestorSelector: (IntrospectionPlatformViewController) -> Target? + + private init( + receiverSelector: @escaping (IntrospectionPlatformViewController, IntrospectionAnchorID) -> Target?, + ancestorSelector: @escaping (IntrospectionPlatformViewController) -> Target? + ) { + self.receiverSelector = receiverSelector + self.ancestorSelector = ancestorSelector + } + + @_spi(Internals) + public func withReceiverSelector(_ selector: @escaping (PlatformViewController, IntrospectionAnchorID) -> Target?) -> Self { + var copy = self + copy.receiverSelector = selector + return copy + } + + @_spi(Internals) + public func withAncestorSelector(_ selector: @escaping (PlatformViewController) -> Target?) -> Self { + var copy = self + copy.ancestorSelector = selector + return copy } func callAsFunction( @@ -52,6 +45,33 @@ public struct IntrospectionSelector { _ scope: IntrospectionScope, _ anchorID: IntrospectionAnchorID ) -> Target? { - selector(controller, scope, anchorID) + if + scope.contains(.receiver), + let target = receiverSelector(controller, anchorID) + { + return target + } + if + scope.contains(.ancestor), + let target = ancestorSelector(controller) + { + return target + } + return nil + } +} + +extension PlatformViewController { + func `as`(_ entityType: Entity.Type) -> (any PlatformEntity)? { + if Entity.Base.self == PlatformView.self { + #if canImport(UIKit) + return viewIfLoaded + #elseif canImport(AppKit) + return isViewLoaded ? view : nil + #endif + } else if Entity.Base.self == PlatformViewController.self { + return self + } + return nil } } diff --git a/Sources/IntrospectionView.swift b/Sources/IntrospectionView.swift index 0965098..ea96909 100644 --- a/Sources/IntrospectionView.swift +++ b/Sources/IntrospectionView.swift @@ -1,6 +1,7 @@ import SwiftUI -typealias IntrospectionAnchorID = UUID +@_spi(Internals) +public typealias IntrospectionAnchorID = UUID /// ⚓️ struct IntrospectionAnchorView: PlatformViewControllerRepresentable { From 4b81342e511a7fad7203bb248c7fe0486fe2d252 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 5 Jun 2023 01:56:08 +0100 Subject: [PATCH 16/18] Optimize ancestor controller selectors (#240) --- Sources/ViewTypes/NavigationSplitView.swift | 12 ++++++++-- Sources/ViewTypes/NavigationStack.swift | 12 ++++++++-- .../NavigationViewWithColumnsStyle.swift | 24 ++++++++++++------- .../NavigationViewWithStackStyle.swift | 24 ++++++++++++------- Sources/ViewTypes/TabView.swift | 24 ++++++++++++------- 5 files changed, 68 insertions(+), 28 deletions(-) diff --git a/Sources/ViewTypes/NavigationSplitView.swift b/Sources/ViewTypes/NavigationSplitView.swift index b0b2b59..a7eec20 100644 --- a/Sources/ViewTypes/NavigationSplitView.swift +++ b/Sources/ViewTypes/NavigationSplitView.swift @@ -17,7 +17,11 @@ extension iOSViewVersion { @available(*, unavailable, message: "NavigationSplitView isn't available on iOS 15") public static let v15 = Self.unavailable() - public static let v16 = Self(for: .v16) + public static let v16 = Self(for: .v16, selector: selector) + + private static var selector: IntrospectionSelector { + .default.withAncestorSelector(\.splitViewController) + } } extension tvOSViewVersion { @@ -28,7 +32,11 @@ extension tvOSViewVersion { @available(*, unavailable, message: "NavigationSplitView isn't available on tvOS 15") public static let v15 = Self.unavailable() - public static let v16 = Self(for: .v16) + public static let v16 = Self(for: .v16, selector: selector) + + private static var selector: IntrospectionSelector { + .default.withAncestorSelector(\.navigationController) + } } #elseif canImport(AppKit) extension macOSViewVersion { diff --git a/Sources/ViewTypes/NavigationStack.swift b/Sources/ViewTypes/NavigationStack.swift index 7aa1028..a891760 100644 --- a/Sources/ViewTypes/NavigationStack.swift +++ b/Sources/ViewTypes/NavigationStack.swift @@ -17,7 +17,11 @@ extension iOSViewVersion { @available(*, unavailable, message: "NavigationStack isn't available on iOS 15") public static let v15 = Self.unavailable() - public static let v16 = Self(for: .v16) + public static let v16 = Self(for: .v16, selector: selector) + + private static var selector: IntrospectionSelector { + .default.withAncestorSelector(\.navigationController) + } } extension tvOSViewVersion { @@ -28,6 +32,10 @@ extension tvOSViewVersion { @available(*, unavailable, message: "NavigationStack isn't available on tvOS 15") public static let v15 = Self.unavailable() - public static let v16 = Self(for: .v16) + public static let v16 = Self(for: .v16, selector: selector) + + private static var selector: IntrospectionSelector { + .default.withAncestorSelector(\.navigationController) + } } #endif diff --git a/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift b/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift index 41231a0..e8e8d3a 100644 --- a/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift +++ b/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift @@ -14,17 +14,25 @@ extension IntrospectableViewType where Self == NavigationViewWithColumnsStyleTyp #if canImport(UIKit) extension iOSViewVersion { - public static let v13 = Self(for: .v13) - public static let v14 = Self(for: .v14) - public static let v15 = Self(for: .v15) - public static let v16 = Self(for: .v16) + public static let v13 = Self(for: .v13, selector: selector) + public static let v14 = Self(for: .v14, selector: selector) + public static let v15 = Self(for: .v15, selector: selector) + public static let v16 = Self(for: .v16, selector: selector) + + private static var selector: IntrospectionSelector { + .default.withAncestorSelector(\.splitViewController) + } } extension tvOSViewVersion { - public static let v13 = Self(for: .v13) - public static let v14 = Self(for: .v14) - public static let v15 = Self(for: .v15) - public static let v16 = Self(for: .v16) + public static let v13 = Self(for: .v13, selector: selector) + public static let v14 = Self(for: .v14, selector: selector) + public static let v15 = Self(for: .v15, selector: selector) + public static let v16 = Self(for: .v16, selector: selector) + + private static var selector: IntrospectionSelector { + .default.withAncestorSelector(\.navigationController) + } } #elseif canImport(AppKit) extension macOSViewVersion { diff --git a/Sources/ViewTypes/NavigationViewWithStackStyle.swift b/Sources/ViewTypes/NavigationViewWithStackStyle.swift index b6e1083..4507ddd 100644 --- a/Sources/ViewTypes/NavigationViewWithStackStyle.swift +++ b/Sources/ViewTypes/NavigationViewWithStackStyle.swift @@ -14,16 +14,24 @@ extension IntrospectableViewType where Self == NavigationViewWithStackStyleType #if canImport(UIKit) extension iOSViewVersion { - public static let v13 = Self(for: .v13) - public static let v14 = Self(for: .v14) - public static let v15 = Self(for: .v15) - public static let v16 = Self(for: .v16) + public static let v13 = Self(for: .v13, selector: selector) + public static let v14 = Self(for: .v14, selector: selector) + public static let v15 = Self(for: .v15, selector: selector) + public static let v16 = Self(for: .v16, selector: selector) + + private static var selector: IntrospectionSelector { + .default.withAncestorSelector(\.navigationController) + } } extension tvOSViewVersion { - public static let v13 = Self(for: .v13) - public static let v14 = Self(for: .v14) - public static let v15 = Self(for: .v15) - public static let v16 = Self(for: .v16) + public static let v13 = Self(for: .v13, selector: selector) + public static let v14 = Self(for: .v14, selector: selector) + public static let v15 = Self(for: .v15, selector: selector) + public static let v16 = Self(for: .v16, selector: selector) + + private static var selector: IntrospectionSelector { + .default.withAncestorSelector(\.navigationController) + } } #endif diff --git a/Sources/ViewTypes/TabView.swift b/Sources/ViewTypes/TabView.swift index 1f16a7f..ae22911 100644 --- a/Sources/ViewTypes/TabView.swift +++ b/Sources/ViewTypes/TabView.swift @@ -10,17 +10,25 @@ extension IntrospectableViewType where Self == TabViewType { #if canImport(UIKit) extension iOSViewVersion { - public static let v13 = Self(for: .v13) - public static let v14 = Self(for: .v14) - public static let v15 = Self(for: .v15) - public static let v16 = Self(for: .v16) + public static let v13 = Self(for: .v13, selector: selector) + public static let v14 = Self(for: .v14, selector: selector) + public static let v15 = Self(for: .v15, selector: selector) + public static let v16 = Self(for: .v16, selector: selector) + + private static var selector: IntrospectionSelector { + .default.withAncestorSelector(\.tabBarController) + } } extension tvOSViewVersion { - public static let v13 = Self(for: .v13) - public static let v14 = Self(for: .v14) - public static let v15 = Self(for: .v15) - public static let v16 = Self(for: .v16) + public static let v13 = Self(for: .v13, selector: selector) + public static let v14 = Self(for: .v14, selector: selector) + public static let v15 = Self(for: .v15, selector: selector) + public static let v16 = Self(for: .v16, selector: selector) + + private static var selector: IntrospectionSelector { + .default.withAncestorSelector(\.tabBarController) + } } #elseif canImport(AppKit) extension macOSViewVersion { From 67e2a59be1cf1c6dc4bb7a861cbea888d423bb78 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 5 Jun 2023 01:58:46 +0100 Subject: [PATCH 17/18] Bump to 0.5.2 (#241) --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc11223..f9c8d0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ Changelog ## master +## [0.5.2] + +- Added: selector overrides (#239) +- Changed: optimized ancestor controller selectors (#240) + ## [0.5.1] - Fixed: SwiftUIIntrospect.podspec (#237) From 930432278c347c24ef2aa4fd1b48c75170aa2108 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 6 Jun 2023 12:00:42 +0100 Subject: [PATCH 18/18] iOS 17 / tvOS 17 / macOS 14 support (#243) --- Examples/Showcase/Showcase/ContentView.swift | 74 +++++++++---------- Sources/PlatformVersion.swift | 30 ++++++++ Sources/ViewTypes/Button.swift | 1 + Sources/ViewTypes/ColorPicker.swift | 2 + Sources/ViewTypes/DatePicker.swift | 2 + .../DatePickerWithCompactStyle.swift | 2 + .../ViewTypes/DatePickerWithFieldStyle.swift | 1 + .../DatePickerWithGraphicalStyleType.swift | 2 + .../DatePickerWithStepperFieldStyle.swift | 1 + .../ViewTypes/DatePickerWithWheelStyle.swift | 1 + Sources/ViewTypes/Form.swift | 2 + Sources/ViewTypes/FormWithGroupedStyle.swift | 3 + Sources/ViewTypes/List.swift | 3 + Sources/ViewTypes/ListCell.swift | 3 + Sources/ViewTypes/ListWithBorderedStyle.swift | 1 + Sources/ViewTypes/ListWithGroupedStyle.swift | 2 + .../ViewTypes/ListWithInsetGroupedStyle.swift | 1 + Sources/ViewTypes/ListWithInsetStyle.swift | 2 + Sources/ViewTypes/ListWithSidebarStyle.swift | 2 + Sources/ViewTypes/NavigationSplitView.swift | 3 + Sources/ViewTypes/NavigationStack.swift | 2 + .../NavigationViewWithColumnsStyle.swift | 3 + .../NavigationViewWithStackStyle.swift | 2 + Sources/ViewTypes/PickerWithMenuStyle.swift | 1 + .../ViewTypes/PickerWithSegmentedStyle.swift | 3 + Sources/ViewTypes/PickerWithWheelStyle.swift | 1 + .../ProgressViewWithCircularStyle.swift | 3 + .../ProgressViewWithLinearStyle.swift | 3 + Sources/ViewTypes/ScrollView.swift | 3 + Sources/ViewTypes/SearchField.swift | 2 + Sources/ViewTypes/Slider.swift | 2 + Sources/ViewTypes/Stepper.swift | 2 + Sources/ViewTypes/TabView.swift | 3 + Sources/ViewTypes/TabViewWithPageStyle.swift | 2 + Sources/ViewTypes/Table.swift | 2 + Sources/ViewTypes/TextEditor.swift | 2 + Sources/ViewTypes/TextField.swift | 3 + .../ViewTypes/TextFieldWithVerticalAxis.swift | 3 + Sources/ViewTypes/Toggle.swift | 2 + Sources/ViewTypes/ToggleWithButtonStyle.swift | 1 + .../ViewTypes/ToggleWithCheckboxStyle.swift | 1 + Sources/ViewTypes/ToggleWithSwitchStyle.swift | 2 + Sources/ViewTypes/View.swift | 2 + Tests/Tests/PlatformTests.swift | 39 +++++++++- Tests/Tests/ViewTypes/ButtonTests.swift | 8 +- Tests/Tests/ViewTypes/ColorPickerTests.swift | 12 +-- Tests/Tests/ViewTypes/DatePickerTests.swift | 12 +-- ...DatePickerWithCompactFieldStyleTests.swift | 12 +-- .../DatePickerWithFieldStyleTests.swift | 6 +- .../DatePickerWithGraphicalStyleTests.swift | 12 +-- ...DatePickerWithStepperFieldStyleTests.swift | 6 +- .../DatePickerWithWheelStyleTests.swift | 6 +- Tests/Tests/ViewTypes/FormTests.swift | 8 +- .../ViewTypes/FormWithGroupedStyleTests.swift | 12 +-- Tests/Tests/ViewTypes/ListCellTests.swift | 12 +-- Tests/Tests/ViewTypes/ListTests.swift | 32 ++++---- .../ListWithBorderedStyleTests.swift | 4 +- .../ViewTypes/ListWithGroupedStyleTests.swift | 8 +- .../ListWithInsetGroupedStyleTests.swift | 4 +- .../ViewTypes/ListWithInsetStyleTests.swift | 8 +- .../ViewTypes/ListWithPlainStyleTests.swift | 12 +-- .../ViewTypes/ListWithSidebarStyleTests.swift | 8 +- .../ViewTypes/NavigationSplitViewTests.swift | 12 +-- .../ViewTypes/NavigationStackTests.swift | 4 +- .../NavigationViewWithColumnsStyleTests.swift | 14 ++-- .../NavigationViewWithStackStyleTests.swift | 4 +- .../ViewTypes/PickerWithMenuStyleTests.swift | 6 +- .../PickerWithSegmentedStyleTests.swift | 12 +-- .../ViewTypes/PickerWithWheelStyleTests.swift | 6 +- .../ProgressViewWithCircularStyleTests.swift | 12 +-- .../ProgressViewWithLinearStyleTests.swift | 12 +-- Tests/Tests/ViewTypes/ScrollViewTests.swift | 24 +++--- Tests/Tests/ViewTypes/SearchFieldTests.swift | 12 +-- Tests/Tests/ViewTypes/SliderTests.swift | 12 +-- Tests/Tests/ViewTypes/StepperTests.swift | 12 +-- Tests/Tests/ViewTypes/TabViewTests.swift | 8 +- .../ViewTypes/TabViewWithPageStyleTests.swift | 4 +- Tests/Tests/ViewTypes/TableTests.swift | 30 ++++---- Tests/Tests/ViewTypes/TextEditorTests.swift | 12 +-- Tests/Tests/ViewTypes/TextFieldTests.swift | 24 +++--- .../TextFieldWithVerticalAxisTests.swift | 18 ++--- Tests/Tests/ViewTypes/ToggleTests.swift | 12 +-- .../ToggleWithButtonStyleTests.swift | 6 +- .../ToggleWithCheckboxStyleTests.swift | 6 +- .../ToggleWithSwitchStyleTests.swift | 12 +-- Tests/Tests/ViewTypes/ViewTests.swift | 4 +- docs/SwiftUIIntrospect.md | 14 ++-- 87 files changed, 424 insertions(+), 277 deletions(-) diff --git a/Examples/Showcase/Showcase/ContentView.swift b/Examples/Showcase/Showcase/ContentView.swift index 70032fc..6d00095 100644 --- a/Examples/Showcase/Showcase/ContentView.swift +++ b/Examples/Showcase/Showcase/ContentView.swift @@ -25,11 +25,11 @@ struct ContentView: View { .tag(4) } #if os(iOS) || os(tvOS) - .introspect(.tabView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { tabBarController in + .introspect(.tabView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { tabBarController in tabBarController.tabBar.layer.backgroundColor = UIColor.green.cgColor } #elseif os(macOS) - .introspect(.tabView, on: .macOS(.v10_15, .v11, .v12, .v13)) { splitView in + .introspect(.tabView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { splitView in splitView.subviews.first?.layer?.backgroundColor = NSColor.green.cgColor } #endif @@ -62,16 +62,16 @@ struct ListShowcase: View { Text("Item 2") } #if os(iOS) || os(tvOS) - .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { tableView in + .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { tableView in tableView.backgroundView = UIView() tableView.backgroundColor = .cyan } - .introspect(.list, on: .iOS(.v16)) { collectionView in + .introspect(.list, on: .iOS(.v16, .v17)) { collectionView in collectionView.backgroundView = UIView() collectionView.subviews.dropFirst(1).first?.backgroundColor = .cyan } #elseif os(macOS) - .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13)) { tableView in + .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { tableView in tableView.backgroundColor = .cyan } #endif @@ -87,16 +87,16 @@ struct ListShowcase: View { Text("Item 1") Text("Item 2") #if os(iOS) || os(tvOS) - .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor) { tableView in + .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { tableView in tableView.backgroundView = UIView() tableView.backgroundColor = .cyan } - .introspect(.list, on: .iOS(.v16), scope: .ancestor) { collectionView in + .introspect(.list, on: .iOS(.v16, .v17), scope: .ancestor) { collectionView in collectionView.backgroundView = UIView() collectionView.subviews.dropFirst(1).first?.backgroundColor = .cyan } #elseif os(macOS) - .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13), scope: .ancestor) { tableView in + .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { tableView in tableView.backgroundColor = .cyan } #endif @@ -127,11 +127,11 @@ struct ScrollViewShowcase: View { .font(.system(.subheadline, design: .monospaced)) } #if os(iOS) || os(tvOS) - .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { scrollView in + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { scrollView in scrollView.layer.backgroundColor = UIColor.cyan.cgColor } #elseif os(macOS) - .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13)) { scrollView in + .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { scrollView in scrollView.drawsBackground = true scrollView.backgroundColor = .cyan } @@ -145,11 +145,11 @@ struct ScrollViewShowcase: View { .padding(.horizontal, 12) .font(.system(.subheadline, design: .monospaced)) #if os(iOS) || os(tvOS) - .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor) { scrollView in + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { scrollView in scrollView.layer.backgroundColor = UIColor.cyan.cgColor } #elseif os(macOS) - .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13), scope: .ancestor) { scrollView in + .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { scrollView in scrollView.drawsBackground = true scrollView.backgroundColor = .cyan } @@ -177,16 +177,16 @@ struct NavigationShowcase: View { #endif } #if os(iOS) || os(tvOS) - .introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { navigationController in + .introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { navigationController in navigationController.navigationBar.backgroundColor = .cyan } - .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16)) { splitViewController in + .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { splitViewController in splitViewController.preferredDisplayMode = .oneOverSecondary } - .introspect(.navigationView(style: .columns), on: .tvOS(.v13, .v14, .v15, .v16)) { navigationController in + .introspect(.navigationView(style: .columns), on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { navigationController in navigationController.navigationBar.backgroundColor = .cyan } - .introspect(.searchField, on: .iOS(.v15, .v16), .tvOS(.v15, .v16)) { searchBar in + .introspect(.searchField, on: .iOS(.v15, .v16, .v17), .tvOS(.v15, .v16, .v17)) { searchBar in searchBar.backgroundColor = .red #if os(iOS) searchBar.searchTextField.backgroundColor = .purple @@ -209,7 +209,7 @@ struct ViewControllerShowcase: View { } } .navigationViewStyle(.stack) - .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { viewController in + .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { viewController in viewController.children.first?.view.backgroundColor = .cyan } } @@ -229,11 +229,11 @@ struct SimpleElementsShowcase: View { HStack { TextField("Text Field Red", text: $textFieldValue) #if os(iOS) || os(tvOS) - .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { textField in + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { textField in textField.backgroundColor = .red } #elseif os(macOS) - .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13)) { textField in + .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { textField in textField.backgroundColor = .red } #endif @@ -241,11 +241,11 @@ struct SimpleElementsShowcase: View { TextField("Text Field Green", text: $textFieldValue) .cornerRadius(8) #if os(iOS) || os(tvOS) - .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { textField in + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { textField in textField.backgroundColor = .green } #elseif os(macOS) - .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13)) { textField in + .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { textField in textField.backgroundColor = .green } #endif @@ -254,22 +254,22 @@ struct SimpleElementsShowcase: View { HStack { Toggle("Toggle Red", isOn: $toggleValue) #if os(iOS) - .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16)) { toggle in + .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { toggle in toggle.backgroundColor = .red } #elseif os(macOS) - .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13)) { toggle in + .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { toggle in toggle.layer?.backgroundColor = NSColor.red.cgColor } #endif Toggle("Toggle Green", isOn: $toggleValue) #if os(iOS) - .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16)) { toggle in + .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { toggle in toggle.backgroundColor = .green } #elseif os(macOS) - .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13)) { toggle in + .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { toggle in toggle.layer?.backgroundColor = NSColor.green.cgColor } #endif @@ -279,22 +279,22 @@ struct SimpleElementsShowcase: View { HStack { Slider(value: $sliderValue, in: 0...100) #if os(iOS) - .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16)) { slider in + .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { slider in slider.backgroundColor = .red } #elseif os(macOS) - .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13)) { slider in + .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { slider in slider.layer?.backgroundColor = NSColor.red.cgColor } #endif Slider(value: $sliderValue, in: 0...100) #if os(iOS) - .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16)) { slider in + .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { slider in slider.backgroundColor = .green } #elseif os(macOS) - .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13)) { slider in + .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { slider in slider.layer?.backgroundColor = NSColor.green.cgColor } #endif @@ -305,11 +305,11 @@ struct SimpleElementsShowcase: View { Text("Stepper Red") } #if os(iOS) - .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16)) { stepper in + .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { stepper in stepper.backgroundColor = .red } #elseif os(macOS) - .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13)) { stepper in + .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { stepper in stepper.layer?.backgroundColor = NSColor.red.cgColor } #endif @@ -318,11 +318,11 @@ struct SimpleElementsShowcase: View { Text("Stepper Green") } #if os(iOS) - .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16)) { stepper in + .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { stepper in stepper.backgroundColor = .green } #elseif os(macOS) - .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13)) { stepper in + .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { stepper in stepper.layer?.backgroundColor = NSColor.green.cgColor } #endif @@ -333,11 +333,11 @@ struct SimpleElementsShowcase: View { Text("DatePicker Red") } #if os(iOS) - .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16)) { datePicker in + .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { datePicker in datePicker.backgroundColor = .red } #elseif os(macOS) - .introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13)) { datePicker in + .introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { datePicker in datePicker.layer?.backgroundColor = NSColor.red.cgColor } #endif @@ -352,11 +352,11 @@ struct SimpleElementsShowcase: View { } .pickerStyle(SegmentedPickerStyle()) #if os(iOS) || os(tvOS) - .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { datePicker in + .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { datePicker in datePicker.backgroundColor = .red } #elseif os(macOS) - .introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13)) { datePicker in + .introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { datePicker in datePicker.layer?.backgroundColor = NSColor.red.cgColor } #endif diff --git a/Sources/PlatformVersion.swift b/Sources/PlatformVersion.swift index 332ef5f..194b597 100644 --- a/Sources/PlatformVersion.swift +++ b/Sources/PlatformVersion.swift @@ -52,6 +52,16 @@ extension iOSVersion { } return false } + + public static let v17 = iOSVersion { + if #available(iOS 18, *) { + return false + } + if #available(iOS 17, *) { + return true + } + return false + } } public struct tvOSVersion: PlatformVersion { @@ -102,6 +112,16 @@ extension tvOSVersion { } return false } + + public static let v17 = tvOSVersion { + if #available(tvOS 18, *) { + return false + } + if #available(tvOS 17, *) { + return true + } + return false + } } public struct macOSVersion: PlatformVersion { @@ -162,4 +182,14 @@ extension macOSVersion { } return false } + + public static let v14 = macOSVersion { + if #available(macOS 15, *) { + return false + } + if #available(macOS 14, *) { + return true + } + return false + } } diff --git a/Sources/ViewTypes/Button.swift b/Sources/ViewTypes/Button.swift index 5cb94cf..7181086 100644 --- a/Sources/ViewTypes/Button.swift +++ b/Sources/ViewTypes/Button.swift @@ -15,6 +15,7 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif #endif diff --git a/Sources/ViewTypes/ColorPicker.swift b/Sources/ViewTypes/ColorPicker.swift index 943cd75..97f6547 100644 --- a/Sources/ViewTypes/ColorPicker.swift +++ b/Sources/ViewTypes/ColorPicker.swift @@ -17,6 +17,7 @@ extension iOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #elseif canImport(AppKit) @available(macOS 11, *) @@ -26,6 +27,7 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif #endif diff --git a/Sources/ViewTypes/DatePicker.swift b/Sources/ViewTypes/DatePicker.swift index 0f9e37a..bd4ac0c 100644 --- a/Sources/ViewTypes/DatePicker.swift +++ b/Sources/ViewTypes/DatePicker.swift @@ -15,6 +15,7 @@ extension iOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #elseif canImport(AppKit) extension macOSViewVersion { @@ -22,6 +23,7 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif #endif diff --git a/Sources/ViewTypes/DatePickerWithCompactStyle.swift b/Sources/ViewTypes/DatePickerWithCompactStyle.swift index b6d9a97..efc36cf 100644 --- a/Sources/ViewTypes/DatePickerWithCompactStyle.swift +++ b/Sources/ViewTypes/DatePickerWithCompactStyle.swift @@ -20,6 +20,7 @@ extension iOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #elseif canImport(AppKit) && !targetEnvironment(macCatalyst) extension macOSViewVersion { @@ -29,6 +30,7 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif #endif diff --git a/Sources/ViewTypes/DatePickerWithFieldStyle.swift b/Sources/ViewTypes/DatePickerWithFieldStyle.swift index a5d04ac..85acc64 100644 --- a/Sources/ViewTypes/DatePickerWithFieldStyle.swift +++ b/Sources/ViewTypes/DatePickerWithFieldStyle.swift @@ -19,6 +19,7 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif #endif diff --git a/Sources/ViewTypes/DatePickerWithGraphicalStyleType.swift b/Sources/ViewTypes/DatePickerWithGraphicalStyleType.swift index 7ef7eab..f8a328f 100644 --- a/Sources/ViewTypes/DatePickerWithGraphicalStyleType.swift +++ b/Sources/ViewTypes/DatePickerWithGraphicalStyleType.swift @@ -20,6 +20,7 @@ extension iOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #elseif canImport(AppKit) && !targetEnvironment(macCatalyst) extension macOSViewVersion { @@ -27,6 +28,7 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif #endif diff --git a/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift b/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift index 690748f..8d9236c 100644 --- a/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift +++ b/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift @@ -19,6 +19,7 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif #endif diff --git a/Sources/ViewTypes/DatePickerWithWheelStyle.swift b/Sources/ViewTypes/DatePickerWithWheelStyle.swift index 991ac17..ae84ab1 100644 --- a/Sources/ViewTypes/DatePickerWithWheelStyle.swift +++ b/Sources/ViewTypes/DatePickerWithWheelStyle.swift @@ -19,6 +19,7 @@ extension iOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #endif #endif diff --git a/Sources/ViewTypes/Form.swift b/Sources/ViewTypes/Form.swift index cf7a70f..f38bae2 100644 --- a/Sources/ViewTypes/Form.swift +++ b/Sources/ViewTypes/Form.swift @@ -18,6 +18,7 @@ extension iOSViewVersion { extension iOSViewVersion { public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } extension tvOSViewVersion { @@ -25,6 +26,7 @@ extension tvOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #endif #endif diff --git a/Sources/ViewTypes/FormWithGroupedStyle.swift b/Sources/ViewTypes/FormWithGroupedStyle.swift index a8b333d..09800f9 100644 --- a/Sources/ViewTypes/FormWithGroupedStyle.swift +++ b/Sources/ViewTypes/FormWithGroupedStyle.swift @@ -24,6 +24,7 @@ extension iOSViewVersion { extension iOSViewVersion { public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } extension tvOSViewVersion { @@ -34,6 +35,7 @@ extension tvOSViewVersion { @available(*, unavailable, message: ".formStyle(.grouped) isn't available on tvOS 15") public static let v15 = Self.unavailable() public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #elseif canImport(AppKit) extension macOSViewVersion { @@ -44,5 +46,6 @@ extension macOSViewVersion { @available(*, unavailable, message: ".formStyle(.grouped) isn't available on macOS 12") public static let v12 = Self.unavailable() public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif diff --git a/Sources/ViewTypes/List.swift b/Sources/ViewTypes/List.swift index a3bbdb1..74f54b5 100644 --- a/Sources/ViewTypes/List.swift +++ b/Sources/ViewTypes/List.swift @@ -22,6 +22,7 @@ extension iOSViewVersion { extension iOSViewVersion { public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } extension tvOSViewVersion { @@ -29,6 +30,7 @@ extension tvOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #elseif canImport(AppKit) extension macOSViewVersion { @@ -36,5 +38,6 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif diff --git a/Sources/ViewTypes/ListCell.swift b/Sources/ViewTypes/ListCell.swift index 8f5e3de..396f65d 100644 --- a/Sources/ViewTypes/ListCell.swift +++ b/Sources/ViewTypes/ListCell.swift @@ -19,6 +19,7 @@ extension iOSViewVersion { extension iOSViewVersion { public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } extension tvOSViewVersion { @@ -26,6 +27,7 @@ extension tvOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #elseif canImport(AppKit) extension macOSViewVersion { @@ -33,5 +35,6 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif diff --git a/Sources/ViewTypes/ListWithBorderedStyle.swift b/Sources/ViewTypes/ListWithBorderedStyle.swift index 4136a1a..f64ffc2 100644 --- a/Sources/ViewTypes/ListWithBorderedStyle.swift +++ b/Sources/ViewTypes/ListWithBorderedStyle.swift @@ -21,6 +21,7 @@ extension macOSViewVersion { public static let v11 = Self.unavailable() public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif #endif diff --git a/Sources/ViewTypes/ListWithGroupedStyle.swift b/Sources/ViewTypes/ListWithGroupedStyle.swift index b96fab0..7661b5e 100644 --- a/Sources/ViewTypes/ListWithGroupedStyle.swift +++ b/Sources/ViewTypes/ListWithGroupedStyle.swift @@ -22,6 +22,7 @@ extension iOSViewVersion { extension iOSViewVersion { public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } extension tvOSViewVersion { @@ -29,6 +30,7 @@ extension tvOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #endif #endif diff --git a/Sources/ViewTypes/ListWithInsetGroupedStyle.swift b/Sources/ViewTypes/ListWithInsetGroupedStyle.swift index b40d282..5d6f2c3 100644 --- a/Sources/ViewTypes/ListWithInsetGroupedStyle.swift +++ b/Sources/ViewTypes/ListWithInsetGroupedStyle.swift @@ -23,6 +23,7 @@ extension iOSViewVersion { extension iOSViewVersion { public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #endif #endif diff --git a/Sources/ViewTypes/ListWithInsetStyle.swift b/Sources/ViewTypes/ListWithInsetStyle.swift index 9a09fd6..2e33b2e 100644 --- a/Sources/ViewTypes/ListWithInsetStyle.swift +++ b/Sources/ViewTypes/ListWithInsetStyle.swift @@ -23,6 +23,7 @@ extension iOSViewVersion { extension iOSViewVersion { public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #elseif canImport(AppKit) extension macOSViewVersion { @@ -31,6 +32,7 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif #endif diff --git a/Sources/ViewTypes/ListWithSidebarStyle.swift b/Sources/ViewTypes/ListWithSidebarStyle.swift index 6820bf7..8a64127 100644 --- a/Sources/ViewTypes/ListWithSidebarStyle.swift +++ b/Sources/ViewTypes/ListWithSidebarStyle.swift @@ -23,6 +23,7 @@ extension iOSViewVersion { extension iOSViewVersion { public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #elseif canImport(AppKit) extension macOSViewVersion { @@ -30,6 +31,7 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif #endif diff --git a/Sources/ViewTypes/NavigationSplitView.swift b/Sources/ViewTypes/NavigationSplitView.swift index a7eec20..bd669a0 100644 --- a/Sources/ViewTypes/NavigationSplitView.swift +++ b/Sources/ViewTypes/NavigationSplitView.swift @@ -18,6 +18,7 @@ extension iOSViewVersion { public static let v15 = Self.unavailable() public static let v16 = Self(for: .v16, selector: selector) + public static let v17 = Self(for: .v17, selector: selector) private static var selector: IntrospectionSelector { .default.withAncestorSelector(\.splitViewController) @@ -33,6 +34,7 @@ extension tvOSViewVersion { public static let v15 = Self.unavailable() public static let v16 = Self(for: .v16, selector: selector) + public static let v17 = Self(for: .v17, selector: selector) private static var selector: IntrospectionSelector { .default.withAncestorSelector(\.navigationController) @@ -48,5 +50,6 @@ extension macOSViewVersion { public static let v12 = Self.unavailable() public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif diff --git a/Sources/ViewTypes/NavigationStack.swift b/Sources/ViewTypes/NavigationStack.swift index a891760..0cc431f 100644 --- a/Sources/ViewTypes/NavigationStack.swift +++ b/Sources/ViewTypes/NavigationStack.swift @@ -18,6 +18,7 @@ extension iOSViewVersion { public static let v15 = Self.unavailable() public static let v16 = Self(for: .v16, selector: selector) + public static let v17 = Self(for: .v17, selector: selector) private static var selector: IntrospectionSelector { .default.withAncestorSelector(\.navigationController) @@ -33,6 +34,7 @@ extension tvOSViewVersion { public static let v15 = Self.unavailable() public static let v16 = Self(for: .v16, selector: selector) + public static let v17 = Self(for: .v17, selector: selector) private static var selector: IntrospectionSelector { .default.withAncestorSelector(\.navigationController) diff --git a/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift b/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift index e8e8d3a..b31bf33 100644 --- a/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift +++ b/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift @@ -18,6 +18,7 @@ extension iOSViewVersion { .default.withAncestorSelector(\.splitViewController) @@ -29,6 +30,7 @@ extension tvOSViewVersion { .default.withAncestorSelector(\.navigationController) @@ -40,5 +42,6 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif diff --git a/Sources/ViewTypes/NavigationViewWithStackStyle.swift b/Sources/ViewTypes/NavigationViewWithStackStyle.swift index 4507ddd..798a2d2 100644 --- a/Sources/ViewTypes/NavigationViewWithStackStyle.swift +++ b/Sources/ViewTypes/NavigationViewWithStackStyle.swift @@ -18,6 +18,7 @@ extension iOSViewVersion { .default.withAncestorSelector(\.navigationController) @@ -29,6 +30,7 @@ extension tvOSViewVersion { .default.withAncestorSelector(\.navigationController) diff --git a/Sources/ViewTypes/PickerWithMenuStyle.swift b/Sources/ViewTypes/PickerWithMenuStyle.swift index b8e98c6..9ff617f 100644 --- a/Sources/ViewTypes/PickerWithMenuStyle.swift +++ b/Sources/ViewTypes/PickerWithMenuStyle.swift @@ -20,6 +20,7 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif #endif diff --git a/Sources/ViewTypes/PickerWithSegmentedStyle.swift b/Sources/ViewTypes/PickerWithSegmentedStyle.swift index e8f93d6..9a85f63 100644 --- a/Sources/ViewTypes/PickerWithSegmentedStyle.swift +++ b/Sources/ViewTypes/PickerWithSegmentedStyle.swift @@ -18,6 +18,7 @@ extension iOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } extension tvOSViewVersion { @@ -25,6 +26,7 @@ extension tvOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #elseif canImport(AppKit) extension macOSViewVersion { @@ -32,5 +34,6 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif diff --git a/Sources/ViewTypes/PickerWithWheelStyle.swift b/Sources/ViewTypes/PickerWithWheelStyle.swift index aa97a11..b6de6d0 100644 --- a/Sources/ViewTypes/PickerWithWheelStyle.swift +++ b/Sources/ViewTypes/PickerWithWheelStyle.swift @@ -19,6 +19,7 @@ extension iOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #endif #endif diff --git a/Sources/ViewTypes/ProgressViewWithCircularStyle.swift b/Sources/ViewTypes/ProgressViewWithCircularStyle.swift index 38f017b..1a7f988 100644 --- a/Sources/ViewTypes/ProgressViewWithCircularStyle.swift +++ b/Sources/ViewTypes/ProgressViewWithCircularStyle.swift @@ -19,6 +19,7 @@ extension iOSViewVersion { @@ -27,6 +28,7 @@ extension tvOSViewVersion { @@ -35,5 +37,6 @@ extension macOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } extension tvOSViewVersion { @@ -27,6 +28,7 @@ extension tvOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #elseif canImport(AppKit) extension macOSViewVersion { @@ -35,5 +37,6 @@ extension macOSViewVersion public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif diff --git a/Sources/ViewTypes/ScrollView.swift b/Sources/ViewTypes/ScrollView.swift index edd6cf7..9171185 100644 --- a/Sources/ViewTypes/ScrollView.swift +++ b/Sources/ViewTypes/ScrollView.swift @@ -14,6 +14,7 @@ extension iOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } extension tvOSViewVersion { @@ -21,6 +22,7 @@ extension tvOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #elseif canImport(AppKit) extension macOSViewVersion { @@ -28,5 +30,6 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif diff --git a/Sources/ViewTypes/SearchField.swift b/Sources/ViewTypes/SearchField.swift index 1ee82d7..13ade58 100644 --- a/Sources/ViewTypes/SearchField.swift +++ b/Sources/ViewTypes/SearchField.swift @@ -16,6 +16,7 @@ extension iOSViewVersion { public static let v14 = Self.unavailable() public static let v15 = Self(for: .v15, selector: selector) public static let v16 = Self(for: .v16, selector: selector) + public static let v17 = Self(for: .v17, selector: selector) private static var selector: IntrospectionSelector { .from(UINavigationController.self) { @@ -31,6 +32,7 @@ extension tvOSViewVersion { public static let v14 = Self.unavailable() public static let v15 = Self(for: .v15, selector: selector) public static let v16 = Self(for: .v16, selector: selector) + public static let v17 = Self(for: .v17, selector: selector) private static var selector: IntrospectionSelector { .from(UINavigationController.self) { diff --git a/Sources/ViewTypes/Slider.swift b/Sources/ViewTypes/Slider.swift index 2c7d6e1..6c5fd01 100644 --- a/Sources/ViewTypes/Slider.swift +++ b/Sources/ViewTypes/Slider.swift @@ -15,6 +15,7 @@ extension iOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #elseif canImport(AppKit) extension macOSViewVersion { @@ -22,6 +23,7 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif #endif diff --git a/Sources/ViewTypes/Stepper.swift b/Sources/ViewTypes/Stepper.swift index d4d952a..da2ea55 100644 --- a/Sources/ViewTypes/Stepper.swift +++ b/Sources/ViewTypes/Stepper.swift @@ -15,6 +15,7 @@ extension iOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #elseif canImport(AppKit) extension macOSViewVersion { @@ -22,6 +23,7 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif #endif diff --git a/Sources/ViewTypes/TabView.swift b/Sources/ViewTypes/TabView.swift index ae22911..1001217 100644 --- a/Sources/ViewTypes/TabView.swift +++ b/Sources/ViewTypes/TabView.swift @@ -14,6 +14,7 @@ extension iOSViewVersion { public static let v14 = Self(for: .v14, selector: selector) public static let v15 = Self(for: .v15, selector: selector) public static let v16 = Self(for: .v16, selector: selector) + public static let v17 = Self(for: .v17, selector: selector) private static var selector: IntrospectionSelector { .default.withAncestorSelector(\.tabBarController) @@ -25,6 +26,7 @@ extension tvOSViewVersion { public static let v14 = Self(for: .v14, selector: selector) public static let v15 = Self(for: .v15, selector: selector) public static let v16 = Self(for: .v16, selector: selector) + public static let v17 = Self(for: .v17, selector: selector) private static var selector: IntrospectionSelector { .default.withAncestorSelector(\.tabBarController) @@ -36,5 +38,6 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif diff --git a/Sources/ViewTypes/TabViewWithPageStyle.swift b/Sources/ViewTypes/TabViewWithPageStyle.swift index 0c904ee..9bf1136 100644 --- a/Sources/ViewTypes/TabViewWithPageStyle.swift +++ b/Sources/ViewTypes/TabViewWithPageStyle.swift @@ -20,6 +20,7 @@ extension iOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } extension tvOSViewVersion { @@ -28,6 +29,7 @@ extension tvOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #endif #endif diff --git a/Sources/ViewTypes/Table.swift b/Sources/ViewTypes/Table.swift index 4c6aefd..5534c4b 100644 --- a/Sources/ViewTypes/Table.swift +++ b/Sources/ViewTypes/Table.swift @@ -18,6 +18,7 @@ extension iOSViewVersion { @available(*, unavailable, message: "Table isn't available on iOS 15") public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #elseif canImport(AppKit) && !targetEnvironment(macCatalyst) extension macOSViewVersion { @@ -27,6 +28,7 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif #endif diff --git a/Sources/ViewTypes/TextEditor.swift b/Sources/ViewTypes/TextEditor.swift index 629d146..b451ae0 100644 --- a/Sources/ViewTypes/TextEditor.swift +++ b/Sources/ViewTypes/TextEditor.swift @@ -16,6 +16,7 @@ extension iOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #elseif canImport(AppKit) extension macOSViewVersion { @@ -24,6 +25,7 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif #endif diff --git a/Sources/ViewTypes/TextField.swift b/Sources/ViewTypes/TextField.swift index d95d65b..ae5c750 100644 --- a/Sources/ViewTypes/TextField.swift +++ b/Sources/ViewTypes/TextField.swift @@ -14,6 +14,7 @@ extension iOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } extension tvOSViewVersion { @@ -21,6 +22,7 @@ extension tvOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #elseif canImport(AppKit) extension macOSViewVersion { @@ -28,5 +30,6 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif diff --git a/Sources/ViewTypes/TextFieldWithVerticalAxis.swift b/Sources/ViewTypes/TextFieldWithVerticalAxis.swift index 579a116..b0e29e3 100644 --- a/Sources/ViewTypes/TextFieldWithVerticalAxis.swift +++ b/Sources/ViewTypes/TextFieldWithVerticalAxis.swift @@ -24,6 +24,7 @@ extension iOSViewVersion { public static let v15 = Self.unavailable() public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } extension tvOSViewVersion { @@ -35,6 +36,7 @@ extension tvOSViewVersion { public static let v15 = Self.unavailable() public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #elseif canImport(AppKit) extension macOSViewVersion { @@ -46,5 +48,6 @@ extension macOSViewVersion { public static let v12 = Self.unavailable() public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif diff --git a/Sources/ViewTypes/Toggle.swift b/Sources/ViewTypes/Toggle.swift index 27a0012..dfd7633 100644 --- a/Sources/ViewTypes/Toggle.swift +++ b/Sources/ViewTypes/Toggle.swift @@ -15,6 +15,7 @@ extension iOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #elseif canImport(AppKit) extension macOSViewVersion { @@ -22,6 +23,7 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif #endif diff --git a/Sources/ViewTypes/ToggleWithButtonStyle.swift b/Sources/ViewTypes/ToggleWithButtonStyle.swift index b530bcd..cf77f86 100644 --- a/Sources/ViewTypes/ToggleWithButtonStyle.swift +++ b/Sources/ViewTypes/ToggleWithButtonStyle.swift @@ -21,6 +21,7 @@ extension macOSViewVersion { public static let v11 = Self.unavailable() public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif #endif diff --git a/Sources/ViewTypes/ToggleWithCheckboxStyle.swift b/Sources/ViewTypes/ToggleWithCheckboxStyle.swift index bb2e01e..1fcf049 100644 --- a/Sources/ViewTypes/ToggleWithCheckboxStyle.swift +++ b/Sources/ViewTypes/ToggleWithCheckboxStyle.swift @@ -19,6 +19,7 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif #endif diff --git a/Sources/ViewTypes/ToggleWithSwitchStyle.swift b/Sources/ViewTypes/ToggleWithSwitchStyle.swift index f4ca188..494c62a 100644 --- a/Sources/ViewTypes/ToggleWithSwitchStyle.swift +++ b/Sources/ViewTypes/ToggleWithSwitchStyle.swift @@ -19,6 +19,7 @@ extension iOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #elseif canImport(AppKit) extension macOSViewVersion { @@ -26,6 +27,7 @@ extension macOSViewVersion { public static let v11 = Self(for: .v11) public static let v12 = Self(for: .v12) public static let v13 = Self(for: .v13) + public static let v14 = Self(for: .v14) } #endif #endif diff --git a/Sources/ViewTypes/View.swift b/Sources/ViewTypes/View.swift index c571e4e..2415e8c 100644 --- a/Sources/ViewTypes/View.swift +++ b/Sources/ViewTypes/View.swift @@ -16,6 +16,7 @@ extension iOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } extension tvOSViewVersion { @@ -23,5 +24,6 @@ extension tvOSViewVersion { public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) public static let v16 = Self(for: .v16) + public static let v17 = Self(for: .v17) } #endif diff --git a/Tests/Tests/PlatformTests.swift b/Tests/Tests/PlatformTests.swift index a41c133..0c736fa 100644 --- a/Tests/Tests/PlatformTests.swift +++ b/Tests/Tests/PlatformTests.swift @@ -4,28 +4,39 @@ import XCTest final class PlatformTests: XCTestCase { func test_iOS() { #if os(iOS) - if #available(iOS 16, *) { + if #available(iOS 17, *) { + XCTAssertEqual(iOSVersion.v17.isCurrent, true) + XCTAssertEqual(iOSVersion.v16.isCurrent, false) + XCTAssertEqual(iOSVersion.v15.isCurrent, false) + XCTAssertEqual(iOSVersion.v14.isCurrent, false) + XCTAssertEqual(iOSVersion.v13.isCurrent, false) + } else if #available(iOS 16, *) { + XCTAssertEqual(iOSVersion.v17.isCurrent, false) XCTAssertEqual(iOSVersion.v16.isCurrent, true) XCTAssertEqual(iOSVersion.v15.isCurrent, false) XCTAssertEqual(iOSVersion.v14.isCurrent, false) XCTAssertEqual(iOSVersion.v13.isCurrent, false) } else if #available(iOS 15, *) { + XCTAssertEqual(iOSVersion.v17.isCurrent, false) XCTAssertEqual(iOSVersion.v16.isCurrent, false) XCTAssertEqual(iOSVersion.v15.isCurrent, true) XCTAssertEqual(iOSVersion.v14.isCurrent, false) XCTAssertEqual(iOSVersion.v13.isCurrent, false) } else if #available(iOS 14, *) { + XCTAssertEqual(iOSVersion.v17.isCurrent, false) XCTAssertEqual(iOSVersion.v16.isCurrent, false) XCTAssertEqual(iOSVersion.v15.isCurrent, false) XCTAssertEqual(iOSVersion.v14.isCurrent, true) XCTAssertEqual(iOSVersion.v13.isCurrent, false) } else if #available(iOS 13, *) { + XCTAssertEqual(iOSVersion.v17.isCurrent, false) XCTAssertEqual(iOSVersion.v16.isCurrent, false) XCTAssertEqual(iOSVersion.v15.isCurrent, false) XCTAssertEqual(iOSVersion.v14.isCurrent, false) XCTAssertEqual(iOSVersion.v13.isCurrent, true) } #else + XCTAssertEqual(iOSVersion.v17.isCurrent, false) XCTAssertEqual(iOSVersion.v16.isCurrent, false) XCTAssertEqual(iOSVersion.v15.isCurrent, false) XCTAssertEqual(iOSVersion.v14.isCurrent, false) @@ -35,28 +46,39 @@ final class PlatformTests: XCTestCase { func test_macOS() { #if os(macOS) - if #available(macOS 13, *) { + if #available(macOS 14, *) { + XCTAssertEqual(macOSVersion.v14.isCurrent, true) + XCTAssertEqual(macOSVersion.v13.isCurrent, false) + XCTAssertEqual(macOSVersion.v12.isCurrent, false) + XCTAssertEqual(macOSVersion.v11.isCurrent, false) + XCTAssertEqual(macOSVersion.v10_15.isCurrent, false) + } else if #available(macOS 13, *) { + XCTAssertEqual(macOSVersion.v14.isCurrent, false) XCTAssertEqual(macOSVersion.v13.isCurrent, true) XCTAssertEqual(macOSVersion.v12.isCurrent, false) XCTAssertEqual(macOSVersion.v11.isCurrent, false) XCTAssertEqual(macOSVersion.v10_15.isCurrent, false) } else if #available(macOS 12, *) { + XCTAssertEqual(macOSVersion.v14.isCurrent, false) XCTAssertEqual(macOSVersion.v13.isCurrent, false) XCTAssertEqual(macOSVersion.v12.isCurrent, true) XCTAssertEqual(macOSVersion.v11.isCurrent, false) XCTAssertEqual(macOSVersion.v10_15.isCurrent, false) } else if #available(macOS 11, *) { + XCTAssertEqual(macOSVersion.v14.isCurrent, false) XCTAssertEqual(macOSVersion.v13.isCurrent, false) XCTAssertEqual(macOSVersion.v12.isCurrent, false) XCTAssertEqual(macOSVersion.v11.isCurrent, true) XCTAssertEqual(macOSVersion.v10_15.isCurrent, false) } else if #available(macOS 10.15, *) { + XCTAssertEqual(macOSVersion.v14.isCurrent, false) XCTAssertEqual(macOSVersion.v13.isCurrent, false) XCTAssertEqual(macOSVersion.v12.isCurrent, false) XCTAssertEqual(macOSVersion.v11.isCurrent, false) XCTAssertEqual(macOSVersion.v10_15.isCurrent, true) } #else + XCTAssertEqual(macOSVersion.v14.isCurrent, false) XCTAssertEqual(macOSVersion.v13.isCurrent, false) XCTAssertEqual(macOSVersion.v12.isCurrent, false) XCTAssertEqual(macOSVersion.v11.isCurrent, false) @@ -66,28 +88,39 @@ final class PlatformTests: XCTestCase { func test_tvOS() { #if os(tvOS) - if #available(tvOS 16, *) { + if #available(tvOS 17, *) { + XCTAssertEqual(tvOSVersion.v17.isCurrent, true) + XCTAssertEqual(tvOSVersion.v16.isCurrent, false) + XCTAssertEqual(tvOSVersion.v15.isCurrent, false) + XCTAssertEqual(tvOSVersion.v14.isCurrent, false) + XCTAssertEqual(tvOSVersion.v13.isCurrent, false) + } else if #available(tvOS 16, *) { + XCTAssertEqual(tvOSVersion.v17.isCurrent, false) XCTAssertEqual(tvOSVersion.v16.isCurrent, true) XCTAssertEqual(tvOSVersion.v15.isCurrent, false) XCTAssertEqual(tvOSVersion.v14.isCurrent, false) XCTAssertEqual(tvOSVersion.v13.isCurrent, false) } else if #available(tvOS 15, *) { + XCTAssertEqual(tvOSVersion.v17.isCurrent, false) XCTAssertEqual(tvOSVersion.v16.isCurrent, false) XCTAssertEqual(tvOSVersion.v15.isCurrent, true) XCTAssertEqual(tvOSVersion.v14.isCurrent, false) XCTAssertEqual(tvOSVersion.v13.isCurrent, false) } else if #available(tvOS 14, *) { + XCTAssertEqual(tvOSVersion.v17.isCurrent, false) XCTAssertEqual(tvOSVersion.v16.isCurrent, false) XCTAssertEqual(tvOSVersion.v15.isCurrent, false) XCTAssertEqual(tvOSVersion.v14.isCurrent, true) XCTAssertEqual(tvOSVersion.v13.isCurrent, false) } else if #available(tvOS 13, *) { + XCTAssertEqual(tvOSVersion.v17.isCurrent, false) XCTAssertEqual(tvOSVersion.v16.isCurrent, false) XCTAssertEqual(tvOSVersion.v15.isCurrent, false) XCTAssertEqual(tvOSVersion.v14.isCurrent, false) XCTAssertEqual(tvOSVersion.v13.isCurrent, true) } #else + XCTAssertEqual(tvOSVersion.v17.isCurrent, false) XCTAssertEqual(tvOSVersion.v16.isCurrent, false) XCTAssertEqual(tvOSVersion.v15.isCurrent, false) XCTAssertEqual(tvOSVersion.v14.isCurrent, false) diff --git a/Tests/Tests/ViewTypes/ButtonTests.swift b/Tests/Tests/ViewTypes/ButtonTests.swift index 4bdbb06..acbb392 100644 --- a/Tests/Tests/ViewTypes/ButtonTests.swift +++ b/Tests/Tests/ViewTypes/ButtonTests.swift @@ -19,24 +19,24 @@ final class ButtonTests: XCTestCase { Button("Button 0", action: {}) .buttonStyle(.bordered) #if os(macOS) - .introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + .introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif Button("Button 1", action: {}) .buttonStyle(.borderless) #if os(macOS) - .introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + .introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) #endif Button("Button 2", action: {}) .buttonStyle(.link) #if os(macOS) - .introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + .introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif Button("Button 3", action: {}) #if os(macOS) - .introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy3) + .introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy3) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/ColorPickerTests.swift b/Tests/Tests/ViewTypes/ColorPickerTests.swift index 87d5d26..0f550a2 100644 --- a/Tests/Tests/ViewTypes/ColorPickerTests.swift +++ b/Tests/Tests/ViewTypes/ColorPickerTests.swift @@ -26,23 +26,23 @@ final class ColorPickerTests: XCTestCase { VStack { ColorPicker("", selection: .constant(PlatformColor.red.cgColor)) #if os(iOS) - .introspect(.colorPicker, on: .iOS(.v14, .v15, .v16), customize: spy0) + .introspect(.colorPicker, on: .iOS(.v14, .v15, .v16, .v17), customize: spy0) #elseif os(macOS) - .introspect(.colorPicker, on: .macOS(.v11, .v12, .v13), customize: spy0) + .introspect(.colorPicker, on: .macOS(.v11, .v12, .v13, .v14), customize: spy0) #endif ColorPicker("", selection: .constant(PlatformColor.green.cgColor)) #if os(iOS) - .introspect(.colorPicker, on: .iOS(.v14, .v15, .v16), customize: spy1) + .introspect(.colorPicker, on: .iOS(.v14, .v15, .v16, .v17), customize: spy1) #elseif os(macOS) - .introspect(.colorPicker, on: .macOS(.v11, .v12, .v13), customize: spy1) + .introspect(.colorPicker, on: .macOS(.v11, .v12, .v13, .v14), customize: spy1) #endif ColorPicker("", selection: .constant(PlatformColor.blue.cgColor)) #if os(iOS) - .introspect(.colorPicker, on: .iOS(.v14, .v15, .v16), customize: spy2) + .introspect(.colorPicker, on: .iOS(.v14, .v15, .v16, .v17), customize: spy2) #elseif os(macOS) - .introspect(.colorPicker, on: .macOS(.v11, .v12, .v13), customize: spy2) + .introspect(.colorPicker, on: .macOS(.v11, .v12, .v13, .v14), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/DatePickerTests.swift b/Tests/Tests/ViewTypes/DatePickerTests.swift index e2ba293..2ba7775 100644 --- a/Tests/Tests/ViewTypes/DatePickerTests.swift +++ b/Tests/Tests/ViewTypes/DatePickerTests.swift @@ -23,25 +23,25 @@ final class DatePickerTests: XCTestCase { VStack { DatePicker("", selection: .constant(date0)) #if os(iOS) - .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16), customize: spy0) + .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) #elseif os(macOS) - .introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + .introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif .cornerRadius(8) DatePicker("", selection: .constant(date1)) #if os(iOS) - .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16), customize: spy1) + .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) #elseif os(macOS) - .introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + .introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) #endif .cornerRadius(8) DatePicker("", selection: .constant(date2)) #if os(iOS) - .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16), customize: spy2) + .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) #elseif os(macOS) - .introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + .introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/DatePickerWithCompactFieldStyleTests.swift b/Tests/Tests/ViewTypes/DatePickerWithCompactFieldStyleTests.swift index 9a8e3bb..abdea4a 100644 --- a/Tests/Tests/ViewTypes/DatePickerWithCompactFieldStyleTests.swift +++ b/Tests/Tests/ViewTypes/DatePickerWithCompactFieldStyleTests.swift @@ -29,27 +29,27 @@ final class DatePickerWithCompactStyleTests: XCTestCase { DatePicker("", selection: .constant(date0)) .datePickerStyle(.compact) #if os(iOS) - .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16), customize: spy0) + .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17), customize: spy0) #elseif os(macOS) - .introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13), customize: spy0) + .introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13, .v14), customize: spy0) #endif .cornerRadius(8) DatePicker("", selection: .constant(date1)) .datePickerStyle(.compact) #if os(iOS) - .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16), customize: spy1) + .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17), customize: spy1) #elseif os(macOS) - .introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13), customize: spy1) + .introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13, .v14), customize: spy1) #endif .cornerRadius(8) DatePicker("", selection: .constant(date2)) .datePickerStyle(.compact) #if os(iOS) - .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16), customize: spy2) + .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17), customize: spy2) #elseif os(macOS) - .introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13), customize: spy2) + .introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13, .v14), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/DatePickerWithFieldStyleTests.swift b/Tests/Tests/ViewTypes/DatePickerWithFieldStyleTests.swift index a8824f6..fca60c7 100644 --- a/Tests/Tests/ViewTypes/DatePickerWithFieldStyleTests.swift +++ b/Tests/Tests/ViewTypes/DatePickerWithFieldStyleTests.swift @@ -22,21 +22,21 @@ final class DatePickerWithFieldStyleTests: XCTestCase { DatePicker("", selection: .constant(date0)) .datePickerStyle(.field) #if os(macOS) - .introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + .introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif .cornerRadius(8) DatePicker("", selection: .constant(date1)) .datePickerStyle(.field) #if os(macOS) - .introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + .introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) #endif .cornerRadius(8) DatePicker("", selection: .constant(date2)) .datePickerStyle(.field) #if os(macOS) - .introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + .introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/DatePickerWithGraphicalStyleTests.swift b/Tests/Tests/ViewTypes/DatePickerWithGraphicalStyleTests.swift index 04c9412..415692c 100644 --- a/Tests/Tests/ViewTypes/DatePickerWithGraphicalStyleTests.swift +++ b/Tests/Tests/ViewTypes/DatePickerWithGraphicalStyleTests.swift @@ -29,27 +29,27 @@ final class DatePickerWithGraphicalStyleTests: XCTestCase { DatePicker("", selection: .constant(date0)) .datePickerStyle(.graphical) #if os(iOS) - .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16), customize: spy0) + .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17), customize: spy0) #elseif os(macOS) - .introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + .introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif .cornerRadius(8) DatePicker("", selection: .constant(date1)) .datePickerStyle(.graphical) #if os(iOS) - .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16), customize: spy1) + .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17), customize: spy1) #elseif os(macOS) - .introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + .introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) #endif .cornerRadius(8) DatePicker("", selection: .constant(date2)) .datePickerStyle(.graphical) #if os(iOS) - .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16), customize: spy2) + .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17), customize: spy2) #elseif os(macOS) - .introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + .introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/DatePickerWithStepperFieldStyleTests.swift b/Tests/Tests/ViewTypes/DatePickerWithStepperFieldStyleTests.swift index d7ce689..1439d25 100644 --- a/Tests/Tests/ViewTypes/DatePickerWithStepperFieldStyleTests.swift +++ b/Tests/Tests/ViewTypes/DatePickerWithStepperFieldStyleTests.swift @@ -22,21 +22,21 @@ final class DatePickerWithWheelStyleTests: XCTestCase { DatePicker("", selection: .constant(date0)) .datePickerStyle(.stepperField) #if os(macOS) - .introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + .introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif .cornerRadius(8) DatePicker("", selection: .constant(date1)) .datePickerStyle(.stepperField) #if os(macOS) - .introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + .introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) #endif .cornerRadius(8) DatePicker("", selection: .constant(date2)) .datePickerStyle(.stepperField) #if os(macOS) - .introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + .introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/DatePickerWithWheelStyleTests.swift b/Tests/Tests/ViewTypes/DatePickerWithWheelStyleTests.swift index 7285072..b7d3461 100644 --- a/Tests/Tests/ViewTypes/DatePickerWithWheelStyleTests.swift +++ b/Tests/Tests/ViewTypes/DatePickerWithWheelStyleTests.swift @@ -22,21 +22,21 @@ final class DatePickerWithWheelStyleTests: XCTestCase { DatePicker("", selection: .constant(date0)) .datePickerStyle(.wheel) #if os(iOS) - .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16), customize: spy0) + .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) #endif .cornerRadius(8) DatePicker("", selection: .constant(date1)) .datePickerStyle(.wheel) #if os(iOS) - .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16), customize: spy1) + .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) #endif .cornerRadius(8) DatePicker("", selection: .constant(date2)) .datePickerStyle(.wheel) #if os(iOS) - .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16), customize: spy2) + .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/FormTests.swift b/Tests/Tests/ViewTypes/FormTests.swift index bca2770..84a644e 100644 --- a/Tests/Tests/ViewTypes/FormTests.swift +++ b/Tests/Tests/ViewTypes/FormTests.swift @@ -20,15 +20,15 @@ final class FormTests: XCTestCase { Text("Item 1") } #if os(iOS) || os(tvOS) - .introspect(.form, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { spy0($0) } - .introspect(.form, on: .iOS(.v16)) { spy0($0) } + .introspect(.form, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) } + .introspect(.form, on: .iOS(.v16, .v17)) { spy0($0) } #endif Form { Text("Item 1") #if os(iOS) || os(tvOS) - .introspect(.form, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor) { spy1($0) } - .introspect(.form, on: .iOS(.v16), scope: .ancestor) { spy1($0) } + .introspect(.form, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { spy1($0) } + .introspect(.form, on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) } #endif } } diff --git a/Tests/Tests/ViewTypes/FormWithGroupedStyleTests.swift b/Tests/Tests/ViewTypes/FormWithGroupedStyleTests.swift index 6451e1a..abbfa39 100644 --- a/Tests/Tests/ViewTypes/FormWithGroupedStyleTests.swift +++ b/Tests/Tests/ViewTypes/FormWithGroupedStyleTests.swift @@ -25,19 +25,19 @@ final class FormWithGroupedStyleTests: XCTestCase { } .formStyle(.grouped) #if os(iOS) || os(tvOS) - .introspect(.form(style: .grouped), on: .iOS(.v16)) { spy0($0) } - .introspect(.form(style: .grouped), on: .tvOS(.v16)) { spy0($0) } + .introspect(.form(style: .grouped), on: .iOS(.v16, .v17)) { spy0($0) } + .introspect(.form(style: .grouped), on: .tvOS(.v16, .v17)) { spy0($0) } #elseif os(macOS) - .introspect(.form(style: .grouped), on: .macOS(.v13)) { spy0($0) } + .introspect(.form(style: .grouped), on: .macOS(.v13, .v14)) { spy0($0) } #endif Form { Text("Item 1") #if os(iOS) || os(tvOS) - .introspect(.form(style: .grouped), on: .iOS(.v16), scope: .ancestor) { spy1($0) } - .introspect(.form(style: .grouped), on: .tvOS(.v16), scope: .ancestor) { spy1($0) } + .introspect(.form(style: .grouped), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) } + .introspect(.form(style: .grouped), on: .tvOS(.v16, .v17), scope: .ancestor) { spy1($0) } #elseif os(macOS) - .introspect(.form(style: .grouped), on: .macOS(.v13), scope: .ancestor) { spy1($0) } + .introspect(.form(style: .grouped), on: .macOS(.v13, .v14), scope: .ancestor) { spy1($0) } #endif } .formStyle(.grouped) diff --git a/Tests/Tests/ViewTypes/ListCellTests.swift b/Tests/Tests/ViewTypes/ListCellTests.swift index b512f95..cb9b1f8 100644 --- a/Tests/Tests/ViewTypes/ListCellTests.swift +++ b/Tests/Tests/ViewTypes/ListCellTests.swift @@ -16,10 +16,10 @@ final class ListCellTests: XCTestCase { List { Text("Item 1") #if os(iOS) || os(tvOS) - .introspect(.listCell, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { spy($0) } - .introspect(.listCell, on: .iOS(.v16)) { spy($0) } + .introspect(.listCell, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy($0) } + .introspect(.listCell, on: .iOS(.v16, .v17)) { spy($0) } #elseif os(macOS) - .introspect(.listCell, on: .macOS(.v10_15, .v11, .v12, .v13)) { spy($0) } + .introspect(.listCell, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy($0) } #endif } } @@ -32,10 +32,10 @@ final class ListCellTests: XCTestCase { List { Text("Item 1") #if os(iOS) || os(tvOS) - .introspect(.listCell, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { spy($0) } - .introspect(.listCell, on: .iOS(.v16)) { spy($0) } + .introspect(.listCell, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy($0) } + .introspect(.listCell, on: .iOS(.v16, .v17)) { spy($0) } #elseif os(macOS) - .introspect(.listCell, on: .macOS(.v10_15, .v11, .v12, .v13)) { spy($0) } + .introspect(.listCell, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy($0) } #endif .clipped() .clipShape(RoundedRectangle(cornerRadius: 20.0)) diff --git a/Tests/Tests/ViewTypes/ListTests.swift b/Tests/Tests/ViewTypes/ListTests.swift index f196abc..642f134 100644 --- a/Tests/Tests/ViewTypes/ListTests.swift +++ b/Tests/Tests/ViewTypes/ListTests.swift @@ -19,19 +19,19 @@ final class ListTests: XCTestCase { Text("Item 1") } #if os(iOS) || os(tvOS) - .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { spy0($0) } - .introspect(.list, on: .iOS(.v16)) { spy0($0) } + .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) } + .introspect(.list, on: .iOS(.v16, .v17)) { spy0($0) } #elseif os(macOS) - .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13)) { spy0($0) } + .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy0($0) } #endif List { Text("Item 1") #if os(iOS) || os(tvOS) - .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor) { spy1($0) } - .introspect(.list, on: .iOS(.v16), scope: .ancestor) { spy1($0) } + .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { spy1($0) } + .introspect(.list, on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) } #elseif os(macOS) - .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13), scope: .ancestor) { spy1($0) } + .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) } #endif } } @@ -53,13 +53,13 @@ final class ListTests: XCTestCase { Text("Item 1") } #if os(iOS) || os(tvOS) - .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { spy1($0) } - .introspect(.list, on: .iOS(.v16)) { spy1($0) } + .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy1($0) } + .introspect(.list, on: .iOS(.v16, .v17)) { spy1($0) } #endif } #if os(iOS) || os(tvOS) - .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { spy0($0) } - .introspect(.list, on: .iOS(.v16)) { spy0($0) } + .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) } + .introspect(.list, on: .iOS(.v16, .v17)) { spy0($0) } #endif } extraAssertions: { XCTAssert($0[safe: 0] !== $0[safe: 1]) @@ -77,10 +77,10 @@ final class ListTests: XCTestCase { Text("Item 1") } #if os(iOS) || os(tvOS) - .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { spy0($0) } - .introspect(.list, on: .iOS(.v16)) { spy0($0) } + .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) } + .introspect(.list, on: .iOS(.v16, .v17)) { spy0($0) } #elseif os(macOS) - .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13)) { spy0($0) } + .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy0($0) } #endif .clipped() .clipShape(RoundedRectangle(cornerRadius: 20.0)) @@ -89,10 +89,10 @@ final class ListTests: XCTestCase { List { Text("Item 1") #if os(iOS) || os(tvOS) - .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor) { spy1($0) } - .introspect(.list, on: .iOS(.v16), scope: .ancestor) { spy1($0) } + .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { spy1($0) } + .introspect(.list, on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) } #elseif os(macOS) - .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13), scope: .ancestor) { spy1($0) } + .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) } #endif } } diff --git a/Tests/Tests/ViewTypes/ListWithBorderedStyleTests.swift b/Tests/Tests/ViewTypes/ListWithBorderedStyleTests.swift index 0aede8f..4a8c29a 100644 --- a/Tests/Tests/ViewTypes/ListWithBorderedStyleTests.swift +++ b/Tests/Tests/ViewTypes/ListWithBorderedStyleTests.swift @@ -24,13 +24,13 @@ final class ListWithBorderedStyleTests: XCTestCase { } .listStyle(.bordered) #if os(macOS) - .introspect(.list(style: .bordered), on: .macOS(.v12, .v13)) { spy0($0) } + .introspect(.list(style: .bordered), on: .macOS(.v12, .v13, .v14)) { spy0($0) } #endif List { Text("Item 1") #if os(macOS) - .introspect(.list(style: .bordered), on: .macOS(.v12, .v13), scope: .ancestor) { spy1($0) } + .introspect(.list(style: .bordered), on: .macOS(.v12, .v13, .v14), scope: .ancestor) { spy1($0) } #endif } .listStyle(.bordered) diff --git a/Tests/Tests/ViewTypes/ListWithGroupedStyleTests.swift b/Tests/Tests/ViewTypes/ListWithGroupedStyleTests.swift index ee388fe..ef09afa 100644 --- a/Tests/Tests/ViewTypes/ListWithGroupedStyleTests.swift +++ b/Tests/Tests/ViewTypes/ListWithGroupedStyleTests.swift @@ -19,15 +19,15 @@ final class ListWithGroupedStyleTests: XCTestCase { } .listStyle(.grouped) #if os(iOS) || os(tvOS) - .introspect(.list(style: .grouped), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { spy0($0) } - .introspect(.list(style: .grouped), on: .iOS(.v16)) { spy0($0) } + .introspect(.list(style: .grouped), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) } + .introspect(.list(style: .grouped), on: .iOS(.v16, .v17)) { spy0($0) } #endif List { Text("Item 1") #if os(iOS) || os(tvOS) - .introspect(.list(style: .grouped), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor) { spy1($0) } - .introspect(.list(style: .grouped), on: .iOS(.v16), scope: .ancestor) { spy1($0) } + .introspect(.list(style: .grouped), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { spy1($0) } + .introspect(.list(style: .grouped), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) } #endif } .listStyle(.grouped) diff --git a/Tests/Tests/ViewTypes/ListWithInsetGroupedStyleTests.swift b/Tests/Tests/ViewTypes/ListWithInsetGroupedStyleTests.swift index 90f0389..074a857 100644 --- a/Tests/Tests/ViewTypes/ListWithInsetGroupedStyleTests.swift +++ b/Tests/Tests/ViewTypes/ListWithInsetGroupedStyleTests.swift @@ -25,14 +25,14 @@ final class ListWithInsetGroupedStyleTests: XCTestCase { .listStyle(.insetGrouped) #if os(iOS) .introspect(.list(style: .insetGrouped), on: .iOS(.v14, .v15)) { spy0($0) } - .introspect(.list(style: .insetGrouped), on: .iOS(.v16)) { spy0($0) } + .introspect(.list(style: .insetGrouped), on: .iOS(.v16, .v17)) { spy0($0) } #endif List { Text("Item 1") #if os(iOS) .introspect(.list(style: .insetGrouped), on: .iOS(.v14, .v15), scope: .ancestor) { spy1($0) } - .introspect(.list(style: .insetGrouped), on: .iOS(.v16), scope: .ancestor) { spy1($0) } + .introspect(.list(style: .insetGrouped), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) } #endif } .listStyle(.insetGrouped) diff --git a/Tests/Tests/ViewTypes/ListWithInsetStyleTests.swift b/Tests/Tests/ViewTypes/ListWithInsetStyleTests.swift index e7132b8..f8767cc 100644 --- a/Tests/Tests/ViewTypes/ListWithInsetStyleTests.swift +++ b/Tests/Tests/ViewTypes/ListWithInsetStyleTests.swift @@ -27,18 +27,18 @@ final class ListWithInsetStyleTests: XCTestCase { .listStyle(.inset) #if os(iOS) .introspect(.list(style: .inset), on: .iOS(.v14, .v15)) { spy0($0) } - .introspect(.list(style: .inset), on: .iOS(.v16)) { spy0($0) } + .introspect(.list(style: .inset), on: .iOS(.v16, .v17)) { spy0($0) } #elseif os(macOS) - .introspect(.list(style: .inset), on: .macOS(.v11, .v12, .v13)) { spy0($0) } + .introspect(.list(style: .inset), on: .macOS(.v11, .v12, .v13, .v14)) { spy0($0) } #endif List { Text("Item 1") #if os(iOS) .introspect(.list(style: .inset), on: .iOS(.v14, .v15), scope: .ancestor) { spy1($0) } - .introspect(.list(style: .inset), on: .iOS(.v16), scope: .ancestor) { spy1($0) } + .introspect(.list(style: .inset), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) } #elseif os(macOS) - .introspect(.list(style: .inset), on: .macOS(.v11, .v12, .v13), scope: .ancestor) { spy1($0) } + .introspect(.list(style: .inset), on: .macOS(.v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) } #endif } .listStyle(.inset) diff --git a/Tests/Tests/ViewTypes/ListWithPlainStyleTests.swift b/Tests/Tests/ViewTypes/ListWithPlainStyleTests.swift index 87fb267..0c783cf 100644 --- a/Tests/Tests/ViewTypes/ListWithPlainStyleTests.swift +++ b/Tests/Tests/ViewTypes/ListWithPlainStyleTests.swift @@ -20,19 +20,19 @@ final class ListWithPlainStyleTests: XCTestCase { } .listStyle(.plain) #if os(iOS) || os(tvOS) - .introspect(.list(style: .plain), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { spy0($0) } - .introspect(.list(style: .plain), on: .iOS(.v16)) { spy0($0) } + .introspect(.list(style: .plain), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) } + .introspect(.list(style: .plain), on: .iOS(.v16, .v17)) { spy0($0) } #elseif os(macOS) - .introspect(.list(style: .plain), on: .macOS(.v10_15, .v11, .v12, .v13)) { spy0($0) } + .introspect(.list(style: .plain), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy0($0) } #endif List { Text("Item 1") #if os(iOS) || os(tvOS) - .introspect(.list(style: .plain), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor) { spy1($0) } - .introspect(.list(style: .plain), on: .iOS(.v16), scope: .ancestor) { spy1($0) } + .introspect(.list(style: .plain), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { spy1($0) } + .introspect(.list(style: .plain), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) } #elseif os(macOS) - .introspect(.list(style: .plain), on: .macOS(.v10_15, .v11, .v12, .v13), scope: .ancestor) { spy1($0) } + .introspect(.list(style: .plain), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) } #endif } .listStyle(.plain) diff --git a/Tests/Tests/ViewTypes/ListWithSidebarStyleTests.swift b/Tests/Tests/ViewTypes/ListWithSidebarStyleTests.swift index b112e95..3199b9d 100644 --- a/Tests/Tests/ViewTypes/ListWithSidebarStyleTests.swift +++ b/Tests/Tests/ViewTypes/ListWithSidebarStyleTests.swift @@ -27,18 +27,18 @@ final class ListWithSidebarStyleTests: XCTestCase { .listStyle(.sidebar) #if os(iOS) .introspect(.list(style: .sidebar), on: .iOS(.v14, .v15)) { spy0($0) } - .introspect(.list(style: .sidebar), on: .iOS(.v16)) { spy0($0) } + .introspect(.list(style: .sidebar), on: .iOS(.v16, .v17)) { spy0($0) } #elseif os(macOS) - .introspect(.list(style: .sidebar), on: .macOS(.v10_15, .v11, .v12, .v13)) { spy0($0) } + .introspect(.list(style: .sidebar), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy0($0) } #endif List { Text("Item 1") #if os(iOS) .introspect(.list(style: .sidebar), on: .iOS(.v14, .v15), scope: .ancestor) { spy1($0) } - .introspect(.list(style: .sidebar), on: .iOS(.v16), scope: .ancestor) { spy1($0) } + .introspect(.list(style: .sidebar), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) } #elseif os(macOS) - .introspect(.list(style: .sidebar), on: .macOS(.v10_15, .v11, .v12, .v13), scope: .ancestor) { spy1($0) } + .introspect(.list(style: .sidebar), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) } #endif } .listStyle(.sidebar) diff --git a/Tests/Tests/ViewTypes/NavigationSplitViewTests.swift b/Tests/Tests/ViewTypes/NavigationSplitViewTests.swift index 77ff258..ddeff1d 100644 --- a/Tests/Tests/ViewTypes/NavigationSplitViewTests.swift +++ b/Tests/Tests/ViewTypes/NavigationSplitViewTests.swift @@ -33,11 +33,11 @@ final class NavigationSplitViewTests: XCTestCase { } } #if os(iOS) - .introspect(.navigationSplitView, on: .iOS(.v16), customize: spy) + .introspect(.navigationSplitView, on: .iOS(.v16, .v17), customize: spy) #elseif os(tvOS) - .introspect(.navigationSplitView, on: .tvOS(.v16), customize: spy) + .introspect(.navigationSplitView, on: .tvOS(.v16, .v17), customize: spy) #elseif os(macOS) - .introspect(.navigationSplitView, on: .macOS(.v13), customize: spy) + .introspect(.navigationSplitView, on: .macOS(.v13, .v14), customize: spy) #endif } } @@ -56,11 +56,11 @@ final class NavigationSplitViewTests: XCTestCase { Color.red Text("Sidebar") #if os(iOS) - .introspect(.navigationSplitView, on: .iOS(.v16), scope: .ancestor, customize: spy) + .introspect(.navigationSplitView, on: .iOS(.v16, .v17), scope: .ancestor, customize: spy) #elseif os(tvOS) - .introspect(.navigationSplitView, on: .tvOS(.v16), scope: .ancestor, customize: spy) + .introspect(.navigationSplitView, on: .tvOS(.v16, .v17), scope: .ancestor, customize: spy) #elseif os(macOS) - .introspect(.navigationSplitView, on: .macOS(.v13), scope: .ancestor, customize: spy) + .introspect(.navigationSplitView, on: .macOS(.v13, .v14), scope: .ancestor, customize: spy) #endif } } detail: { diff --git a/Tests/Tests/ViewTypes/NavigationStackTests.swift b/Tests/Tests/ViewTypes/NavigationStackTests.swift index a37bb83..f32d4a0 100644 --- a/Tests/Tests/ViewTypes/NavigationStackTests.swift +++ b/Tests/Tests/ViewTypes/NavigationStackTests.swift @@ -24,7 +24,7 @@ final class NavigationStackTests: XCTestCase { } } #if os(iOS) || os(tvOS) - .introspect(.navigationStack, on: .iOS(.v16), .tvOS(.v16), customize: spy) + .introspect(.navigationStack, on: .iOS(.v16, .v17), .tvOS(.v16, .v17), customize: spy) #endif } } @@ -42,7 +42,7 @@ final class NavigationStackTests: XCTestCase { Color.red Text("Something") #if os(iOS) || os(tvOS) - .introspect(.navigationStack, on: .iOS(.v16), .tvOS(.v16), scope: .ancestor, customize: spy) + .introspect(.navigationStack, on: .iOS(.v16, .v17), .tvOS(.v16, .v17), scope: .ancestor, customize: spy) #endif } } diff --git a/Tests/Tests/ViewTypes/NavigationViewWithColumnsStyleTests.swift b/Tests/Tests/ViewTypes/NavigationViewWithColumnsStyleTests.swift index 5902b78..b40ac5d 100644 --- a/Tests/Tests/ViewTypes/NavigationViewWithColumnsStyleTests.swift +++ b/Tests/Tests/ViewTypes/NavigationViewWithColumnsStyleTests.swift @@ -23,11 +23,11 @@ final class NavigationViewWithColumnsStyleTests: XCTestCase { } .navigationViewStyle(DoubleColumnNavigationViewStyle()) #if os(iOS) - .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16), customize: spy) + .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy) #elseif os(tvOS) - .introspect(.navigationView(style: .columns), on: .tvOS(.v13, .v14, .v15, .v16), customize: spy) + .introspect(.navigationView(style: .columns), on: .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy) #elseif os(macOS) - .introspect(.navigationView(style: .columns), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy) + .introspect(.navigationView(style: .columns), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy) #endif } } @@ -41,18 +41,18 @@ final class NavigationViewWithColumnsStyleTests: XCTestCase { Color.red Text("Something") #if os(iOS) - .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16), scope: .ancestor, customize: spy) + .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor, customize: spy) #elseif os(tvOS) - .introspect(.navigationView(style: .columns), on: .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor, customize: spy) + .introspect(.navigationView(style: .columns), on: .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor, customize: spy) #elseif os(macOS) - .introspect(.navigationView(style: .columns), on: .macOS(.v10_15, .v11, .v12, .v13), scope: .ancestor, customize: spy) + .introspect(.navigationView(style: .columns), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor, customize: spy) #endif } } .navigationViewStyle(DoubleColumnNavigationViewStyle()) #if os(iOS) // NB: this is necessary for ancestor introspection to work, because initially on iPad the "Customized" text isn't shown as it's hidden in the sidebar. This is why ancestor introspection is discouraged for most situations and it's opt-in. - .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16)) { + .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { $0.preferredDisplayMode = .oneOverSecondary } #endif diff --git a/Tests/Tests/ViewTypes/NavigationViewWithStackStyleTests.swift b/Tests/Tests/ViewTypes/NavigationViewWithStackStyleTests.swift index 6e89763..51635d8 100644 --- a/Tests/Tests/ViewTypes/NavigationViewWithStackStyleTests.swift +++ b/Tests/Tests/ViewTypes/NavigationViewWithStackStyleTests.swift @@ -20,7 +20,7 @@ final class NavigationViewWithStackStyleTests: XCTestCase { } .navigationViewStyle(.stack) #if os(iOS) || os(tvOS) - .introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy) + .introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy) #endif } } @@ -34,7 +34,7 @@ final class NavigationViewWithStackStyleTests: XCTestCase { Color.red Text("Something") #if os(iOS) || os(tvOS) - .introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor, customize: spy) + .introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor, customize: spy) #endif } } diff --git a/Tests/Tests/ViewTypes/PickerWithMenuStyleTests.swift b/Tests/Tests/ViewTypes/PickerWithMenuStyleTests.swift index ae17cba..d8dc312 100644 --- a/Tests/Tests/ViewTypes/PickerWithMenuStyleTests.swift +++ b/Tests/Tests/ViewTypes/PickerWithMenuStyleTests.swift @@ -20,7 +20,7 @@ final class PickerWithMenuStyleTests: XCTestCase { } .pickerStyle(.menu) #if os(macOS) - .introspect(.picker(style: .menu), on: .macOS(.v11, .v12, .v13), customize: spy0) + .introspect(.picker(style: .menu), on: .macOS(.v11, .v12, .v13, .v14), customize: spy0) #endif .cornerRadius(8) @@ -30,7 +30,7 @@ final class PickerWithMenuStyleTests: XCTestCase { } .pickerStyle(.menu) #if os(macOS) - .introspect(.picker(style: .menu), on: .macOS(.v11, .v12, .v13), customize: spy1) + .introspect(.picker(style: .menu), on: .macOS(.v11, .v12, .v13, .v14), customize: spy1) #endif .cornerRadius(8) @@ -41,7 +41,7 @@ final class PickerWithMenuStyleTests: XCTestCase { } .pickerStyle(.menu) #if os(macOS) - .introspect(.picker(style: .menu), on: .macOS(.v11, .v12, .v13), customize: spy2) + .introspect(.picker(style: .menu), on: .macOS(.v11, .v12, .v13, .v14), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/PickerWithSegmentedStyleTests.swift b/Tests/Tests/ViewTypes/PickerWithSegmentedStyleTests.swift index ae9a97c..903ab89 100644 --- a/Tests/Tests/ViewTypes/PickerWithSegmentedStyleTests.swift +++ b/Tests/Tests/ViewTypes/PickerWithSegmentedStyleTests.swift @@ -21,9 +21,9 @@ final class PickerWithSegmentedStyleTests: XCTestCase { } .pickerStyle(.segmented) #if os(iOS) || os(tvOS) - .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy0) + .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) #elseif os(macOS) - .introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + .introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif .cornerRadius(8) @@ -33,9 +33,9 @@ final class PickerWithSegmentedStyleTests: XCTestCase { } .pickerStyle(.segmented) #if os(iOS) || os(tvOS) - .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy1) + .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) #elseif os(macOS) - .introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + .introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) #endif .cornerRadius(8) @@ -46,9 +46,9 @@ final class PickerWithSegmentedStyleTests: XCTestCase { } .pickerStyle(.segmented) #if os(iOS) || os(tvOS) - .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy2) + .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) #elseif os(macOS) - .introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + .introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/PickerWithWheelStyleTests.swift b/Tests/Tests/ViewTypes/PickerWithWheelStyleTests.swift index 5915c6f..4efdd61 100644 --- a/Tests/Tests/ViewTypes/PickerWithWheelStyleTests.swift +++ b/Tests/Tests/ViewTypes/PickerWithWheelStyleTests.swift @@ -20,7 +20,7 @@ final class PickerWithWheelStyleTests: XCTestCase { } .pickerStyle(.wheel) #if os(iOS) - .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16), customize: spy0) + .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) #endif .cornerRadius(8) @@ -30,7 +30,7 @@ final class PickerWithWheelStyleTests: XCTestCase { } .pickerStyle(.wheel) #if os(iOS) - .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16), customize: spy1) + .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) #endif .cornerRadius(8) @@ -41,7 +41,7 @@ final class PickerWithWheelStyleTests: XCTestCase { } .pickerStyle(.wheel) #if os(iOS) - .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16), customize: spy2) + .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/ProgressViewWithCircularStyleTests.swift b/Tests/Tests/ViewTypes/ProgressViewWithCircularStyleTests.swift index e990ea7..b5d58ba 100644 --- a/Tests/Tests/ViewTypes/ProgressViewWithCircularStyleTests.swift +++ b/Tests/Tests/ViewTypes/ProgressViewWithCircularStyleTests.swift @@ -23,25 +23,25 @@ final class ProgressViewWithCircularStyleTests: XCTestCase { ProgressView(value: 0.25) .progressViewStyle(.circular) #if os(iOS) || os(tvOS) - .introspect(.progressView(style: .circular), on: .iOS(.v14, .v15, .v16), .tvOS(.v14, .v15, .v16), customize: spy0) + .introspect(.progressView(style: .circular), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy0) #elseif os(macOS) - .introspect(.progressView(style: .circular), on: .macOS(.v11, .v12, .v13), customize: spy0) + .introspect(.progressView(style: .circular), on: .macOS(.v11, .v12, .v13, .v14), customize: spy0) #endif ProgressView(value: 0.5) .progressViewStyle(.circular) #if os(iOS) || os(tvOS) - .introspect(.progressView(style: .circular), on: .iOS(.v14, .v15, .v16), .tvOS(.v14, .v15, .v16), customize: spy1) + .introspect(.progressView(style: .circular), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy1) #elseif os(macOS) - .introspect(.progressView(style: .circular), on: .macOS(.v11, .v12, .v13), customize: spy1) + .introspect(.progressView(style: .circular), on: .macOS(.v11, .v12, .v13, .v14), customize: spy1) #endif ProgressView(value: 0.75) .progressViewStyle(.circular) #if os(iOS) || os(tvOS) - .introspect(.progressView(style: .circular), on: .iOS(.v14, .v15, .v16), .tvOS(.v14, .v15, .v16), customize: spy2) + .introspect(.progressView(style: .circular), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy2) #elseif os(macOS) - .introspect(.progressView(style: .circular), on: .macOS(.v11, .v12, .v13), customize: spy2) + .introspect(.progressView(style: .circular), on: .macOS(.v11, .v12, .v13, .v14), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/ProgressViewWithLinearStyleTests.swift b/Tests/Tests/ViewTypes/ProgressViewWithLinearStyleTests.swift index 6041a84..b1751ef 100644 --- a/Tests/Tests/ViewTypes/ProgressViewWithLinearStyleTests.swift +++ b/Tests/Tests/ViewTypes/ProgressViewWithLinearStyleTests.swift @@ -23,25 +23,25 @@ final class ProgressViewWithLinearStyleTests: XCTestCase { ProgressView(value: 0.25) .progressViewStyle(.linear) #if os(iOS) || os(tvOS) - .introspect(.progressView(style: .linear), on: .iOS(.v14, .v15, .v16), .tvOS(.v14, .v15, .v16), customize: spy0) + .introspect(.progressView(style: .linear), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy0) #elseif os(macOS) - .introspect(.progressView(style: .linear), on: .macOS(.v11, .v12, .v13), customize: spy0) + .introspect(.progressView(style: .linear), on: .macOS(.v11, .v12, .v13, .v14), customize: spy0) #endif ProgressView(value: 0.5) .progressViewStyle(.linear) #if os(iOS) || os(tvOS) - .introspect(.progressView(style: .linear), on: .iOS(.v14, .v15, .v16), .tvOS(.v14, .v15, .v16), customize: spy1) + .introspect(.progressView(style: .linear), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy1) #elseif os(macOS) - .introspect(.progressView(style: .linear), on: .macOS(.v11, .v12, .v13), customize: spy1) + .introspect(.progressView(style: .linear), on: .macOS(.v11, .v12, .v13, .v14), customize: spy1) #endif ProgressView(value: 0.75) .progressViewStyle(.linear) #if os(iOS) || os(tvOS) - .introspect(.progressView(style: .linear), on: .iOS(.v14, .v15, .v16), .tvOS(.v14, .v15, .v16), customize: spy2) + .introspect(.progressView(style: .linear), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy2) #elseif os(macOS) - .introspect(.progressView(style: .linear), on: .macOS(.v11, .v12, .v13), customize: spy2) + .introspect(.progressView(style: .linear), on: .macOS(.v11, .v12, .v13, .v14), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/ScrollViewTests.swift b/Tests/Tests/ViewTypes/ScrollViewTests.swift index 682bf71..5170c42 100644 --- a/Tests/Tests/ViewTypes/ScrollViewTests.swift +++ b/Tests/Tests/ViewTypes/ScrollViewTests.swift @@ -19,17 +19,17 @@ final class ScrollViewTests: XCTestCase { Text("Item 1") } #if os(iOS) || os(tvOS) - .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy0) + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) #elseif os(macOS) - .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif ScrollView(showsIndicators: true) { Text("Item 1") #if os(iOS) || os(tvOS) - .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor, customize: spy1) + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor, customize: spy1) #elseif os(macOS) - .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13), scope: .ancestor, customize: spy1) + .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor, customize: spy1) #endif } } @@ -61,15 +61,15 @@ final class ScrollViewTests: XCTestCase { Text("Item 1") } #if os(iOS) || os(tvOS) - .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy1) + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) #elseif os(macOS) - .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) #endif } #if os(iOS) || os(tvOS) - .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy0) + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) #elseif os(macOS) - .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif } extraAssertions: { #if canImport(UIKit) @@ -97,9 +97,9 @@ final class ScrollViewTests: XCTestCase { Text("Item 1") } #if os(iOS) || os(tvOS) - .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy0) + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) #elseif os(macOS) - .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif .clipped() .clipShape(RoundedRectangle(cornerRadius: 20.0)) @@ -108,9 +108,9 @@ final class ScrollViewTests: XCTestCase { ScrollView(showsIndicators: true) { Text("Item 1") #if os(iOS) || os(tvOS) - .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor, customize: spy1) + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor, customize: spy1) #elseif os(macOS) - .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13), scope: .ancestor, customize: spy1) + .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor, customize: spy1) #endif } } diff --git a/Tests/Tests/ViewTypes/SearchFieldTests.swift b/Tests/Tests/ViewTypes/SearchFieldTests.swift index b188356..334e1cf 100644 --- a/Tests/Tests/ViewTypes/SearchFieldTests.swift +++ b/Tests/Tests/ViewTypes/SearchFieldTests.swift @@ -23,7 +23,7 @@ final class SearchFieldTests: XCTestCase { } .navigationViewStyle(.stack) #if os(iOS) || os(tvOS) - .introspect(.searchField, on: .iOS(.v15, .v16), .tvOS(.v15, .v16), customize: spy) + .introspect(.searchField, on: .iOS(.v15, .v16, .v17), .tvOS(.v15, .v16, .v17), customize: spy) #endif } } @@ -40,7 +40,7 @@ final class SearchFieldTests: XCTestCase { Text("Customized") .searchable(text: .constant("")) #if os(iOS) || os(tvOS) - .introspect(.searchField, on: .iOS(.v15, .v16), .tvOS(.v15, .v16), scope: .ancestor, customize: spy) + .introspect(.searchField, on: .iOS(.v15, .v16, .v17), .tvOS(.v15, .v16, .v17), scope: .ancestor, customize: spy) #endif } .navigationViewStyle(.stack) @@ -61,11 +61,11 @@ final class SearchFieldTests: XCTestCase { } .navigationViewStyle(DoubleColumnNavigationViewStyle()) #if os(iOS) || os(tvOS) - .introspect(.searchField, on: .iOS(.v15, .v16), .tvOS(.v15, .v16), customize: spy) + .introspect(.searchField, on: .iOS(.v15, .v16, .v17), .tvOS(.v15, .v16, .v17), customize: spy) #endif #if os(iOS) // NB: this is necessary for introspection to work, because on iPad the search field is in the sidebar, which is initially hidden. - .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16)) { + .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { $0.preferredDisplayMode = .oneOverSecondary } #endif @@ -84,13 +84,13 @@ final class SearchFieldTests: XCTestCase { Text("Customized") .searchable(text: .constant("")) #if os(iOS) || os(tvOS) - .introspect(.searchField, on: .iOS(.v15, .v16), .tvOS(.v15, .v16), scope: .ancestor, customize: spy) + .introspect(.searchField, on: .iOS(.v15, .v16, .v17), .tvOS(.v15, .v16, .v17), scope: .ancestor, customize: spy) #endif } .navigationViewStyle(DoubleColumnNavigationViewStyle()) #if os(iOS) // NB: this is necessary for introspection to work, because on iPad the search field is in the sidebar, which is initially hidden. - .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16)) { + .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { $0.preferredDisplayMode = .oneOverSecondary } #endif diff --git a/Tests/Tests/ViewTypes/SliderTests.swift b/Tests/Tests/ViewTypes/SliderTests.swift index 3221464..7c85709 100644 --- a/Tests/Tests/ViewTypes/SliderTests.swift +++ b/Tests/Tests/ViewTypes/SliderTests.swift @@ -19,25 +19,25 @@ final class SliderTests: XCTestCase { VStack { Slider(value: .constant(0.2), in: 0...1) #if os(iOS) - .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16), customize: spy0) + .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) #elseif os(macOS) - .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif .cornerRadius(8) Slider(value: .constant(0.5), in: 0...1) #if os(iOS) - .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16), customize: spy1) + .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) #elseif os(macOS) - .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) #endif .cornerRadius(8) Slider(value: .constant(0.8), in: 0...1) #if os(iOS) - .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16), customize: spy2) + .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) #elseif os(macOS) - .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/StepperTests.swift b/Tests/Tests/ViewTypes/StepperTests.swift index 211f834..af5436f 100644 --- a/Tests/Tests/ViewTypes/StepperTests.swift +++ b/Tests/Tests/ViewTypes/StepperTests.swift @@ -19,25 +19,25 @@ final class StepperTests: XCTestCase { VStack { Stepper("", value: .constant(0), in: 0...10) #if os(iOS) - .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16), customize: spy0) + .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) #elseif os(macOS) - .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif .cornerRadius(8) Stepper("", value: .constant(0), in: 0...10) #if os(iOS) - .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16), customize: spy1) + .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) #elseif os(macOS) - .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) #endif .cornerRadius(8) Stepper("", value: .constant(0), in: 0...10) #if os(iOS) - .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16), customize: spy2) + .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) #elseif os(macOS) - .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/TabViewTests.swift b/Tests/Tests/ViewTypes/TabViewTests.swift index c17e28a..0185197 100644 --- a/Tests/Tests/ViewTypes/TabViewTests.swift +++ b/Tests/Tests/ViewTypes/TabViewTests.swift @@ -21,9 +21,9 @@ final class TabViewTests: XCTestCase { } } #if os(iOS) || os(tvOS) - .introspect(.tabView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy) + .introspect(.tabView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy) #elseif os(macOS) - .introspect(.tabView, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy) + .introspect(.tabView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy) #endif } } @@ -37,9 +37,9 @@ final class TabViewTests: XCTestCase { Color.red Text("Something") #if os(iOS) || os(tvOS) - .introspect(.tabView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor, customize: spy) + .introspect(.tabView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor, customize: spy) #elseif os(macOS) - .introspect(.tabView, on: .macOS(.v10_15, .v11, .v12, .v13), scope: .ancestor, customize: spy) + .introspect(.tabView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor, customize: spy) #endif } } diff --git a/Tests/Tests/ViewTypes/TabViewWithPageStyleTests.swift b/Tests/Tests/ViewTypes/TabViewWithPageStyleTests.swift index 51ac089..eac652c 100644 --- a/Tests/Tests/ViewTypes/TabViewWithPageStyleTests.swift +++ b/Tests/Tests/ViewTypes/TabViewWithPageStyleTests.swift @@ -25,7 +25,7 @@ final class TabViewWithPageStyleTests: XCTestCase { } .tabViewStyle(.page) #if os(iOS) || os(tvOS) - .introspect(.tabView(style: .page), on: .iOS(.v14, .v15, .v16), .tvOS(.v14, .v15, .v16), customize: spy) + .introspect(.tabView(style: .page), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy) #endif } } @@ -41,7 +41,7 @@ final class TabViewWithPageStyleTests: XCTestCase { TabView { ZStack { Color.red; Text("1") } #if os(iOS) || os(tvOS) - .introspect(.tabView(style: .page), on: .iOS(.v14, .v15, .v16), .tvOS(.v14, .v15, .v16), scope: .ancestor, customize: spy) + .introspect(.tabView(style: .page), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), scope: .ancestor, customize: spy) #endif ZStack { Color.green; Text("2") } } diff --git a/Tests/Tests/ViewTypes/TableTests.swift b/Tests/Tests/ViewTypes/TableTests.swift index 7b55c03..6a1f323 100644 --- a/Tests/Tests/ViewTypes/TableTests.swift +++ b/Tests/Tests/ViewTypes/TableTests.swift @@ -24,23 +24,23 @@ final class TableTests: XCTestCase { VStack { TipTable() #if os(iOS) - .introspect(.table, on: .iOS(.v16), customize: spy0) + .introspect(.table, on: .iOS(.v16, .v17), customize: spy0) #elseif os(macOS) - .introspect(.table, on: .macOS(.v12, .v13), customize: spy0) + .introspect(.table, on: .macOS(.v12, .v13, .v14), customize: spy0) #endif TipTable() #if os(iOS) - .introspect(.table, on: .iOS(.v16), customize: spy1) + .introspect(.table, on: .iOS(.v16, .v17), customize: spy1) #elseif os(macOS) - .introspect(.table, on: .macOS(.v12, .v13), customize: spy1) + .introspect(.table, on: .macOS(.v12, .v13, .v14), customize: spy1) #endif TipTable() #if os(iOS) - .introspect(.table, on: .iOS(.v16), customize: spy2) + .introspect(.table, on: .iOS(.v16, .v17), customize: spy2) #elseif os(macOS) - .introspect(.table, on: .macOS(.v12, .v13), customize: spy2) + .introspect(.table, on: .macOS(.v12, .v13, .v14), customize: spy2) #endif } } @@ -60,25 +60,25 @@ final class TableTests: XCTestCase { TipTable() .tableStyle(.inset) #if os(iOS) - .introspect(.table, on: .iOS(.v16), customize: spy0) + .introspect(.table, on: .iOS(.v16, .v17), customize: spy0) #elseif os(macOS) - .introspect(.table, on: .macOS(.v12, .v13), customize: spy0) + .introspect(.table, on: .macOS(.v12, .v13, .v14), customize: spy0) #endif TipTable() .tableStyle(.inset) #if os(iOS) - .introspect(.table, on: .iOS(.v16), customize: spy1) + .introspect(.table, on: .iOS(.v16, .v17), customize: spy1) #elseif os(macOS) - .introspect(.table, on: .macOS(.v12, .v13), customize: spy1) + .introspect(.table, on: .macOS(.v12, .v13, .v14), customize: spy1) #endif TipTable() .tableStyle(.inset) #if os(iOS) - .introspect(.table, on: .iOS(.v16), customize: spy2) + .introspect(.table, on: .iOS(.v16, .v17), customize: spy2) #elseif os(macOS) - .introspect(.table, on: .macOS(.v12, .v13), customize: spy2) + .introspect(.table, on: .macOS(.v12, .v13, .v14), customize: spy2) #endif } } @@ -99,19 +99,19 @@ final class TableTests: XCTestCase { TipTable() .tableStyle(.bordered) #if os(macOS) - .introspect(.table, on: .macOS(.v12, .v13), customize: spy0) + .introspect(.table, on: .macOS(.v12, .v13, .v14), customize: spy0) #endif TipTable() .tableStyle(.bordered) #if os(macOS) - .introspect(.table, on: .macOS(.v12, .v13), customize: spy1) + .introspect(.table, on: .macOS(.v12, .v13, .v14), customize: spy1) #endif TipTable() .tableStyle(.bordered) #if os(macOS) - .introspect(.table, on: .macOS(.v12, .v13), customize: spy2) + .introspect(.table, on: .macOS(.v12, .v13, .v14), customize: spy2) #endif } } diff --git a/Tests/Tests/ViewTypes/TextEditorTests.swift b/Tests/Tests/ViewTypes/TextEditorTests.swift index b89047d..91243fc 100644 --- a/Tests/Tests/ViewTypes/TextEditorTests.swift +++ b/Tests/Tests/ViewTypes/TextEditorTests.swift @@ -24,25 +24,25 @@ final class TextEditorTests: XCTestCase { VStack { TextEditor(text: .constant("Text Field 0")) #if os(iOS) - .introspect(.textEditor, on: .iOS(.v14, .v15, .v16), customize: spy0) + .introspect(.textEditor, on: .iOS(.v14, .v15, .v16, .v17), customize: spy0) #elseif os(macOS) - .introspect(.textEditor, on: .macOS(.v11, .v12, .v13), customize: spy0) + .introspect(.textEditor, on: .macOS(.v11, .v12, .v13, .v14), customize: spy0) #endif .cornerRadius(8) TextEditor(text: .constant("Text Field 1")) #if os(iOS) - .introspect(.textEditor, on: .iOS(.v14, .v15, .v16), customize: spy1) + .introspect(.textEditor, on: .iOS(.v14, .v15, .v16, .v17), customize: spy1) #elseif os(macOS) - .introspect(.textEditor, on: .macOS(.v11, .v12, .v13), customize: spy1) + .introspect(.textEditor, on: .macOS(.v11, .v12, .v13, .v14), customize: spy1) #endif .cornerRadius(8) TextEditor(text: .constant("Text Field 2")) #if os(iOS) - .introspect(.textEditor, on: .iOS(.v14, .v15, .v16), customize: spy2) + .introspect(.textEditor, on: .iOS(.v14, .v15, .v16, .v17), customize: spy2) #elseif os(macOS) - .introspect(.textEditor, on: .macOS(.v11, .v12, .v13), customize: spy2) + .introspect(.textEditor, on: .macOS(.v11, .v12, .v13, .v14), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/TextFieldTests.swift b/Tests/Tests/ViewTypes/TextFieldTests.swift index b5126db..b68ce7e 100644 --- a/Tests/Tests/ViewTypes/TextFieldTests.swift +++ b/Tests/Tests/ViewTypes/TextFieldTests.swift @@ -18,25 +18,25 @@ final class TextFieldTests: XCTestCase { VStack { TextField("", text: .constant("Text Field 0")) #if os(iOS) || os(tvOS) - .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy0) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) #elseif os(macOS) - .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif .cornerRadius(8) TextField("", text: .constant("Text Field 1")) #if os(iOS) || os(tvOS) - .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy1) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) #elseif os(macOS) - .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) #endif .cornerRadius(8) TextField("", text: .constant("Text Field 2")) #if os(iOS) || os(tvOS) - .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy2) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) #elseif os(macOS) - .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif } } extraAssertions: { @@ -61,23 +61,23 @@ final class TextFieldTests: XCTestCase { List { TextField("", text: .constant("Text Field 0")) #if os(iOS) || os(tvOS) - .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy0) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) #elseif os(macOS) - .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif TextField("", text: .constant("Text Field 1")) #if os(iOS) || os(tvOS) - .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy1) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) #elseif os(macOS) - .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) #endif TextField("", text: .constant("Text Field 2")) #if os(iOS) || os(tvOS) - .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy2) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) #elseif os(macOS) - .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/TextFieldWithVerticalAxisTests.swift b/Tests/Tests/ViewTypes/TextFieldWithVerticalAxisTests.swift index cd877fa..d6b008f 100644 --- a/Tests/Tests/ViewTypes/TextFieldWithVerticalAxisTests.swift +++ b/Tests/Tests/ViewTypes/TextFieldWithVerticalAxisTests.swift @@ -26,31 +26,31 @@ final class TextFieldWithVerticalAxisTests: XCTestCase { VStack { TextField("", text: .constant("Text Field 1"), axis: .vertical) #if os(iOS) - .introspect(.textField(axis: .vertical), on: .iOS(.v16), customize: spy0) + .introspect(.textField(axis: .vertical), on: .iOS(.v16, .v17), customize: spy0) #elseif os(tvOS) - .introspect(.textField(axis: .vertical), on: .tvOS(.v16), customize: spy0) + .introspect(.textField(axis: .vertical), on: .tvOS(.v16, .v17), customize: spy0) #elseif os(macOS) - .introspect(.textField(axis: .vertical), on: .macOS(.v13), customize: spy0) + .introspect(.textField(axis: .vertical), on: .macOS(.v13, .v14), customize: spy0) #endif .cornerRadius(8) TextField("", text: .constant("Text Field 2"), axis: .vertical) #if os(iOS) - .introspect(.textField(axis: .vertical), on: .iOS(.v16), customize: spy1) + .introspect(.textField(axis: .vertical), on: .iOS(.v16, .v17), customize: spy1) #elseif os(tvOS) - .introspect(.textField(axis: .vertical), on: .tvOS(.v16), customize: spy1) + .introspect(.textField(axis: .vertical), on: .tvOS(.v16, .v17), customize: spy1) #elseif os(macOS) - .introspect(.textField(axis: .vertical), on: .macOS(.v13), customize: spy1) + .introspect(.textField(axis: .vertical), on: .macOS(.v13, .v14), customize: spy1) #endif .cornerRadius(8) TextField("", text: .constant("Text Field 3"), axis: .vertical) #if os(iOS) - .introspect(.textField(axis: .vertical), on: .iOS(.v16), customize: spy2) + .introspect(.textField(axis: .vertical), on: .iOS(.v16, .v17), customize: spy2) #elseif os(tvOS) - .introspect(.textField(axis: .vertical), on: .tvOS(.v16), customize: spy2) + .introspect(.textField(axis: .vertical), on: .tvOS(.v16, .v17), customize: spy2) #elseif os(macOS) - .introspect(.textField(axis: .vertical), on: .macOS(.v13), customize: spy2) + .introspect(.textField(axis: .vertical), on: .macOS(.v13, .v14), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/ToggleTests.swift b/Tests/Tests/ViewTypes/ToggleTests.swift index 8ffb641..9278124 100644 --- a/Tests/Tests/ViewTypes/ToggleTests.swift +++ b/Tests/Tests/ViewTypes/ToggleTests.swift @@ -19,23 +19,23 @@ final class ToggleTests: XCTestCase { VStack { Toggle("", isOn: .constant(true)) #if os(iOS) - .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16), customize: spy0) + .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) #elseif os(macOS) - .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif Toggle("", isOn: .constant(false)) #if os(iOS) - .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16), customize: spy1) + .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) #elseif os(macOS) - .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) #endif Toggle("", isOn: .constant(true)) #if os(iOS) - .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16), customize: spy2) + .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) #elseif os(macOS) - .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/ToggleWithButtonStyleTests.swift b/Tests/Tests/ViewTypes/ToggleWithButtonStyleTests.swift index 9636d93..d2f5896 100644 --- a/Tests/Tests/ViewTypes/ToggleWithButtonStyleTests.swift +++ b/Tests/Tests/ViewTypes/ToggleWithButtonStyleTests.swift @@ -23,19 +23,19 @@ final class ToggleWithButtonStyleTests: XCTestCase { Toggle("", isOn: .constant(true)) .toggleStyle(.button) #if os(macOS) - .introspect(.toggle(style: .button), on: .macOS(.v12, .v13), customize: spy0) + .introspect(.toggle(style: .button), on: .macOS(.v12, .v13, .v14), customize: spy0) #endif Toggle("", isOn: .constant(false)) .toggleStyle(.button) #if os(macOS) - .introspect(.toggle(style: .button), on: .macOS(.v12, .v13), customize: spy1) + .introspect(.toggle(style: .button), on: .macOS(.v12, .v13, .v14), customize: spy1) #endif Toggle("", isOn: .constant(true)) .toggleStyle(.button) #if os(macOS) - .introspect(.toggle(style: .button), on: .macOS(.v12, .v13), customize: spy2) + .introspect(.toggle(style: .button), on: .macOS(.v12, .v13, .v14), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/ToggleWithCheckboxStyleTests.swift b/Tests/Tests/ViewTypes/ToggleWithCheckboxStyleTests.swift index 855ebf3..cdf83a9 100644 --- a/Tests/Tests/ViewTypes/ToggleWithCheckboxStyleTests.swift +++ b/Tests/Tests/ViewTypes/ToggleWithCheckboxStyleTests.swift @@ -18,19 +18,19 @@ final class ToggleWithCheckboxStyleTests: XCTestCase { Toggle("", isOn: .constant(true)) .toggleStyle(.checkbox) #if os(macOS) - .introspect(.toggle(style: .checkbox), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + .introspect(.toggle(style: .checkbox), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif Toggle("", isOn: .constant(false)) .toggleStyle(.checkbox) #if os(macOS) - .introspect(.toggle(style: .checkbox), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + .introspect(.toggle(style: .checkbox), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) #endif Toggle("", isOn: .constant(true)) .toggleStyle(.checkbox) #if os(macOS) - .introspect(.toggle(style: .checkbox), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + .introspect(.toggle(style: .checkbox), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/ToggleWithSwitchStyleTests.swift b/Tests/Tests/ViewTypes/ToggleWithSwitchStyleTests.swift index a1daddf..f27b351 100644 --- a/Tests/Tests/ViewTypes/ToggleWithSwitchStyleTests.swift +++ b/Tests/Tests/ViewTypes/ToggleWithSwitchStyleTests.swift @@ -20,25 +20,25 @@ final class ToggleWithSwitchStyleTests: XCTestCase { Toggle("", isOn: .constant(true)) .toggleStyle(.switch) #if os(iOS) - .introspect(.toggle(style: .switch), on: .iOS(.v13, .v14, .v15, .v16), customize: spy0) + .introspect(.toggle(style: .switch), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) #elseif os(macOS) - .introspect(.toggle(style: .switch), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy0) + .introspect(.toggle(style: .switch), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif Toggle("", isOn: .constant(false)) .toggleStyle(.switch) #if os(iOS) - .introspect(.toggle(style: .switch), on: .iOS(.v13, .v14, .v15, .v16), customize: spy1) + .introspect(.toggle(style: .switch), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) #elseif os(macOS) - .introspect(.toggle(style: .switch), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy1) + .introspect(.toggle(style: .switch), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) #endif Toggle("", isOn: .constant(true)) .toggleStyle(.switch) #if os(iOS) - .introspect(.toggle(style: .switch), on: .iOS(.v13, .v14, .v15, .v16), customize: spy2) + .introspect(.toggle(style: .switch), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) #elseif os(macOS) - .introspect(.toggle(style: .switch), on: .macOS(.v10_15, .v11, .v12, .v13), customize: spy2) + .introspect(.toggle(style: .switch), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/ViewTests.swift b/Tests/Tests/ViewTypes/ViewTests.swift index 4cf9a3a..133029b 100644 --- a/Tests/Tests/ViewTypes/ViewTests.swift +++ b/Tests/Tests/ViewTypes/ViewTests.swift @@ -13,7 +13,7 @@ final class ViewTests: XCTestCase { NavigationView { Text("Item 0") #if os(iOS) || os(tvOS) - .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy0) + .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) #endif } .navigationViewStyle(.stack) @@ -21,7 +21,7 @@ final class ViewTests: XCTestCase { NavigationView { Text("Item 1") #if os(iOS) || os(tvOS) - .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), customize: spy1) + .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) #endif } .navigationViewStyle(.stack) diff --git a/docs/SwiftUIIntrospect.md b/docs/SwiftUIIntrospect.md index 8d3cf76..fd87e48 100644 --- a/docs/SwiftUIIntrospect.md +++ b/docs/SwiftUIIntrospect.md @@ -27,7 +27,7 @@ For instance, when introspecting a `ScrollView`... ScrollView { Text("Item 1") } -.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { scrollView in +.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { scrollView in // do something with UIScrollView } ``` @@ -46,7 +46,7 @@ By default, `.introspect` works directly on its _receiver_. This means calling ` ```swift ScrollView { Text("Item 1") - .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16), scope: .ancestor) { scrollView in + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { scrollView in // do something with UIScrollView } } @@ -108,11 +108,11 @@ Examples List { Text("Item") } -.introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16)) { tableView in +.introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { tableView in tableView.backgroundView = UIView() tableView.backgroundColor = .cyan } -.introspect(.list, on: .iOS(.v16)) { collectionView in +.introspect(.list, on: .iOS(.v16, .v17)) { collectionView in collectionView.backgroundView = UIView() collectionView.subviews.dropFirst(1).first?.backgroundColor = .cyan } @@ -124,7 +124,7 @@ List { ScrollView { Text("Item") } -.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { scrollView in +.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { scrollView in scrollView.refreshControl = UIRefreshControl() } ``` @@ -136,7 +136,7 @@ NavigationView { Text("Item") } .navigationViewStyle(.stack) -.introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { navigationController in +.introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { navigationController in navigationController.navigationBar.backgroundColor = .cyan } ``` @@ -145,7 +145,7 @@ NavigationView { ```swift TextField("Text Field", text: <#Binding#>) - .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16), .tvOS(.v13, .v14, .v15, .v16)) { textField in + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { textField in textField.backgroundColor = .red } ```