From 3c3be81765ce0866d9edcaef5a57b9e1f803c4ac Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 15 Apr 2022 15:56:35 +0200 Subject: [PATCH 001/116] Update actions/checkout action to v3 (#145) Co-authored-by: Renovate Bot --- .github/workflows/build-and-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 836fb0b6e..9cdfc21f3 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -13,7 +13,7 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Xcode 12 uses: maxim-lobanov/setup-xcode@v1 From d7c7eca40fc087109389771dc19cc9be872a877a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 15 Apr 2022 15:58:44 +0200 Subject: [PATCH 002/116] Update actions/cache action to v3 (#149) Co-authored-by: Renovate Bot --- .github/workflows/build-and-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 9cdfc21f3..9e24ab8aa 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -30,7 +30,7 @@ jobs: bundler-cache: true - name: Cache cocoapods dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 id: cache-pods with: path: Pods From 63b336ad99073455faaf5a417ac7130a5f1acc0f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 16 Apr 2022 13:08:57 +0200 Subject: [PATCH 003/116] Update dependency ruby to v2.7.6 (#101) Co-authored-by: Renovate Bot --- .ruby-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ruby-version b/.ruby-version index 37c2961c2..49cdd668e 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.2 +2.7.6 From 7ef0df639079491ee1aaf6b83b6fd4d08df80393 Mon Sep 17 00:00:00 2001 From: Philip Niedertscheider Date: Sun, 17 Apr 2022 12:49:59 +0200 Subject: [PATCH 004/116] Added introspect for scroll view in TabBarView with PageTabViewStyle (#117) --- Introspect/ViewExtensions.swift | 18 ++++++++++++++- IntrospectTests/UIKitTests.swift | 39 ++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/Introspect/ViewExtensions.swift b/Introspect/ViewExtensions.swift index 9ae53d6c0..386e735f3 100644 --- a/Introspect/ViewExtensions.swift +++ b/Introspect/ViewExtensions.swift @@ -105,7 +105,23 @@ extension View { return introspect(selector: TargetViewSelector.siblingContainingOrAncestor, customize: customize) } } - + + /// Finds the horizontal `UIScrollView` from a `SwiftUI.TabBarView` with tab style `SwiftUI.PageTabViewStyle`. + /// + /// Customize is called with a `UICollectionView` wrapper, and the horizontal `UIScrollView`. + @available(iOS 14.0, tvOS 14.0, watchOS 7.0, *) + @available(macOS, unavailable) + public func introspectPagedTabView(customize: @escaping (UICollectionView, UIScrollView) -> ()) -> some View { + return introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, customize: { (collectionView: UICollectionView) in + for subview in collectionView.subviews { + if NSStringFromClass(type(of: subview)).contains("EmbeddedScrollView"), let scrollView = subview as? UIScrollView { + customize(collectionView, scrollView) + break + } + } + }) + } + /// Finds a `UITextField` from a `SwiftUI.TextField` public func introspectTextField(customize: @escaping (UITextField) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContainingOrAncestorOrAncestorChild, customize: customize) diff --git a/IntrospectTests/UIKitTests.swift b/IntrospectTests/UIKitTests.swift index 8f22333ce..a42c447ab 100644 --- a/IntrospectTests/UIKitTests.swift +++ b/IntrospectTests/UIKitTests.swift @@ -125,6 +125,24 @@ private struct TabRootTestView: View { } } +@available(iOS 14.0, tvOS 14.0, watchOS 7.0, *) +@available(macOS, unavailable) +private struct PageTabViewStyleTestView: View { + + let spy: (UICollectionView, UIScrollView) -> Void + + var body: some View { + TabView { + Text("Item 1") + .tag(0) + } + .tabViewStyle(PageTabViewStyle()) + .introspectPagedTabView { collectionView, scrollView in + spy(collectionView, scrollView) + } + } +} + @available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) private struct ListTestView: View { @@ -576,6 +594,27 @@ class UIKitTests: XCTestCase { TestUtils.present(view: view) wait(for: [expectation], timeout: TestUtils.Constants.timeout) } + + @available(iOS 14.0, tvOS 14.0, watchOS 7.0, *) + func testPagedTabView() throws { + + var collectionView1: UICollectionView? + var scrollView1: UIScrollView? + + let expectation = XCTestExpectation() + let view = PageTabViewStyleTestView(spy: { collectionView, scrollView in + collectionView1 = collectionView + scrollView1 = scrollView + expectation.fulfill() + }) + TestUtils.present(view: view) + wait(for: [expectation], timeout: TestUtils.Constants.timeout) + + let unwrappedCollectionView = try XCTUnwrap(collectionView1) + let unwrappedScrollView = try XCTUnwrap(scrollView1) + + XCTAssertTrue(unwrappedCollectionView.subviews.contains(where: { $0 === unwrappedScrollView })) + } #endif } #endif From d3cbf32788250741d397cfb5bcd532c37aa1096d Mon Sep 17 00:00:00 2001 From: Cody Rayment Date: Sun, 21 Aug 2022 12:39:13 -0600 Subject: [PATCH 005/116] Apply fix described in #140 (#153) --- Introspect/UIKitIntrospectionView.swift | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Introspect/UIKitIntrospectionView.swift b/Introspect/UIKitIntrospectionView.swift index 5307afed3..484ae55f4 100644 --- a/Introspect/UIKitIntrospectionView.swift +++ b/Introspect/UIKitIntrospectionView.swift @@ -6,6 +6,8 @@ import SwiftUI @available(iOS 13.0, *) public class IntrospectionUIView: UIView { + var moveToWindowHandler: (() -> Void)? + required init() { super.init(frame: .zero) isHidden = true @@ -16,6 +18,11 @@ public class IntrospectionUIView: UIView { required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + override public func didMoveToWindow() { + super.didMoveToWindow() + moveToWindowHandler?() + } } /// Introspection View that is injected into the UIKit hierarchy alongside the target view. @@ -53,11 +60,13 @@ public struct UIKitIntrospectionView: UIViewRepresentabl _ uiView: IntrospectionUIView, context: UIViewRepresentableContext ) { - DispatchQueue.main.async { - guard let targetView = self.selector(uiView) else { - return + uiView.moveToWindowHandler = { + DispatchQueue.main.async { + guard let targetView = self.selector(uiView) else { + return + } + self.customize(targetView) } - self.customize(targetView) } } } From 63a079f102964a6987eaaf70f8fdc39b84feeece Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 24 Jan 2023 21:57:11 +0100 Subject: [PATCH 006/116] Update dependency cocoapods to v1.11.3 (#133) Co-authored-by: Renovate Bot --- Gemfile | 2 +- Gemfile.lock | 74 +++++++++++++++++++++++++++------------------------- 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/Gemfile b/Gemfile index a0469b6fd..ce315d7a6 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source "https://rubygems.org" -gem "cocoapods", "1.10.1" +gem "cocoapods", "1.11.3" gem "fastlane", "~> 2.137" plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') diff --git a/Gemfile.lock b/Gemfile.lock index 1ee04c956..2bd429a52 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,13 +1,15 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.3) - activesupport (5.2.4.5) + CFPropertyList (3.0.5) + rexml + activesupport (6.1.5) concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 0.7, < 2) - minitest (~> 5.1) - tzinfo (~> 1.1) - addressable (2.7.0) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + zeitwerk (~> 2.3) + addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) @@ -30,11 +32,11 @@ GEM aws-sigv4 (1.2.2) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) - claide (1.0.3) - cocoapods (1.10.1) - addressable (~> 2.6) + claide (1.1.0) + cocoapods (1.11.3) + addressable (~> 2.8) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.10.1) + cocoapods-core (= 1.11.3) cocoapods-deintegrate (>= 1.0.3, < 2.0) cocoapods-downloader (>= 1.4.0, < 2.0) cocoapods-plugins (>= 1.0.0, < 2.0) @@ -45,26 +47,26 @@ GEM escape (~> 0.0.4) fourflusher (>= 2.3.0, < 3.0) gh_inspector (~> 1.0) - molinillo (~> 0.6.6) + molinillo (~> 0.8.0) nap (~> 1.0) - ruby-macho (~> 1.4) - xcodeproj (>= 1.19.0, < 2.0) - cocoapods-core (1.10.1) - activesupport (> 5.0, < 6) - addressable (~> 2.6) + ruby-macho (>= 1.0, < 3.0) + xcodeproj (>= 1.21.0, < 2.0) + cocoapods-core (1.11.3) + activesupport (>= 5.0, < 7) + addressable (~> 2.8) algoliasearch (~> 1.0) concurrent-ruby (~> 1.1) fuzzy_match (~> 2.0.4) nap (~> 1.0) netrc (~> 0.11) - public_suffix + public_suffix (~> 4.0) typhoeus (~> 1.0) - cocoapods-deintegrate (1.0.4) - cocoapods-downloader (1.4.0) + cocoapods-deintegrate (1.0.5) + cocoapods-downloader (1.6.1) cocoapods-plugins (1.0.0) nap - cocoapods-search (1.0.0) - cocoapods-trunk (1.5.0) + cocoapods-search (1.0.1) + cocoapods-trunk (1.6.0) nap (>= 0.8, < 2.0) netrc (~> 0.11) cocoapods-try (1.2.0) @@ -73,7 +75,7 @@ GEM colorize (0.8.1) commander-fastlane (4.4.6) highline (~> 1.7.2) - concurrent-ruby (1.1.8) + concurrent-ruby (1.1.10) declarative (0.0.20) declarative-option (0.1.0) digest-crc (0.6.1) @@ -83,8 +85,8 @@ GEM dotenv (2.7.6) emoji_regex (3.0.0) escape (0.0.4) - ethon (0.12.0) - ffi (>= 1.3.0) + ethon (0.15.0) + ffi (>= 1.15.0) excon (0.78.0) faraday (1.1.0) multipart-post (>= 1.2, < 3) @@ -138,7 +140,7 @@ GEM trainer xcodeproj xctest_list (>= 1.2.1) - ffi (1.15.0) + ffi (1.15.5) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) @@ -174,16 +176,16 @@ GEM http-cookie (1.0.3) domain_name (~> 0.5) httpclient (2.8.3) - i18n (1.8.9) + i18n (1.10.0) concurrent-ruby (~> 1.0) jmespath (1.4.0) - json (2.5.1) + json (2.6.1) jwt (2.2.2) memoist (0.16.2) mini_magick (4.10.1) mini_mime (1.0.2) - minitest (5.14.4) - molinillo (0.6.6) + minitest (5.15.0) + molinillo (0.8.0) multi_json (1.15.0) multipart-post (2.0.0) nanaimo (0.3.0) @@ -199,8 +201,9 @@ GEM declarative-option (< 0.2.0) uber (< 0.2.0) retriable (3.1.2) + rexml (3.2.5) rouge (2.0.7) - ruby-macho (1.4.0) + ruby-macho (2.5.1) ruby2_keywords (0.0.2) rubyzip (2.3.0) security (0.1.3) @@ -216,7 +219,6 @@ GEM terminal-notifier (2.0.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) - thread_safe (0.3.6) trainer (0.9.1) fastlane (>= 2.25.0) plist (>= 3.1.0, < 4.0.0) @@ -226,31 +228,33 @@ GEM tty-cursor (~> 0.7) typhoeus (1.4.0) ethon (>= 0.9.0) - tzinfo (1.2.9) - thread_safe (~> 0.1) + tzinfo (2.0.4) + concurrent-ruby (~> 1.0) uber (0.1.0) unf (0.1.4) unf_ext unf_ext (0.0.7.7) unicode-display_width (1.7.0) word_wrap (1.0.0) - xcodeproj (1.19.0) + xcodeproj (1.21.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) nanaimo (~> 0.3.0) + rexml (~> 3.2.4) xcpretty (0.3.0) rouge (~> 2.0.7) xcpretty-travis-formatter (1.0.0) xcpretty (~> 0.2, >= 0.0.7) xctest_list (1.2.1) + zeitwerk (2.5.4) PLATFORMS ruby DEPENDENCIES - cocoapods (= 1.10.1) + cocoapods (= 1.11.3) fastlane (~> 2.137) fastlane-plugin-test_center From f824e009a822ff38e18abf49fb57d19bb4b7bd7b Mon Sep 17 00:00:00 2001 From: Joel Poloney Date: Fri, 10 Feb 2023 09:17:59 -0800 Subject: [PATCH 007/116] Remove CircleCI (#182) Part of #179. I'll remove the config settings once this is merged. --- .circleci/config.yml | 62 -------------------------------------------- 1 file changed, 62 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index c09dd18a6..000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,62 +0,0 @@ - -# Shared Xcode version between jobs -xcode-version: &xcode-version "12.4.0" - -# This is the key that we use to store and restore our dependencies cache. -# It needs to be different anytime we add Pods, Gems, or npm packages. -# If something went wrong during a cache build, you can increase the cache version -# like this: 1-dependencies-... => 2-dependencies-... -cache-key: &cache-key 4-dependencies-{{ checksum "Gemfile.lock" }}-{{ checksum "Podfile.lock" }} - -# Environment that is shared between jobs -shared-env: &shared-env - BUNDLE_PATH: vendor/bundle - FL_OUTPUT_DIR: output - HOMEBREW_NO_AUTO_UPDATE: 1 - -version: 2 -jobs: - build: - macos: - xcode: *xcode-version - environment: - <<: *shared-env - steps: - - checkout - - restore_cache: - key: *cache-key - - run: - name: Install bundler - command: gem install bundler:1.17.3 - - run: - name: Install Ruby bundle dependencies - command: bundle check || bundle install - - run: - name: Install Cocoapods dependencies - command: diff ./Podfile.lock ./Pods/Manifest.lock > /dev/null || bundle exec pod install - - save_cache: - key: *cache-key - paths: - - vendor/bundle/ - - Pods/ - - run: - name: Create reports directory - command: mkdir output - - run: - name: Test iOS/tvOS framework - command: bundle exec fastlane ios test - - run: - name: Test macOS framework - command: bundle exec fastlane mac test - - run: - name: Lint podspec - command: bundle exec pod lib lint - - run: - name: Build swift package - command: swift build - - run: - name: Test swift package - command: swift test - - store_test_results: - path: output - \ No newline at end of file From d8dfc24d8394d20844d986e5271a0567e574f7e8 Mon Sep 17 00:00:00 2001 From: Joel Poloney Date: Fri, 10 Feb 2023 10:16:11 -0800 Subject: [PATCH 008/116] Remove CircleCI badge (#183) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d58c97586..1995b16b7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Introspect for SwiftUI ====================== -[![CircleCI_Status]][CircleCI_URL]  [![GithubCI_Status]][GithubCI_URL] [![Siteline_Badge]](https://siteline.com) [![Quintschaf_Badge]](https://quintschaf.com) +[![GithubCI_Status]][GithubCI_URL] [![Siteline_Badge]](https://siteline.com) [![Quintschaf_Badge]](https://quintschaf.com) > Introspect allows you to get the underlying UIKit or AppKit element of a SwiftUI view. From e4d9b8d3dab1480d0b60061e501dcc87f84b1e6e Mon Sep 17 00:00:00 2001 From: Marcin Krzyzanowski Date: Fri, 10 Feb 2023 19:16:59 +0100 Subject: [PATCH 009/116] Static/dynamic library products (#168) Co-authored-by: David Roman <2538074+davdroman@users.noreply.github.com> Resolves https://github.com/siteline/SwiftUI-Introspect/issues/159 --- Package.swift | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Package.swift b/Package.swift index 574ee13df..d39f203b5 100644 --- a/Package.swift +++ b/Package.swift @@ -10,10 +10,9 @@ let package = Package( .tvOS(.v11) ], products: [ - .library( - name: "Introspect", - targets: ["Introspect"] - ) + .library(name: "Introspect", targets: ["Introspect"]), + .library(name: "Introspect-Static", type: .static, targets: ["Introspect"]), + .library(name: "Introspect-Dynamic", type: .dynamic, targets: ["Introspect"]), ], dependencies: [], targets: [ @@ -28,4 +27,4 @@ let package = Package( path: "IntrospectTests" ) ] -) \ No newline at end of file +) From 46a563fac85644c3bc3ef3c122e4bca18ca400a7 Mon Sep 17 00:00:00 2001 From: Ryan Zulkoski Date: Fri, 10 Feb 2023 12:33:30 -0600 Subject: [PATCH 010/116] Fix view controller introspection (#165) Co-authored-by: Ryan Zulkoski Co-authored-by: David Roman <2538074+davdroman@users.noreply.github.com> --- Introspect/UIKitIntrospectionView.swift | 28 +++++++++---------- .../UIKitIntrospectionViewController.swift | 23 +++++++++------ 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/Introspect/UIKitIntrospectionView.swift b/Introspect/UIKitIntrospectionView.swift index 484ae55f4..a14eb7892 100644 --- a/Introspect/UIKitIntrospectionView.swift +++ b/Introspect/UIKitIntrospectionView.swift @@ -45,29 +45,29 @@ public struct UIKitIntrospectionView: UIViewRepresentabl self.customize = customize } + /// When `makeUIView` and `updateUIView` are called, the Introspection view is not yet in the UIKit hierarchy. + /// At this point, `introspectionView.superview.superview` is nil and we can't access the target UIKit view. + /// To workaround this, we wait until the runloop is done inserting the introspection view in the hierarchy, then run the selector. + /// Finding the target view fails silently if the selector yields no result. This happens when the introspection view gets + /// removed from the hierarchy. public func makeUIView(context: UIViewRepresentableContext) -> IntrospectionUIView { let view = IntrospectionUIView() view.accessibilityLabel = "IntrospectionUIView<\(TargetViewType.self)>" - return view - } - - /// When `updateUiView` is called after creating the Introspection view, it is not yet in the UIKit hierarchy. - /// At this point, `introspectionView.superview.superview` is nil and we can't access the target UIKit view. - /// To workaround this, we wait until the runloop is done inserting the introspection view in the hierarchy, then run the selector. - /// Finding the target view fails silently if the selector yield no result. This happens when `updateUIView` - /// gets called when the introspection view gets removed from the hierarchy. - public func updateUIView( - _ uiView: IntrospectionUIView, - context: UIViewRepresentableContext - ) { - uiView.moveToWindowHandler = { + view.moveToWindowHandler = { [weak view] in + guard let view = view else { return } DispatchQueue.main.async { - guard let targetView = self.selector(uiView) else { + guard let targetView = self.selector(view) else { return } self.customize(targetView) } } + return view } + + public func updateUIView( + _ uiView: IntrospectionUIView, + context: UIViewRepresentableContext + ) {} } #endif diff --git a/Introspect/UIKitIntrospectionViewController.swift b/Introspect/UIKitIntrospectionViewController.swift index e7343a334..fd8e437ec 100644 --- a/Introspect/UIKitIntrospectionViewController.swift +++ b/Introspect/UIKitIntrospectionViewController.swift @@ -31,25 +31,32 @@ public struct UIKitIntrospectionViewController ) -> IntrospectionUIViewController { let viewController = IntrospectionUIViewController() viewController.accessibilityLabel = "IntrospectionUIViewController<\(TargetViewControllerType.self)>" viewController.view.accessibilityLabel = "IntrospectionUIView<\(TargetViewControllerType.self)>" + (viewController.view as? IntrospectionUIView)?.moveToWindowHandler = { [weak viewController] in + guard let viewController = viewController else { return } + DispatchQueue.main.async { + guard let targetView = self.selector(viewController) else { + return + } + self.customize(targetView) + } + } return viewController } public func updateUIViewController( _ uiViewController: IntrospectionUIViewController, context: UIViewControllerRepresentableContext - ) { - DispatchQueue.main.async { - guard let targetView = self.selector(uiViewController) else { - return - } - self.customize(targetView) - } - } + ) {} } #endif From cac338530e3548b1b9fe69f2b4379a9ca7363dc1 Mon Sep 17 00:00:00 2001 From: Chris Maddern Date: Fri, 10 Feb 2023 14:09:15 -0500 Subject: [PATCH 011/116] Add support for UICollectionView introspection (#169) Co-authored-by: David Roman <2538074+davdroman@users.noreply.github.com> --- Introspect/ViewExtensions.swift | 10 ++++++ IntrospectTests/UIKitTests.swift | 52 ++++++++++++++++++++++---------- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/Introspect/ViewExtensions.swift b/Introspect/ViewExtensions.swift index 386e735f3..d4abd396a 100644 --- a/Introspect/ViewExtensions.swift +++ b/Introspect/ViewExtensions.swift @@ -97,6 +97,16 @@ extension View { introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, customize: customize) } + /// Finds a `UICollectionView` from a `SwiftUI.List`, or `SwiftUI.List` child. + public func introspectCollectionView(customize: @escaping (UICollectionView) -> ()) -> some View { + introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, customize: customize) + } + + /// Finds a `UICollectionView` from a `SwiftUI.List`, or `SwiftUI.List` child. You can attach this directly to the element inside the list. + public func introspectCollectionViewCell(customize: @escaping (UICollectionViewCell) -> ()) -> some View { + introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, customize: customize) + } + /// Finds a `UIScrollView` from a `SwiftUI.ScrollView`, or `SwiftUI.ScrollView` child. public func introspectScrollView(customize: @escaping (UIScrollView) -> ()) -> some View { if #available(iOS 14.0, tvOS 14.0, macOS 11.0, *) { diff --git a/IntrospectTests/UIKitTests.swift b/IntrospectTests/UIKitTests.swift index a42c447ab..ff59be312 100644 --- a/IntrospectTests/UIKitTests.swift +++ b/IntrospectTests/UIKitTests.swift @@ -152,22 +152,42 @@ private struct ListTestView: View { let spyCell2: () -> Void var body: some View { - List { - Text("Item 1") - Text("Item 2") - .introspectTableView { tableView in - self.spy2() - } - .introspectTableViewCell { cell in - self.spyCell2() - } - - } - .introspectTableView { tableView in - self.spy1() - } - .introspectTableViewCell { cell in - self.spyCell1() + if #available(iOS 16, tvOS 16, macOS 13, *) { + List { + Text("Item 1") + Text("Item 2") + .introspectCollectionView { tableView in + self.spy2() + } + .introspectCollectionViewCell { cell in + self.spyCell2() + } + + } + .introspectCollectionView { tableView in + self.spy1() + } + .introspectCollectionViewCell { cell in + self.spyCell1() + } + } else { + List { + Text("Item 1") + Text("Item 2") + .introspectTableView { tableView in + self.spy2() + } + .introspectTableViewCell { cell in + self.spyCell2() + } + + } + .introspectTableView { tableView in + self.spy1() + } + .introspectTableViewCell { cell in + self.spyCell1() + } } } } From 3fe1be15b29af36fb74f9dfb7543482df4734d05 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 10 Feb 2023 19:13:54 +0000 Subject: [PATCH 012/116] Cancel ongoing CI runs on commit (#184) --- .github/workflows/build-and-test.yml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 9e24ab8aa..bf17d9f54 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -1,4 +1,5 @@ name: Build and Test Package + on: push: branches: @@ -7,6 +8,10 @@ on: branches: - master +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + jobs: build: runs-on: macos-10.15 @@ -14,7 +19,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@v3 - + - name: Setup Xcode 12 uses: maxim-lobanov/setup-xcode@v1 with: @@ -22,13 +27,13 @@ jobs: - name: Export macOS SDK run: echo SDKROOT=$(xcrun --sdk macosx --show-sdk-path) >> $GITHUB_ENV - + - name: Install gem dependencies uses: ruby/setup-ruby@v1 with: ruby-version: .ruby-version bundler-cache: true - + - name: Cache cocoapods dependencies uses: actions/cache@v3 id: cache-pods @@ -36,19 +41,19 @@ jobs: path: Pods key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} restore-keys: ${{ runner.os }}-pods- - - - name: Install cocoapods dependencies + + - name: Install cocoapods dependencies if: steps.cache-pods.outputs.cache-hit != 'true' run: bundle exec pod install - + - name: Build Swift Package run: swift build -j 2 --disable-index-store -v - name: Test Framework on iOS and tvOS run: bundle exec fastlane ios test ci:github - + - name: Test Framework on macOS run: bundle exec fastlane mac test ci:github - + - name: Lint podspec run: bundle exec pod lib lint From 223b26ba8b525530f1eb9355f80b84ec735ba168 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sat, 11 Feb 2023 23:34:16 +0000 Subject: [PATCH 013/116] Revamp CI (#185) --- .github/workflows/build-and-test.yml | 59 - .github/workflows/ci.yml | 64 + .gitignore | 84 +- .ruby-version | 1 - .../contents.xcworkspacedata | 0 Examples/Package.swift | 9 + .../Showcase.xcodeproj/project.pbxproj | 376 ++++++ .../contents.xcworkspacedata | 2 +- .../xcshareddata/IDEWorkspaceChecks.plist | 0 Examples/Showcase/Showcase/App.swift | 10 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 13 + .../Showcase/Assets.xcassets/Contents.json | 6 + .../Showcase/Showcase}/ContentView.swift | 110 +- .../Preview Assets.xcassets/Contents.json | 6 + .../Showcase/Showcase/Showcase.entitlements | 10 + Gemfile | 7 - Gemfile.lock | 262 ---- Introspect.podspec | 8 +- Introspect.xcodeproj/project.pbxproj | 1172 ----------------- .../xcschemes/xcschememanagement.plist | 62 - .../contents.xcworkspacedata | 4 +- .../xcschemes/Introspect-Dynamic.xcscheme | 28 +- .../xcschemes/Introspect-Static.xcscheme | 28 +- .../xcschemes/Introspect.xcscheme | 26 +- Introspect/AppKitIntrospectionView.swift | 2 - Introspect/Info.plist | 22 - Introspect/Introspect.h | 7 - Introspect/UIKitIntrospectionView.swift | 2 - .../UIKitIntrospectionViewController.swift | 2 - Introspect/ViewExtensions.swift | 7 +- IntrospectExamples/AppDelegate.swift | 9 - IntrospectExamples/Info.plist | 60 - IntrospectExamples/SceneDelegate.swift | 26 - IntrospectTests/AppKitTests.swift | 57 +- IntrospectTests/Info.plist | 22 - IntrospectTests/UIKitTests.swift | 70 +- Package.swift | 16 +- Podfile | 9 - Podfile.lock | 16 - fastlane/Fastfile | 52 +- fastlane/Pluginfile | 2 - renovate.json | 5 - 43 files changed, 709 insertions(+), 2035 deletions(-) delete mode 100644 .github/workflows/build-and-test.yml create mode 100644 .github/workflows/ci.yml delete mode 100644 .ruby-version rename {.swiftpm => Examples/.swiftpm}/xcode/package.xcworkspace/contents.xcworkspacedata (100%) create mode 100644 Examples/Package.swift create mode 100644 Examples/Showcase/Showcase.xcodeproj/project.pbxproj rename {Introspect.xcodeproj => Examples/Showcase/Showcase.xcodeproj}/project.xcworkspace/contents.xcworkspacedata (70%) rename {Introspect.xcodeproj => Examples/Showcase/Showcase.xcodeproj}/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) create mode 100644 Examples/Showcase/Showcase/App.swift create mode 100644 Examples/Showcase/Showcase/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Examples/Showcase/Showcase/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Examples/Showcase/Showcase/Assets.xcassets/Contents.json rename {IntrospectExamples => Examples/Showcase/Showcase}/ContentView.swift (72%) create mode 100644 Examples/Showcase/Showcase/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 Examples/Showcase/Showcase/Showcase.entitlements delete mode 100644 Gemfile delete mode 100644 Gemfile.lock delete mode 100644 Introspect.xcodeproj/project.pbxproj delete mode 100644 Introspect.xcodeproj/xcuserdata/ldiqual.xcuserdatad/xcschemes/xcschememanagement.plist rename Introspect.xcodeproj/xcshareddata/xcschemes/Introspect tvOS.xcscheme => Introspect.xcworkspace/xcshareddata/xcschemes/Introspect-Dynamic.xcscheme (66%) rename Introspect.xcodeproj/xcshareddata/xcschemes/Introspect macOS.xcscheme => Introspect.xcworkspace/xcshareddata/xcschemes/Introspect-Static.xcscheme (66%) rename Introspect.xcodeproj/xcshareddata/xcschemes/Introspect iOS.xcscheme => Introspect.xcworkspace/xcshareddata/xcschemes/Introspect.xcscheme (73%) delete mode 100644 Introspect/Info.plist delete mode 100644 Introspect/Introspect.h delete mode 100644 IntrospectExamples/AppDelegate.swift delete mode 100644 IntrospectExamples/Info.plist delete mode 100644 IntrospectExamples/SceneDelegate.swift delete mode 100644 IntrospectTests/Info.plist delete mode 100644 Podfile delete mode 100644 Podfile.lock delete mode 100644 renovate.json diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml deleted file mode 100644 index bf17d9f54..000000000 --- a/.github/workflows/build-and-test.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Build and Test Package - -on: - push: - branches: - - master - pull_request: - branches: - - master - -concurrency: - group: ci-${{ github.ref }} - cancel-in-progress: true - -jobs: - build: - runs-on: macos-10.15 - steps: - - - name: Checkout Repository - uses: actions/checkout@v3 - - - name: Setup Xcode 12 - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: '12.4' - - - name: Export macOS SDK - run: echo SDKROOT=$(xcrun --sdk macosx --show-sdk-path) >> $GITHUB_ENV - - - name: Install gem dependencies - uses: ruby/setup-ruby@v1 - with: - ruby-version: .ruby-version - bundler-cache: true - - - name: Cache cocoapods dependencies - uses: actions/cache@v3 - id: cache-pods - with: - path: Pods - key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} - restore-keys: ${{ runner.os }}-pods- - - - name: Install cocoapods dependencies - if: steps.cache-pods.outputs.cache-hit != 'true' - run: bundle exec pod install - - - name: Build Swift Package - run: swift build -j 2 --disable-index-store -v - - - name: Test Framework on iOS and tvOS - run: bundle exec fastlane ios test ci:github - - - name: Test Framework on macOS - run: bundle exec fastlane mac test ci:github - - - name: Lint podspec - run: bundle exec pod lib lint diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..1837e6e99 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,64 @@ +name: ci + +on: + push: + branches: + - master + pull_request: + branches: + - "**" + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + ci: + name: ${{ matrix.platform[0] }} ${{ matrix.platform[1] }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + platform: + - [ios, 14] + - [ios, 15] + - [ios, 16] + - [macos, 11] + - [macos, 12] + include: + - platform: [ios, 14] + os: macos-11 + xcode: 12.5.1 + - platform: [ios, 15] + os: macos-12 + xcode: 13.4.1 + - platform: [ios, 16] + os: macos-12 + xcode: 14.2 + - platform: [macos, 11] + os: macos-11 + xcode: 13.0 + - platform: [macos, 12] + os: macos-12 + xcode: 14.0.1 + + steps: + - name: Git Checkout + uses: actions/checkout@v3 + + - name: Select Xcode version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: ${{ matrix.xcode }} + + # - name: Export macOS SDK + # run: echo SDKROOT=$(xcrun --sdk macosx --show-sdk-path) >> $GITHUB_ENV + + - name: Install Homebrew dependencies + run: brew install xcbeautify + + - name: Lint Podspec + run: pod lib lint + + - name: Run Tests + run: fastlane ${{ matrix.platform[0] }} test version:${{ matrix.platform[1] }} diff --git a/.gitignore b/.gitignore index d43323d26..4f0a90591 100644 --- a/.gitignore +++ b/.gitignore @@ -1,87 +1,13 @@ .DS_Store - -# Xcode -# -# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore - -## User settings +/.build +/.swiftpm +/Packages +/*.xcodeproj xcuserdata/ - -## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) -*.xcscmblueprint -*.xccheckout - -## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) -build/ DerivedData/ -*.moved-aside -*.pbxuser -!default.pbxuser -*.mode1v3 -!default.mode1v3 -*.mode2v3 -!default.mode2v3 -*.perspectivev3 -!default.perspectivev3 - -## Obj-C/Swift specific -*.hmap - -## App packaging -*.ipa -*.dSYM.zip -*.dSYM - -## Playgrounds -timeline.xctimeline -playground.xcworkspace - -# Swift Package Manager -# -# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. -# Packages/ -# Package.pins -# Package.resolved -# *.xcodeproj -# -# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata -# hence it is not needed unless you have added a package configuration file to your project -# .swiftpm - -.build/ - -# CocoaPods -Pods/ -# -# Add this line if you want to avoid checking in source code from the Xcode workspace -# *.xcworkspace - -# Carthage -# -# Add this line if you want to avoid checking in source code from Carthage dependencies. -# Carthage/Checkouts - -Carthage/Build/ - -# Accio dependency management -Dependencies/ -.accio/ - -# fastlane -# -# It is recommended to not store the screenshots in the git repo. -# Instead, use fastlane to re-generate the screenshots whenever they are needed. -# For more information about the recommended setup visit: -# https://docs.fastlane.tools/best-practices/source-control/#source-control +.netrc fastlane/report.xml fastlane/Preview.html fastlane/screenshots/**/*.png fastlane/test_output - -# Code Injection -# -# After new code Injection tools there's a generated folder /iOSInjectionProject -# https://github.com/johnno1962/injectionforxcode - -iOSInjectionProject/ diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index 49cdd668e..000000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -2.7.6 diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/Examples/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata similarity index 100% rename from .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata rename to Examples/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata diff --git a/Examples/Package.swift b/Examples/Package.swift new file mode 100644 index 000000000..dbea1ee81 --- /dev/null +++ b/Examples/Package.swift @@ -0,0 +1,9 @@ +// swift-tools-version:5.4 + +import PackageDescription + +let package = Package( + name: "Examples", + products: [], + targets: [] +) diff --git a/Examples/Showcase/Showcase.xcodeproj/project.pbxproj b/Examples/Showcase/Showcase.xcodeproj/project.pbxproj new file mode 100644 index 000000000..f57b87d6b --- /dev/null +++ b/Examples/Showcase/Showcase.xcodeproj/project.pbxproj @@ -0,0 +1,376 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + 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 */; }; +/* 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 = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + D53071F029983CEF00F1936C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D530720729983DCA00F1936C /* Introspect in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + D53071EA29983CEF00F1936C = { + isa = PBXGroup; + children = ( + D53071F529983CEF00F1936C /* Showcase */, + D53071F429983CEF00F1936C /* Products */, + D530720529983DCA00F1936C /* Frameworks */, + ); + sourceTree = ""; + }; + D53071F429983CEF00F1936C /* Products */ = { + isa = PBXGroup; + children = ( + D53071F329983CEF00F1936C /* Showcase.app */, + ); + name = Products; + sourceTree = ""; + }; + D53071F529983CEF00F1936C /* Showcase */ = { + isa = PBXGroup; + children = ( + D530720429983D9300F1936C /* Showcase.entitlements */, + D53071F629983CEF00F1936C /* App.swift */, + D53071F829983CEF00F1936C /* ContentView.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 = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + D53071F229983CEF00F1936C /* Showcase */ = { + isa = PBXNativeTarget; + buildConfigurationList = D530720129983CF000F1936C /* Build configuration list for PBXNativeTarget "Showcase" */; + buildPhases = ( + D53071EF29983CEF00F1936C /* Sources */, + D53071F029983CEF00F1936C /* Frameworks */, + D53071F129983CEF00F1936C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Showcase; + packageProductDependencies = ( + D530720629983DCA00F1936C /* Introspect */, + ); + productName = Showcase; + productReference = D53071F329983CEF00F1936C /* Showcase.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D53071EB29983CEF00F1936C /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1420; + LastUpgradeCheck = 1420; + TargetAttributes = { + D53071F229983CEF00F1936C = { + CreatedOnToolsVersion = 14.2; + }; + }; + }; + buildConfigurationList = D53071EE29983CEF00F1936C /* Build configuration list for PBXProject "Showcase" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = D53071EA29983CEF00F1936C; + productRefGroup = D53071F429983CEF00F1936C /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D53071F229983CEF00F1936C /* Showcase */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + D53071F129983CEF00F1936C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D53071FE29983CF000F1936C /* Preview Assets.xcassets in Resources */, + D53071FB29983CF000F1936C /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + D53071EF29983CEF00F1936C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D53071F929983CEF00F1936C /* ContentView.swift in Sources */, + D53071F729983CEF00F1936C /* App.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + D53071FF29983CF000F1936C /* 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; + COPY_PHASE_STRIP = NO; + 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; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TVOS_DEPLOYMENT_TARGET = 14.0; + }; + name = Debug; + }; + D530720029983CF000F1936C /* 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; + COPY_PHASE_STRIP = NO; + 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; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TVOS_DEPLOYMENT_TARGET = 14.0; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 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_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + 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; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3,6"; + }; + name = Debug; + }; + 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_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + 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; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3,6"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + D53071EE29983CEF00F1936C /* Build configuration list for PBXProject "Showcase" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D53071FF29983CF000F1936C /* Debug */, + D530720029983CF000F1936C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D530720129983CF000F1936C /* Build configuration list for PBXNativeTarget "Showcase" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D530720229983CF000F1936C /* Debug */, + D530720329983CF000F1936C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCSwiftPackageProductDependency section */ + D530720629983DCA00F1936C /* Introspect */ = { + isa = XCSwiftPackageProductDependency; + productName = Introspect; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = D53071EB29983CEF00F1936C /* Project object */; +} diff --git a/Introspect.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/Showcase/Showcase.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 70% rename from Introspect.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Examples/Showcase/Showcase.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 3bd6eed33..919434a62 100644 --- a/Introspect.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/Examples/Showcase/Showcase.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/Introspect.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Examples/Showcase/Showcase.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from Introspect.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Examples/Showcase/Showcase.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/Examples/Showcase/Showcase/App.swift b/Examples/Showcase/Showcase/App.swift new file mode 100644 index 000000000..88579ae08 --- /dev/null +++ b/Examples/Showcase/Showcase/App.swift @@ -0,0 +1,10 @@ +import SwiftUI + +@main +struct App: SwiftUI.App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/Examples/Showcase/Showcase/Assets.xcassets/AccentColor.colorset/Contents.json b/Examples/Showcase/Showcase/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/Examples/Showcase/Showcase/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "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 new file mode 100644 index 000000000..13613e3ee --- /dev/null +++ b/Examples/Showcase/Showcase/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "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 new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Examples/Showcase/Showcase/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/IntrospectExamples/ContentView.swift b/Examples/Showcase/Showcase/ContentView.swift similarity index 72% rename from IntrospectExamples/ContentView.swift rename to Examples/Showcase/Showcase/ContentView.swift index bf5f7fba2..3cfe549df 100644 --- a/IntrospectExamples/ContentView.swift +++ b/Examples/Showcase/Showcase/ContentView.swift @@ -5,31 +5,31 @@ struct ContentView: View { @State private var selection = 0 var body: some View { TabView(selection: $selection) { - ListExample() + ListShowcase() .tabItem { Text("List") } .tag(0) .introspectTabBarController { tabBarController in tabBarController.tabBar.layer.backgroundColor = UIColor.green.cgColor } - ScrollViewExample() + ScrollViewShowcase() .tabItem { Text("ScrollView") } .tag(1) - NavigationExample() + NavigationShowcase() .tabItem { Text("Navigation") } .tag(2) - ViewControllerExample() + ViewControllerShowcase() .tabItem { Text("ViewController") } .tag(3) - SimpleElementsExample() + SimpleElementsShowcase() .tabItem { Text("Simple elements") } .tag(4) } } } -struct ListExample: View { +struct ListShowcase: View { var body: some View { - + HStack { VStack { Text("Default") @@ -38,7 +38,7 @@ struct ListExample: View { Text("Item 2") } } - + VStack { Text("List.introspectTableView()") List { @@ -46,32 +46,38 @@ struct ListExample: View { Text("Item 2") } .introspectTableView { tableView in + #if !os(tvOS) tableView.separatorStyle = .none + #endif } } - + VStack { Text("child.introspectTableView()") List { Text("Item 1") Text("Item 2") .introspectTableView { tableView in + #if !os(tvOS) tableView.separatorStyle = .none + #endif } } } } - + } } -struct NavigationExample: View { +struct NavigationShowcase: View { var body: some View { NavigationView { VStack { Text("Customized") } + #if !os(tvOS) .navigationBarTitle(Text("Customized"), displayMode: .inline) + #endif .introspectNavigationController { nvc in nvc.navigationBar.backgroundColor = .red } @@ -79,7 +85,7 @@ struct NavigationExample: View { } } -struct ViewControllerExample: View { +struct ViewControllerShowcase: View { var body: some View { NavigationView { VStack { @@ -92,7 +98,7 @@ struct ViewControllerExample: View { } } -struct ScrollViewExample: View { +struct ScrollViewShowcase: View { var body: some View { HStack { ScrollView { @@ -106,60 +112,65 @@ struct ScrollViewExample: View { } ScrollView { Text("child.introspectScrollView()") - .introspectScrollView { scrollView in - scrollView.layer.backgroundColor = UIColor.green.cgColor - } + .introspectScrollView { scrollView in + scrollView.layer.backgroundColor = UIColor.green.cgColor + } } } } } -struct SimpleElementsExample: View { - +struct SimpleElementsShowcase: View { + @State private var textFieldValue = "" @State private var toggleValue = false @State private var sliderValue = 0.0 @State private var datePickerValue = Date() @State private var segmentedControlValue = 0 - + var body: some View { VStack { HStack { TextField("Text Field Red", text: $textFieldValue) - .introspectTextField { textField in - textField.layer.backgroundColor = UIColor.red.cgColor - } - + .introspectTextField { textField in + textField.layer.backgroundColor = UIColor.red.cgColor + } + TextField("Text Field Green", text: $textFieldValue) - .introspectTextField { textField in - textField.layer.backgroundColor = UIColor.green.cgColor - } + .introspectTextField { textField in + textField.layer.backgroundColor = UIColor.green.cgColor + } } - + HStack { Toggle("Toggle Red", isOn: $toggleValue) - .introspectSwitch { uiSwitch in - uiSwitch.layer.backgroundColor = UIColor.red.cgColor - } - + #if !os(tvOS) + .introspectSwitch { uiSwitch in + uiSwitch.layer.backgroundColor = UIColor.red.cgColor + } + #endif + Toggle("Toggle Green", isOn: $toggleValue) - .introspectSwitch { uiSwitch in - uiSwitch.layer.backgroundColor = UIColor.green.cgColor - } + #if !os(tvOS) + .introspectSwitch { uiSwitch in + uiSwitch.layer.backgroundColor = UIColor.green.cgColor + } + #endif } - + + #if !os(tvOS) HStack { Slider(value: $sliderValue, in: 0...100) - .introspectSlider { slider in - slider.layer.backgroundColor = UIColor.red.cgColor - } - + .introspectSlider { slider in + slider.layer.backgroundColor = UIColor.red.cgColor + } + Slider(value: $sliderValue, in: 0...100) - .introspectSlider { slider in - slider.layer.backgroundColor = UIColor.green.cgColor - } + .introspectSlider { slider in + slider.layer.backgroundColor = UIColor.green.cgColor + } } - + HStack { Stepper(onIncrement: {}, onDecrement: {}) { Text("Stepper Red") @@ -167,7 +178,7 @@ struct SimpleElementsExample: View { .introspectStepper { stepper in stepper.layer.backgroundColor = UIColor.red.cgColor } - + Stepper(onIncrement: {}, onDecrement: {}) { Text("Stepper Green") } @@ -175,7 +186,7 @@ struct SimpleElementsExample: View { stepper.layer.backgroundColor = UIColor.green.cgColor } } - + HStack { DatePicker(selection: $datePickerValue) { Text("DatePicker Red") @@ -184,7 +195,8 @@ struct SimpleElementsExample: View { datePicker.layer.backgroundColor = UIColor.red.cgColor } } - + #endif + HStack { Picker(selection: $segmentedControlValue, label: Text("Segmented control")) { Text("Option 1").tag(0) @@ -197,15 +209,15 @@ struct SimpleElementsExample: View { } } } - + } } struct ContentView_Previews: PreviewProvider { static var previews: some View { Group { - ListExample() - NavigationExample() + ListShowcase() + NavigationShowcase() } } } diff --git a/Examples/Showcase/Showcase/Preview Content/Preview Assets.xcassets/Contents.json b/Examples/Showcase/Showcase/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Examples/Showcase/Showcase/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/Showcase/Showcase/Showcase.entitlements b/Examples/Showcase/Showcase/Showcase.entitlements new file mode 100644 index 000000000..ee95ab7e5 --- /dev/null +++ b/Examples/Showcase/Showcase/Showcase.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + + diff --git a/Gemfile b/Gemfile deleted file mode 100644 index ce315d7a6..000000000 --- a/Gemfile +++ /dev/null @@ -1,7 +0,0 @@ -source "https://rubygems.org" - -gem "cocoapods", "1.11.3" -gem "fastlane", "~> 2.137" - -plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') -eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 2bd429a52..000000000 --- a/Gemfile.lock +++ /dev/null @@ -1,262 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - CFPropertyList (3.0.5) - rexml - activesupport (6.1.5) - concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 1.6, < 2) - minitest (>= 5.1) - tzinfo (~> 2.0) - zeitwerk (~> 2.3) - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) - algoliasearch (1.27.5) - httpclient (~> 2.8, >= 2.8.3) - json (>= 1.5.1) - atomos (0.1.3) - aws-eventstream (1.1.0) - aws-partitions (1.384.0) - aws-sdk-core (3.109.1) - aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.239.0) - aws-sigv4 (~> 1.1) - jmespath (~> 1.0) - aws-sdk-kms (1.39.0) - aws-sdk-core (~> 3, >= 3.109.0) - aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.83.1) - aws-sdk-core (~> 3, >= 3.109.0) - aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.1) - aws-sigv4 (1.2.2) - aws-eventstream (~> 1, >= 1.0.2) - babosa (1.0.4) - claide (1.1.0) - cocoapods (1.11.3) - addressable (~> 2.8) - claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.11.3) - cocoapods-deintegrate (>= 1.0.3, < 2.0) - cocoapods-downloader (>= 1.4.0, < 2.0) - cocoapods-plugins (>= 1.0.0, < 2.0) - cocoapods-search (>= 1.0.0, < 2.0) - cocoapods-trunk (>= 1.4.0, < 2.0) - cocoapods-try (>= 1.1.0, < 2.0) - colored2 (~> 3.1) - escape (~> 0.0.4) - fourflusher (>= 2.3.0, < 3.0) - gh_inspector (~> 1.0) - molinillo (~> 0.8.0) - nap (~> 1.0) - ruby-macho (>= 1.0, < 3.0) - xcodeproj (>= 1.21.0, < 2.0) - cocoapods-core (1.11.3) - activesupport (>= 5.0, < 7) - addressable (~> 2.8) - algoliasearch (~> 1.0) - concurrent-ruby (~> 1.1) - fuzzy_match (~> 2.0.4) - nap (~> 1.0) - netrc (~> 0.11) - public_suffix (~> 4.0) - typhoeus (~> 1.0) - cocoapods-deintegrate (1.0.5) - cocoapods-downloader (1.6.1) - cocoapods-plugins (1.0.0) - nap - cocoapods-search (1.0.1) - cocoapods-trunk (1.6.0) - nap (>= 0.8, < 2.0) - netrc (~> 0.11) - cocoapods-try (1.2.0) - colored (1.2) - colored2 (3.1.2) - colorize (0.8.1) - commander-fastlane (4.4.6) - highline (~> 1.7.2) - concurrent-ruby (1.1.10) - declarative (0.0.20) - declarative-option (0.1.0) - digest-crc (0.6.1) - rake (~> 13.0) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) - dotenv (2.7.6) - emoji_regex (3.0.0) - escape (0.0.4) - ethon (0.15.0) - ffi (>= 1.15.0) - excon (0.78.0) - faraday (1.1.0) - multipart-post (>= 1.2, < 3) - ruby2_keywords - faraday-cookie_jar (0.0.7) - faraday (>= 0.8.0) - http-cookie (~> 1.0.0) - faraday_middleware (1.0.0) - faraday (~> 1.0) - fastimage (2.2.0) - fastlane (2.164.0) - CFPropertyList (>= 2.3, < 4.0.0) - addressable (>= 2.3, < 3.0.0) - aws-sdk-s3 (~> 1.0) - babosa (>= 1.0.3, < 2.0.0) - bundler (>= 1.12.0, < 3.0.0) - colored - commander-fastlane (>= 4.4.6, < 5.0.0) - dotenv (>= 2.1.1, < 3.0.0) - emoji_regex (>= 0.1, < 4.0) - excon (>= 0.71.0, < 1.0.0) - faraday (~> 1.0) - faraday-cookie_jar (~> 0.0.6) - faraday_middleware (~> 1.0) - fastimage (>= 2.1.0, < 3.0.0) - gh_inspector (>= 1.1.2, < 2.0.0) - google-api-client (>= 0.37.0, < 0.39.0) - google-cloud-storage (>= 1.15.0, < 2.0.0) - highline (>= 1.7.2, < 2.0.0) - json (< 3.0.0) - jwt (>= 2.1.0, < 3) - mini_magick (>= 4.9.4, < 5.0.0) - multipart-post (~> 2.0.0) - plist (>= 3.1.0, < 4.0.0) - rubyzip (>= 2.0.0, < 3.0.0) - security (= 0.1.3) - simctl (~> 1.6.3) - slack-notifier (>= 2.0.0, < 3.0.0) - terminal-notifier (>= 2.0.0, < 3.0.0) - terminal-table (>= 1.4.5, < 2.0.0) - tty-screen (>= 0.6.3, < 1.0.0) - tty-spinner (>= 0.8.0, < 1.0.0) - word_wrap (~> 1.0.0) - xcodeproj (>= 1.13.0, < 2.0.0) - xcpretty (~> 0.3.0) - xcpretty-travis-formatter (>= 0.0.3) - fastlane-plugin-test_center (3.15.3) - colorize - json - plist - trainer - xcodeproj - xctest_list (>= 1.2.1) - ffi (1.15.5) - fourflusher (2.3.1) - fuzzy_match (2.0.4) - gh_inspector (1.1.3) - google-api-client (0.38.0) - addressable (~> 2.5, >= 2.5.1) - googleauth (~> 0.9) - httpclient (>= 2.8.1, < 3.0) - mini_mime (~> 1.0) - representable (~> 3.0) - retriable (>= 2.0, < 4.0) - signet (~> 0.12) - google-cloud-core (1.5.0) - google-cloud-env (~> 1.0) - google-cloud-errors (~> 1.0) - google-cloud-env (1.4.0) - faraday (>= 0.17.3, < 2.0) - google-cloud-errors (1.0.1) - google-cloud-storage (1.29.1) - addressable (~> 2.5) - digest-crc (~> 0.4) - google-api-client (~> 0.33) - google-cloud-core (~> 1.2) - googleauth (~> 0.9) - mini_mime (~> 1.0) - googleauth (0.14.0) - faraday (>= 0.17.3, < 2.0) - jwt (>= 1.4, < 3.0) - memoist (~> 0.16) - multi_json (~> 1.11) - os (>= 0.9, < 2.0) - signet (~> 0.14) - highline (1.7.10) - http-cookie (1.0.3) - domain_name (~> 0.5) - httpclient (2.8.3) - i18n (1.10.0) - concurrent-ruby (~> 1.0) - jmespath (1.4.0) - json (2.6.1) - jwt (2.2.2) - memoist (0.16.2) - mini_magick (4.10.1) - mini_mime (1.0.2) - minitest (5.15.0) - molinillo (0.8.0) - multi_json (1.15.0) - multipart-post (2.0.0) - nanaimo (0.3.0) - nap (1.1.0) - naturally (2.2.0) - netrc (0.11.0) - os (1.1.1) - plist (3.5.0) - public_suffix (4.0.6) - rake (13.0.1) - representable (3.0.4) - declarative (< 0.1.0) - declarative-option (< 0.2.0) - uber (< 0.2.0) - retriable (3.1.2) - rexml (3.2.5) - rouge (2.0.7) - ruby-macho (2.5.1) - ruby2_keywords (0.0.2) - rubyzip (2.3.0) - security (0.1.3) - signet (0.14.0) - addressable (~> 2.3) - faraday (>= 0.17.3, < 2.0) - jwt (>= 1.5, < 3.0) - multi_json (~> 1.10) - simctl (1.6.8) - CFPropertyList - naturally - slack-notifier (2.3.2) - terminal-notifier (2.0.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) - trainer (0.9.1) - fastlane (>= 2.25.0) - plist (>= 3.1.0, < 4.0.0) - tty-cursor (0.7.1) - tty-screen (0.8.1) - tty-spinner (0.9.3) - tty-cursor (~> 0.7) - typhoeus (1.4.0) - ethon (>= 0.9.0) - tzinfo (2.0.4) - concurrent-ruby (~> 1.0) - uber (0.1.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.7.7) - unicode-display_width (1.7.0) - word_wrap (1.0.0) - xcodeproj (1.21.0) - CFPropertyList (>= 2.3.3, < 4.0) - atomos (~> 0.1.3) - claide (>= 1.0.2, < 2.0) - colored2 (~> 3.1) - nanaimo (~> 0.3.0) - rexml (~> 3.2.4) - xcpretty (0.3.0) - rouge (~> 2.0.7) - xcpretty-travis-formatter (1.0.0) - xcpretty (~> 0.2, >= 0.0.7) - xctest_list (1.2.1) - zeitwerk (2.5.4) - -PLATFORMS - ruby - -DEPENDENCIES - cocoapods (= 1.11.3) - fastlane (~> 2.137) - fastlane-plugin-test_center - -BUNDLED WITH - 1.17.3 diff --git a/Introspect.podspec b/Introspect.podspec index 5182b2c87..53e3d8891 100644 --- a/Introspect.podspec +++ b/Introspect.podspec @@ -12,8 +12,8 @@ Pod::Spec.new do |spec| spec.source_files = 'Introspect/*.swift' - spec.swift_version = '5.1' - spec.ios.deployment_target = '11.0' - spec.tvos.deployment_target = '11.0' - spec.osx.deployment_target = '10.13' + spec.swift_version = '5.2' + spec.ios.deployment_target = '13.0' + spec.tvos.deployment_target = '13.0' + spec.osx.deployment_target = '10.15' end diff --git a/Introspect.xcodeproj/project.pbxproj b/Introspect.xcodeproj/project.pbxproj deleted file mode 100644 index 35a6f308a..000000000 --- a/Introspect.xcodeproj/project.pbxproj +++ /dev/null @@ -1,1172 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 51; - objects = { - -/* Begin PBXBuildFile section */ - C068701C238DE85D00DAFD3D /* Introspect.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0687012238DE85D00DAFD3D /* Introspect.framework */; }; - C0687023238DE85D00DAFD3D /* Introspect.h in Headers */ = {isa = PBXBuildFile; fileRef = C0687015238DE85D00DAFD3D /* Introspect.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C068702D238DE8FD00DAFD3D /* Introspect.swift in Sources */ = {isa = PBXBuildFile; fileRef = C068702C238DE8FD00DAFD3D /* Introspect.swift */; }; - C0796E2723F3CD18002BF033 /* Introspect.swift in Sources */ = {isa = PBXBuildFile; fileRef = C068702C238DE8FD00DAFD3D /* Introspect.swift */; }; - C0796E2823F3CD1D002BF033 /* UIKitIntrospectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341423F3A13C005FA859 /* UIKitIntrospectionView.swift */; }; - C0796E2923F3CD1D002BF033 /* UIKitIntrospectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341623F3A149005FA859 /* UIKitIntrospectionViewController.swift */; }; - C0796E3423F3CDA4002BF033 /* Introspect.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0796E1F23F3CCD2002BF033 /* Introspect.framework */; }; - C0C6D68E238E006B00DA6285 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0C6D68D238E006B00DA6285 /* AppDelegate.swift */; }; - C0C6D690238E006B00DA6285 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0C6D68F238E006B00DA6285 /* SceneDelegate.swift */; }; - C0C6D692238E006B00DA6285 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0C6D691238E006B00DA6285 /* ContentView.swift */; }; - C0C6D6A0238E00D300DA6285 /* Introspect.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0687012238DE85D00DAFD3D /* Introspect.framework */; }; - C0ED341123F39EA2005FA859 /* Introspect.swift in Sources */ = {isa = PBXBuildFile; fileRef = C068702C238DE8FD00DAFD3D /* Introspect.swift */; }; - C0ED341523F3A13C005FA859 /* UIKitIntrospectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341423F3A13C005FA859 /* UIKitIntrospectionView.swift */; }; - C0ED341723F3A149005FA859 /* UIKitIntrospectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341623F3A149005FA859 /* UIKitIntrospectionViewController.swift */; }; - C0ED341B23F3A258005FA859 /* AppKitIntrospectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341A23F3A258005FA859 /* AppKitIntrospectionView.swift */; }; - C0ED341D23F3A58B005FA859 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341C23F3A58B005FA859 /* ViewExtensions.swift */; }; - C0ED343A23F3AC43005FA859 /* Introspect.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0ED340923F39E93005FA859 /* Introspect.framework */; }; - C0ED344023F3AC7F005FA859 /* AppKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED342223F3AC14005FA859 /* AppKitTests.swift */; }; - C0ED345D23F48534005FA859 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341C23F3A58B005FA859 /* ViewExtensions.swift */; }; - C0ED345E23F48535005FA859 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341C23F3A58B005FA859 /* ViewExtensions.swift */; }; - C0ED345F23F48671005FA859 /* AppKitIntrospectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341A23F3A258005FA859 /* AppKitIntrospectionView.swift */; }; - C0ED346023F48672005FA859 /* AppKitIntrospectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341A23F3A258005FA859 /* AppKitIntrospectionView.swift */; }; - C0ED346123F486D0005FA859 /* UIKitIntrospectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341423F3A13C005FA859 /* UIKitIntrospectionView.swift */; }; - C0ED346223F48770005FA859 /* UIKitIntrospectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341623F3A149005FA859 /* UIKitIntrospectionViewController.swift */; }; - C0ED346323F48776005FA859 /* Introspect.h in Headers */ = {isa = PBXBuildFile; fileRef = C0687015238DE85D00DAFD3D /* Introspect.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C0ED346423F48776005FA859 /* Introspect.h in Headers */ = {isa = PBXBuildFile; fileRef = C0687015238DE85D00DAFD3D /* Introspect.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C0ED346623F48992005FA859 /* UIKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0687030238DF3C900DAFD3D /* UIKitTests.swift */; }; - C0ED346723F489D5005FA859 /* AppKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED342223F3AC14005FA859 /* AppKitTests.swift */; }; - C0ED346823F489D5005FA859 /* UIKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0687030238DF3C900DAFD3D /* UIKitTests.swift */; }; - C0ED346923F489D6005FA859 /* AppKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED342223F3AC14005FA859 /* AppKitTests.swift */; }; - C0ED346A23F489D6005FA859 /* UIKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0687030238DF3C900DAFD3D /* UIKitTests.swift */; }; - E0B11E0609FFA04A7B6A1418 /* Pods_IntrospectExamples.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5785749C354BCF848BC4EAD9 /* Pods_IntrospectExamples.framework */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - C068701D238DE85D00DAFD3D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = C0687009238DE85D00DAFD3D /* Project object */; - proxyType = 1; - remoteGlobalIDString = C0687011238DE85D00DAFD3D; - remoteInfo = Introspect; - }; - C0796E3523F3CDA4002BF033 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = C0687009238DE85D00DAFD3D /* Project object */; - proxyType = 1; - remoteGlobalIDString = C0796E1E23F3CCD2002BF033; - remoteInfo = "Introspect tvOS"; - }; - C0ED343B23F3AC43005FA859 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = C0687009238DE85D00DAFD3D /* Project object */; - proxyType = 1; - remoteGlobalIDString = C0ED340823F39E93005FA859; - remoteInfo = "Introspect macOS"; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 5785749C354BCF848BC4EAD9 /* Pods_IntrospectExamples.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_IntrospectExamples.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 9BBE78DB32CCDC560004DB54 /* Pods-IntrospectExamples.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-IntrospectExamples.debug.xcconfig"; path = "Target Support Files/Pods-IntrospectExamples/Pods-IntrospectExamples.debug.xcconfig"; sourceTree = ""; }; - B826284199E111BBEA21E76B /* Pods-IntrospectExamples.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-IntrospectExamples.release.xcconfig"; path = "Target Support Files/Pods-IntrospectExamples/Pods-IntrospectExamples.release.xcconfig"; sourceTree = ""; }; - C0687012238DE85D00DAFD3D /* Introspect.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Introspect.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - C0687015238DE85D00DAFD3D /* Introspect.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Introspect.h; sourceTree = ""; }; - C0687016238DE85D00DAFD3D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - C068701B238DE85D00DAFD3D /* Introspect iOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Introspect iOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - C0687022238DE85D00DAFD3D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - C068702C238DE8FD00DAFD3D /* Introspect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Introspect.swift; sourceTree = ""; }; - C0687030238DF3C900DAFD3D /* UIKitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitTests.swift; sourceTree = ""; }; - C0796E1F23F3CCD2002BF033 /* Introspect.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Introspect.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - C0796E2F23F3CDA4002BF033 /* Introspect tvOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Introspect tvOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - C0C6D68B238E006B00DA6285 /* IntrospectExamples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IntrospectExamples.app; sourceTree = BUILT_PRODUCTS_DIR; }; - C0C6D68D238E006B00DA6285 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - C0C6D68F238E006B00DA6285 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; - C0C6D691238E006B00DA6285 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; - C0C6D69B238E007100DA6285 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - C0ED340923F39E93005FA859 /* Introspect.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Introspect.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - C0ED341423F3A13C005FA859 /* UIKitIntrospectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitIntrospectionView.swift; sourceTree = ""; }; - C0ED341623F3A149005FA859 /* UIKitIntrospectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitIntrospectionViewController.swift; sourceTree = ""; }; - C0ED341A23F3A258005FA859 /* AppKitIntrospectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppKitIntrospectionView.swift; sourceTree = ""; }; - C0ED341C23F3A58B005FA859 /* ViewExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewExtensions.swift; sourceTree = ""; }; - C0ED342223F3AC14005FA859 /* AppKitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppKitTests.swift; sourceTree = ""; }; - C0ED343523F3AC43005FA859 /* Introspect macOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Introspect macOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - C068700F238DE85D00DAFD3D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0687018238DE85D00DAFD3D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - C068701C238DE85D00DAFD3D /* Introspect.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0796E1C23F3CCD2002BF033 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0796E2C23F3CDA4002BF033 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - C0796E3423F3CDA4002BF033 /* Introspect.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0C6D688238E006B00DA6285 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - C0C6D6A0238E00D300DA6285 /* Introspect.framework in Frameworks */, - E0B11E0609FFA04A7B6A1418 /* Pods_IntrospectExamples.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0ED340623F39E93005FA859 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0ED343223F3AC43005FA859 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - C0ED343A23F3AC43005FA859 /* Introspect.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 0534532308DF3E515053BF9F /* Pods */ = { - isa = PBXGroup; - children = ( - 9BBE78DB32CCDC560004DB54 /* Pods-IntrospectExamples.debug.xcconfig */, - B826284199E111BBEA21E76B /* Pods-IntrospectExamples.release.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; - C0687008238DE85D00DAFD3D = { - isa = PBXGroup; - children = ( - C0687014238DE85D00DAFD3D /* Introspect */, - C068701F238DE85D00DAFD3D /* IntrospectTests */, - C0C6D68C238E006B00DA6285 /* IntrospectExamples */, - C0687013238DE85D00DAFD3D /* Products */, - C0C6D69F238E00D300DA6285 /* Frameworks */, - 0534532308DF3E515053BF9F /* Pods */, - ); - sourceTree = ""; - }; - C0687013238DE85D00DAFD3D /* Products */ = { - isa = PBXGroup; - children = ( - C0687012238DE85D00DAFD3D /* Introspect.framework */, - C068701B238DE85D00DAFD3D /* Introspect iOS Tests.xctest */, - C0C6D68B238E006B00DA6285 /* IntrospectExamples.app */, - C0ED340923F39E93005FA859 /* Introspect.framework */, - C0ED343523F3AC43005FA859 /* Introspect macOS Tests.xctest */, - C0796E1F23F3CCD2002BF033 /* Introspect.framework */, - C0796E2F23F3CDA4002BF033 /* Introspect tvOS Tests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - C0687014238DE85D00DAFD3D /* Introspect */ = { - isa = PBXGroup; - children = ( - C0ED341A23F3A258005FA859 /* AppKitIntrospectionView.swift */, - C068702C238DE8FD00DAFD3D /* Introspect.swift */, - C0ED341623F3A149005FA859 /* UIKitIntrospectionViewController.swift */, - C0ED341423F3A13C005FA859 /* UIKitIntrospectionView.swift */, - C0ED341C23F3A58B005FA859 /* ViewExtensions.swift */, - C0ED346523F4880B005FA859 /* Supporting Files */, - ); - path = Introspect; - sourceTree = ""; - }; - C068701F238DE85D00DAFD3D /* IntrospectTests */ = { - isa = PBXGroup; - children = ( - C0ED342223F3AC14005FA859 /* AppKitTests.swift */, - C0687030238DF3C900DAFD3D /* UIKitTests.swift */, - C0687022238DE85D00DAFD3D /* Info.plist */, - ); - path = IntrospectTests; - sourceTree = ""; - }; - C0C6D68C238E006B00DA6285 /* IntrospectExamples */ = { - isa = PBXGroup; - children = ( - C0C6D68D238E006B00DA6285 /* AppDelegate.swift */, - C0C6D68F238E006B00DA6285 /* SceneDelegate.swift */, - C0C6D691238E006B00DA6285 /* ContentView.swift */, - C0C6D69B238E007100DA6285 /* Info.plist */, - ); - path = IntrospectExamples; - sourceTree = ""; - }; - C0C6D69F238E00D300DA6285 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 5785749C354BCF848BC4EAD9 /* Pods_IntrospectExamples.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - C0ED346523F4880B005FA859 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - C0687016238DE85D00DAFD3D /* Info.plist */, - C0687015238DE85D00DAFD3D /* Introspect.h */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - C068700D238DE85D00DAFD3D /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - C0687023238DE85D00DAFD3D /* Introspect.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0796E1A23F3CCD2002BF033 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - C0ED346423F48776005FA859 /* Introspect.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0ED340423F39E93005FA859 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - C0ED346323F48776005FA859 /* Introspect.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - C0687011238DE85D00DAFD3D /* Introspect iOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = C0687026238DE85D00DAFD3D /* Build configuration list for PBXNativeTarget "Introspect iOS" */; - buildPhases = ( - C068700D238DE85D00DAFD3D /* Headers */, - C068700E238DE85D00DAFD3D /* Sources */, - C068700F238DE85D00DAFD3D /* Frameworks */, - C0687010238DE85D00DAFD3D /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "Introspect iOS"; - productName = Introspect; - productReference = C0687012238DE85D00DAFD3D /* Introspect.framework */; - productType = "com.apple.product-type.framework"; - }; - C068701A238DE85D00DAFD3D /* Introspect iOS Tests */ = { - isa = PBXNativeTarget; - buildConfigurationList = C0687029238DE85D00DAFD3D /* Build configuration list for PBXNativeTarget "Introspect iOS Tests" */; - buildPhases = ( - C0687017238DE85D00DAFD3D /* Sources */, - C0687018238DE85D00DAFD3D /* Frameworks */, - C0687019238DE85D00DAFD3D /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - C068701E238DE85D00DAFD3D /* PBXTargetDependency */, - ); - name = "Introspect iOS Tests"; - productName = IntrospectTests; - productReference = C068701B238DE85D00DAFD3D /* Introspect iOS Tests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - C0796E1E23F3CCD2002BF033 /* Introspect tvOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = C0796E2423F3CCD2002BF033 /* Build configuration list for PBXNativeTarget "Introspect tvOS" */; - buildPhases = ( - C0796E1A23F3CCD2002BF033 /* Headers */, - C0796E1B23F3CCD2002BF033 /* Sources */, - C0796E1C23F3CCD2002BF033 /* Frameworks */, - C0796E1D23F3CCD2002BF033 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "Introspect tvOS"; - productName = "Introspect tvOS"; - productReference = C0796E1F23F3CCD2002BF033 /* Introspect.framework */; - productType = "com.apple.product-type.framework"; - }; - C0796E2E23F3CDA4002BF033 /* Introspect tvOS Tests */ = { - isa = PBXNativeTarget; - buildConfigurationList = C0796E3723F3CDA4002BF033 /* Build configuration list for PBXNativeTarget "Introspect tvOS Tests" */; - buildPhases = ( - C0796E2B23F3CDA4002BF033 /* Sources */, - C0796E2C23F3CDA4002BF033 /* Frameworks */, - C0796E2D23F3CDA4002BF033 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - C0796E3623F3CDA4002BF033 /* PBXTargetDependency */, - ); - name = "Introspect tvOS Tests"; - productName = "Introspect tvOS Tests"; - productReference = C0796E2F23F3CDA4002BF033 /* Introspect tvOS Tests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - C0C6D68A238E006B00DA6285 /* IntrospectExamples */ = { - isa = PBXNativeTarget; - buildConfigurationList = C0C6D69E238E007100DA6285 /* Build configuration list for PBXNativeTarget "IntrospectExamples" */; - buildPhases = ( - 9C6427CDD5EF4F1EAC75C7F9 /* [CP] Check Pods Manifest.lock */, - C0C6D687238E006B00DA6285 /* Sources */, - C0C6D688238E006B00DA6285 /* Frameworks */, - C0C6D689238E006B00DA6285 /* Resources */, - EC4E9FC6E0E62CFD8A53623A /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = IntrospectExamples; - productName = IntrospectExamples; - productReference = C0C6D68B238E006B00DA6285 /* IntrospectExamples.app */; - productType = "com.apple.product-type.application"; - }; - C0ED340823F39E93005FA859 /* Introspect macOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = C0ED340E23F39E93005FA859 /* Build configuration list for PBXNativeTarget "Introspect macOS" */; - buildPhases = ( - C0ED340423F39E93005FA859 /* Headers */, - C0ED340523F39E93005FA859 /* Sources */, - C0ED340623F39E93005FA859 /* Frameworks */, - C0ED340723F39E93005FA859 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "Introspect macOS"; - productName = "Introspect macOS"; - productReference = C0ED340923F39E93005FA859 /* Introspect.framework */; - productType = "com.apple.product-type.framework"; - }; - C0ED343423F3AC43005FA859 /* Introspect macOS Tests */ = { - isa = PBXNativeTarget; - buildConfigurationList = C0ED343D23F3AC43005FA859 /* Build configuration list for PBXNativeTarget "Introspect macOS Tests" */; - buildPhases = ( - C0ED343123F3AC43005FA859 /* Sources */, - C0ED343223F3AC43005FA859 /* Frameworks */, - C0ED343323F3AC43005FA859 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - C0ED343C23F3AC43005FA859 /* PBXTargetDependency */, - ); - name = "Introspect macOS Tests"; - productName = "Introspect macOS Tests"; - productReference = C0ED343523F3AC43005FA859 /* Introspect macOS Tests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - C0687009238DE85D00DAFD3D /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 1130; - LastUpgradeCheck = 1120; - ORGANIZATIONNAME = "Lois Di Qual"; - TargetAttributes = { - C0687011238DE85D00DAFD3D = { - CreatedOnToolsVersion = 11.2.1; - LastSwiftMigration = 1120; - }; - C068701A238DE85D00DAFD3D = { - CreatedOnToolsVersion = 11.2.1; - }; - C0796E1E23F3CCD2002BF033 = { - CreatedOnToolsVersion = 11.3.1; - }; - C0796E2E23F3CDA4002BF033 = { - CreatedOnToolsVersion = 11.3.1; - }; - C0C6D68A238E006B00DA6285 = { - CreatedOnToolsVersion = 11.2.1; - }; - C0ED340823F39E93005FA859 = { - CreatedOnToolsVersion = 11.3.1; - }; - C0ED343423F3AC43005FA859 = { - CreatedOnToolsVersion = 11.3.1; - }; - }; - }; - buildConfigurationList = C068700C238DE85D00DAFD3D /* Build configuration list for PBXProject "Introspect" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = C0687008238DE85D00DAFD3D; - productRefGroup = C0687013238DE85D00DAFD3D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - C0687011238DE85D00DAFD3D /* Introspect iOS */, - C068701A238DE85D00DAFD3D /* Introspect iOS Tests */, - C0ED340823F39E93005FA859 /* Introspect macOS */, - C0ED343423F3AC43005FA859 /* Introspect macOS Tests */, - C0796E1E23F3CCD2002BF033 /* Introspect tvOS */, - C0796E2E23F3CDA4002BF033 /* Introspect tvOS Tests */, - C0C6D68A238E006B00DA6285 /* IntrospectExamples */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - C0687010238DE85D00DAFD3D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0687019238DE85D00DAFD3D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0796E1D23F3CCD2002BF033 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0796E2D23F3CDA4002BF033 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0C6D689238E006B00DA6285 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0ED340723F39E93005FA859 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0ED343323F3AC43005FA859 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 9C6427CDD5EF4F1EAC75C7F9 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-IntrospectExamples-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - EC4E9FC6E0E62CFD8A53623A /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-IntrospectExamples/Pods-IntrospectExamples-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-IntrospectExamples/Pods-IntrospectExamples-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-IntrospectExamples/Pods-IntrospectExamples-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - C068700E238DE85D00DAFD3D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C0ED341723F3A149005FA859 /* UIKitIntrospectionViewController.swift in Sources */, - C0ED345F23F48671005FA859 /* AppKitIntrospectionView.swift in Sources */, - C0ED341523F3A13C005FA859 /* UIKitIntrospectionView.swift in Sources */, - C0ED345E23F48535005FA859 /* ViewExtensions.swift in Sources */, - C068702D238DE8FD00DAFD3D /* Introspect.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0687017238DE85D00DAFD3D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C0ED346823F489D5005FA859 /* UIKitTests.swift in Sources */, - C0ED346723F489D5005FA859 /* AppKitTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0796E1B23F3CCD2002BF033 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C0796E2823F3CD1D002BF033 /* UIKitIntrospectionView.swift in Sources */, - C0ED346023F48672005FA859 /* AppKitIntrospectionView.swift in Sources */, - C0796E2923F3CD1D002BF033 /* UIKitIntrospectionViewController.swift in Sources */, - C0ED345D23F48534005FA859 /* ViewExtensions.swift in Sources */, - C0796E2723F3CD18002BF033 /* Introspect.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0796E2B23F3CDA4002BF033 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C0ED346A23F489D6005FA859 /* UIKitTests.swift in Sources */, - C0ED346923F489D6005FA859 /* AppKitTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0C6D687238E006B00DA6285 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C0C6D68E238E006B00DA6285 /* AppDelegate.swift in Sources */, - C0C6D690238E006B00DA6285 /* SceneDelegate.swift in Sources */, - C0C6D692238E006B00DA6285 /* ContentView.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0ED340523F39E93005FA859 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C0ED341B23F3A258005FA859 /* AppKitIntrospectionView.swift in Sources */, - C0ED341D23F3A58B005FA859 /* ViewExtensions.swift in Sources */, - C0ED346123F486D0005FA859 /* UIKitIntrospectionView.swift in Sources */, - C0ED341123F39EA2005FA859 /* Introspect.swift in Sources */, - C0ED346223F48770005FA859 /* UIKitIntrospectionViewController.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C0ED343123F3AC43005FA859 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C0ED346623F48992005FA859 /* UIKitTests.swift in Sources */, - C0ED344023F3AC7F005FA859 /* AppKitTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - C068701E238DE85D00DAFD3D /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = C0687011238DE85D00DAFD3D /* Introspect iOS */; - targetProxy = C068701D238DE85D00DAFD3D /* PBXContainerItemProxy */; - }; - C0796E3623F3CDA4002BF033 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = C0796E1E23F3CCD2002BF033 /* Introspect tvOS */; - targetProxy = C0796E3523F3CDA4002BF033 /* PBXContainerItemProxy */; - }; - C0ED343C23F3AC43005FA859 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = C0ED340823F39E93005FA859 /* Introspect macOS */; - targetProxy = C0ED343B23F3AC43005FA859 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - C0687024238DE85D00DAFD3D /* 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++14"; - CLANG_CXX_LIBRARY = "libc++"; - 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_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; - 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; - IPHONEOS_DEPLOYMENT_TARGET = 13.2; - MACOSX_DEPLOYMENT_TARGET = 10.15; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - TVOS_DEPLOYMENT_TARGET = 13.0; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - C0687025238DE85D00DAFD3D /* 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++14"; - CLANG_CXX_LIBRARY = "libc++"; - 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_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; - 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; - IPHONEOS_DEPLOYMENT_TARGET = 13.2; - MACOSX_DEPLOYMENT_TARGET = 10.15; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - TVOS_DEPLOYMENT_TARGET = 13.0; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - C0687027238DE85D00DAFD3D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Manual; - DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = Introspect/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 0.1.3; - PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Introspect; - PRODUCT_NAME = Introspect; - PROVISIONING_PROFILE_SPECIFIER = ""; - SKIP_INSTALL = YES; - SUPPORTS_MACCATALYST = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - C0687028238DE85D00DAFD3D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Manual; - DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = Introspect/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 0.1.3; - PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Introspect; - PRODUCT_NAME = Introspect; - PROVISIONING_PROFILE_SPECIFIER = ""; - SKIP_INSTALL = YES; - SUPPORTS_MACCATALYST = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; - C068702A238DE85D00DAFD3D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = IntrospectTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.loisdiqual.IntrospectTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - C068702B238DE85D00DAFD3D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = IntrospectTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.loisdiqual.IntrospectTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; - C0796E2523F3CCD2002BF033 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = Introspect/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 0.1.3; - PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Introspect; - PRODUCT_NAME = Introspect; - SDKROOT = appletvos; - SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 13.2; - }; - name = Debug; - }; - C0796E2623F3CCD2002BF033 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = Introspect/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 0.1.3; - PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Introspect; - PRODUCT_NAME = Introspect; - SDKROOT = appletvos; - SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 13.2; - }; - name = Release; - }; - C0796E3823F3CDA4002BF033 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = IntrospectTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.siteline.Introspect-tvOS-Tests"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = appletvos; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 13.2; - }; - name = Debug; - }; - C0796E3923F3CDA4002BF033 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = IntrospectTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.siteline.Introspect-tvOS-Tests"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = appletvos; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 13.2; - }; - name = Release; - }; - C0C6D69C238E007100DA6285 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9BBE78DB32CCDC560004DB54 /* Pods-IntrospectExamples.debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_STYLE = Automatic; - ENABLE_PREVIEWS = YES; - INFOPLIST_FILE = IntrospectExamples/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 0.0.6; - PRODUCT_BUNDLE_IDENTIFIER = com.siteline.IntrospectExamples; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - C0C6D69D238E007100DA6285 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = B826284199E111BBEA21E76B /* Pods-IntrospectExamples.release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_STYLE = Automatic; - ENABLE_PREVIEWS = YES; - INFOPLIST_FILE = IntrospectExamples/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 0.0.6; - PRODUCT_BUNDLE_IDENTIFIER = com.siteline.IntrospectExamples; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; - C0ED340F23F39E93005FA859 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - COMBINE_HIDPI_IMAGES = YES; - DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = Introspect/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 0.1.3; - PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Introspect; - PRODUCT_NAME = Introspect; - PROVISIONING_PROFILE_SPECIFIER = ""; - SDKROOT = macosx; - SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - C0ED341023F39E93005FA859 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - COMBINE_HIDPI_IMAGES = YES; - DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = Introspect/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 0.1.3; - PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Introspect; - PRODUCT_NAME = Introspect; - PROVISIONING_PROFILE_SPECIFIER = ""; - SDKROOT = macosx; - SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - C0ED343E23F3AC43005FA859 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = IntrospectTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 10.15; - PRODUCT_BUNDLE_IDENTIFIER = "com.siteline.Introspect-macOS-Tests"; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SDKROOT = macosx; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - C0ED343F23F3AC43005FA859 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = IntrospectTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 10.15; - PRODUCT_BUNDLE_IDENTIFIER = "com.siteline.Introspect-macOS-Tests"; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SDKROOT = macosx; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - C068700C238DE85D00DAFD3D /* Build configuration list for PBXProject "Introspect" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C0687024238DE85D00DAFD3D /* Debug */, - C0687025238DE85D00DAFD3D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - C0687026238DE85D00DAFD3D /* Build configuration list for PBXNativeTarget "Introspect iOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C0687027238DE85D00DAFD3D /* Debug */, - C0687028238DE85D00DAFD3D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - C0687029238DE85D00DAFD3D /* Build configuration list for PBXNativeTarget "Introspect iOS Tests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C068702A238DE85D00DAFD3D /* Debug */, - C068702B238DE85D00DAFD3D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - C0796E2423F3CCD2002BF033 /* Build configuration list for PBXNativeTarget "Introspect tvOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C0796E2523F3CCD2002BF033 /* Debug */, - C0796E2623F3CCD2002BF033 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - C0796E3723F3CDA4002BF033 /* Build configuration list for PBXNativeTarget "Introspect tvOS Tests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C0796E3823F3CDA4002BF033 /* Debug */, - C0796E3923F3CDA4002BF033 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - C0C6D69E238E007100DA6285 /* Build configuration list for PBXNativeTarget "IntrospectExamples" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C0C6D69C238E007100DA6285 /* Debug */, - C0C6D69D238E007100DA6285 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - C0ED340E23F39E93005FA859 /* Build configuration list for PBXNativeTarget "Introspect macOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C0ED340F23F39E93005FA859 /* Debug */, - C0ED341023F39E93005FA859 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - C0ED343D23F3AC43005FA859 /* Build configuration list for PBXNativeTarget "Introspect macOS Tests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C0ED343E23F3AC43005FA859 /* Debug */, - C0ED343F23F3AC43005FA859 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = C0687009238DE85D00DAFD3D /* Project object */; -} diff --git a/Introspect.xcodeproj/xcuserdata/ldiqual.xcuserdatad/xcschemes/xcschememanagement.plist b/Introspect.xcodeproj/xcuserdata/ldiqual.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 1651105eb..000000000 --- a/Introspect.xcodeproj/xcuserdata/ldiqual.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,62 +0,0 @@ - - - - - SchemeUserState - - Introspect iOS.xcscheme_^#shared#^_ - - orderHint - 0 - - Introspect macOS.xcscheme_^#shared#^_ - - orderHint - 1 - - Introspect tvOS.xcscheme_^#shared#^_ - - orderHint - 2 - - IntrospectExamples.xcscheme_^#shared#^_ - - orderHint - 3 - - - SuppressBuildableAutocreation - - C0687011238DE85D00DAFD3D - - primary - - - C068701A238DE85D00DAFD3D - - primary - - - C0796E1E23F3CCD2002BF033 - - primary - - - C0796E2E23F3CDA4002BF033 - - primary - - - C0ED340823F39E93005FA859 - - primary - - - C0ED343423F3AC43005FA859 - - primary - - - - - diff --git a/Introspect.xcworkspace/contents.xcworkspacedata b/Introspect.xcworkspace/contents.xcworkspacedata index 14ab32111..9c257254e 100644 --- a/Introspect.xcworkspace/contents.xcworkspacedata +++ b/Introspect.xcworkspace/contents.xcworkspacedata @@ -2,9 +2,9 @@ + location = "group:Examples/Showcase/Showcase.xcodeproj"> + location = "group:"> diff --git a/Introspect.xcodeproj/xcshareddata/xcschemes/Introspect tvOS.xcscheme b/Introspect.xcworkspace/xcshareddata/xcschemes/Introspect-Dynamic.xcscheme similarity index 66% rename from Introspect.xcodeproj/xcshareddata/xcschemes/Introspect tvOS.xcscheme rename to Introspect.xcworkspace/xcshareddata/xcschemes/Introspect-Dynamic.xcscheme index baa2bfc44..80c68beb9 100644 --- a/Introspect.xcodeproj/xcshareddata/xcschemes/Introspect tvOS.xcscheme +++ b/Introspect.xcworkspace/xcshareddata/xcschemes/Introspect-Dynamic.xcscheme @@ -1,6 +1,6 @@ + BlueprintIdentifier = "Introspect-Dynamic" + BuildableName = "Introspect-Dynamic" + BlueprintName = "Introspect-Dynamic" + ReferencedContainer = "container:"> @@ -28,16 +28,6 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> - - - - + BlueprintIdentifier = "Introspect-Dynamic" + BuildableName = "Introspect-Dynamic" + BlueprintName = "Introspect-Dynamic" + ReferencedContainer = "container:"> diff --git a/Introspect.xcodeproj/xcshareddata/xcschemes/Introspect macOS.xcscheme b/Introspect.xcworkspace/xcshareddata/xcschemes/Introspect-Static.xcscheme similarity index 66% rename from Introspect.xcodeproj/xcshareddata/xcschemes/Introspect macOS.xcscheme rename to Introspect.xcworkspace/xcshareddata/xcschemes/Introspect-Static.xcscheme index 8a729eff0..aebc9608b 100644 --- a/Introspect.xcodeproj/xcshareddata/xcschemes/Introspect macOS.xcscheme +++ b/Introspect.xcworkspace/xcshareddata/xcschemes/Introspect-Static.xcscheme @@ -1,6 +1,6 @@ + BlueprintIdentifier = "Introspect-Static" + BuildableName = "Introspect-Static" + BlueprintName = "Introspect-Static" + ReferencedContainer = "container:"> @@ -28,16 +28,6 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> - - - - + BlueprintIdentifier = "Introspect-Static" + BuildableName = "Introspect-Static" + BlueprintName = "Introspect-Static" + ReferencedContainer = "container:"> diff --git a/Introspect.xcodeproj/xcshareddata/xcschemes/Introspect iOS.xcscheme b/Introspect.xcworkspace/xcshareddata/xcschemes/Introspect.xcscheme similarity index 73% rename from Introspect.xcodeproj/xcshareddata/xcschemes/Introspect iOS.xcscheme rename to Introspect.xcworkspace/xcshareddata/xcschemes/Introspect.xcscheme index 15086f80c..11e22f5d3 100644 --- a/Introspect.xcodeproj/xcshareddata/xcschemes/Introspect iOS.xcscheme +++ b/Introspect.xcworkspace/xcshareddata/xcschemes/Introspect.xcscheme @@ -1,6 +1,6 @@ + BlueprintIdentifier = "Introspect" + BuildableName = "Introspect" + BlueprintName = "Introspect" + ReferencedContainer = "container:"> @@ -32,10 +32,10 @@ skipped = "NO"> + BlueprintIdentifier = "IntrospectTests" + BuildableName = "IntrospectTests" + BlueprintName = "IntrospectTests" + ReferencedContainer = "container:"> @@ -60,10 +60,10 @@ + BlueprintIdentifier = "Introspect" + BuildableName = "Introspect" + BlueprintName = "Introspect" + ReferencedContainer = "container:"> diff --git a/Introspect/AppKitIntrospectionView.swift b/Introspect/AppKitIntrospectionView.swift index d2ace3d23..ae375ff07 100644 --- a/Introspect/AppKitIntrospectionView.swift +++ b/Introspect/AppKitIntrospectionView.swift @@ -3,7 +3,6 @@ import SwiftUI import AppKit /// Introspection NSView that is inserted alongside the target view. -@available(macOS 10.15.0, *) public class IntrospectionNSView: NSView { required init() { @@ -23,7 +22,6 @@ public class IntrospectionNSView: NSView { /// Introspection View that is injected into the UIKit hierarchy alongside the target view. /// After `updateNSView` is called, it calls `selector` to find the target view, then `customize` when the target view is found. -@available(macOS 10.15.0, *) public struct AppKitIntrospectionView: NSViewRepresentable { /// Method that introspects the view hierarchy to find the target view. diff --git a/Introspect/Info.plist b/Introspect/Info.plist deleted file mode 100644 index c0701c6d7..000000000 --- a/Introspect/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - $(MARKETING_VERSION) - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - - diff --git a/Introspect/Introspect.h b/Introspect/Introspect.h deleted file mode 100644 index 7c52b636a..000000000 --- a/Introspect/Introspect.h +++ /dev/null @@ -1,7 +0,0 @@ -#import - -//! Project version number for Introspect. -FOUNDATION_EXPORT double IntrospectVersionNumber; - -//! Project version string for Introspect. -FOUNDATION_EXPORT const unsigned char IntrospectVersionString[]; diff --git a/Introspect/UIKitIntrospectionView.swift b/Introspect/UIKitIntrospectionView.swift index a14eb7892..d999a7ef2 100644 --- a/Introspect/UIKitIntrospectionView.swift +++ b/Introspect/UIKitIntrospectionView.swift @@ -3,7 +3,6 @@ import UIKit import SwiftUI /// Introspection UIView that is inserted alongside the target view. -@available(iOS 13.0, *) public class IntrospectionUIView: UIView { var moveToWindowHandler: (() -> Void)? @@ -27,7 +26,6 @@ public class IntrospectionUIView: UIView { /// Introspection View that is injected into the UIKit hierarchy alongside the target view. /// After `updateUIView` is called, it calls `selector` to find the target view, then `customize` when the target view is found. -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) public struct UIKitIntrospectionView: UIViewRepresentable { /// Method that introspects the view hierarchy to find the target view. diff --git a/Introspect/UIKitIntrospectionViewController.swift b/Introspect/UIKitIntrospectionViewController.swift index fd8e437ec..daeafd108 100644 --- a/Introspect/UIKitIntrospectionViewController.swift +++ b/Introspect/UIKitIntrospectionViewController.swift @@ -3,7 +3,6 @@ import SwiftUI import UIKit /// Introspection UIViewController that is inserted alongside the target view controller. -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) public class IntrospectionUIViewController: UIViewController { required init() { super.init(nibName: nil, bundle: nil) @@ -17,7 +16,6 @@ public class IntrospectionUIViewController: UIViewController { } /// This is the same logic as IntrospectionView but for view controllers. Please see details above. -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) public struct UIKitIntrospectionViewController: UIViewControllerRepresentable { let selector: (IntrospectionUIViewController) -> TargetViewControllerType? diff --git a/Introspect/ViewExtensions.swift b/Introspect/ViewExtensions.swift index d4abd396a..18d27f0e1 100644 --- a/Introspect/ViewExtensions.swift +++ b/Introspect/ViewExtensions.swift @@ -6,7 +6,6 @@ import AppKit import UIKit #endif -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) extension View { public func inject(_ view: SomeView) -> some View where SomeView: View { overlay(view.frame(width: 0, height: 0)) @@ -14,7 +13,6 @@ extension View { } #if canImport(UIKit) -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) extension View { /// Finds a `TargetView` from a `SwiftUI.View` @@ -119,7 +117,7 @@ extension View { /// Finds the horizontal `UIScrollView` from a `SwiftUI.TabBarView` with tab style `SwiftUI.PageTabViewStyle`. /// /// Customize is called with a `UICollectionView` wrapper, and the horizontal `UIScrollView`. - @available(iOS 14.0, tvOS 14.0, watchOS 7.0, *) + @available(iOS 14.0, tvOS 14.0, *) @available(macOS, unavailable) public func introspectPagedTabView(customize: @escaping (UICollectionView, UIScrollView) -> ()) -> some View { return introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, customize: { (collectionView: UICollectionView) in @@ -172,7 +170,7 @@ extension View { } /// Finds a `UIColorWell` from a `SwiftUI.ColorPicker` - @available(iOS 14.0, *) + @available(iOS 14.0, macCatalyst 14.0, *) @available(tvOS, unavailable) public func introspectColorWell(customize: @escaping (UIColorWell) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) @@ -181,7 +179,6 @@ extension View { #endif #if canImport(AppKit) && !targetEnvironment(macCatalyst) -@available(macOS 10.15.0, *) extension View { /// Finds a `TargetView` from a `SwiftUI.View` diff --git a/IntrospectExamples/AppDelegate.swift b/IntrospectExamples/AppDelegate.swift deleted file mode 100644 index 9bafec633..000000000 --- a/IntrospectExamples/AppDelegate.swift +++ /dev/null @@ -1,9 +0,0 @@ -import UIKit - -@UIApplicationMain -class AppDelegate: UIResponder, UIApplicationDelegate { - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - return true - } -} - diff --git a/IntrospectExamples/Info.plist b/IntrospectExamples/Info.plist deleted file mode 100644 index 49e2bd08f..000000000 --- a/IntrospectExamples/Info.plist +++ /dev/null @@ -1,60 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 0.1.3 - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UIApplicationSceneManifest - - UIApplicationSupportsMultipleScenes - - UISceneConfigurations - - UIWindowSceneSessionRoleApplication - - - UISceneConfigurationName - Default Configuration - UISceneDelegateClassName - $(PRODUCT_MODULE_NAME).SceneDelegate - - - - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/IntrospectExamples/SceneDelegate.swift b/IntrospectExamples/SceneDelegate.swift deleted file mode 100644 index dad927ff9..000000000 --- a/IntrospectExamples/SceneDelegate.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// SceneDelegate.swift -// IntrospectExamples -// -// Created by Lois Di Qual on 11/26/19. -// Copyright © 2019 Lois Di Qual. All rights reserved. -// - -import UIKit -import SwiftUI - -class SceneDelegate: UIResponder, UIWindowSceneDelegate { - - var window: UIWindow? - - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - let contentView = ContentView() - if let windowScene = scene as? UIWindowScene { - let window = UIWindow(windowScene: windowScene) - window.rootViewController = UIHostingController(rootView: contentView) - self.window = window - window.makeKeyAndVisible() - } - } -} - diff --git a/IntrospectTests/AppKitTests.swift b/IntrospectTests/AppKitTests.swift index 687b697c7..f7996b876 100644 --- a/IntrospectTests/AppKitTests.swift +++ b/IntrospectTests/AppKitTests.swift @@ -4,7 +4,6 @@ import XCTest import SwiftUI @testable import Introspect -@available(macOS 10.15.0, *) enum TestUtils { enum Constants { static let timeout: TimeInterval = 5 @@ -24,7 +23,6 @@ enum TestUtils { } } -@available(macOS 10.15.0, *) private struct ListTestView: View { let spy1: () -> Void @@ -53,7 +51,6 @@ private struct ListTestView: View { } } -@available(macOS 10.15.0, *) private struct ScrollTestView: View { let spy1: (NSScrollView) -> Void @@ -78,7 +75,6 @@ private struct ScrollTestView: View { } } -@available(macOS 10.15.0, *) private struct NestedScrollTestView: View { let spy1: (NSScrollView) -> Void @@ -103,7 +99,6 @@ private struct NestedScrollTestView: View { } } -@available(macOS 10.15.0, *) private struct TextFieldTestView: View { let spy: () -> Void @State private var textFieldValue = "" @@ -127,7 +122,6 @@ private struct TextEditorTestView: View { } } -@available(macOS 10.15.0, *) private struct SliderTestView: View { let spy: () -> Void @State private var sliderValue = 0.0 @@ -139,7 +133,6 @@ private struct SliderTestView: View { } } -@available(macOS 10.15.0, *) private struct StepperTestView: View { let spy: () -> Void var body: some View { @@ -152,7 +145,6 @@ private struct StepperTestView: View { } } -@available(macOS 10.15.0, *) private struct DatePickerTestView: View { let spy: () -> Void @State private var datePickerValue = Date() @@ -166,7 +158,6 @@ private struct DatePickerTestView: View { } } -@available(macOS 10.15.0, *) private struct SegmentedControlTestView: View { @State private var pickerValue = 0 let spy: () -> Void @@ -183,7 +174,6 @@ private struct SegmentedControlTestView: View { } } -@available(macOS 10.15.0, *) private struct TabViewTestView: View { let spy: () -> Void var body: some View { @@ -203,7 +193,6 @@ private struct TabViewTestView: View { } } -@available(macOS 10.15.0, *) private struct ButtonTestView: View { let spy: () -> Void var body: some View { @@ -214,7 +203,6 @@ private struct ButtonTestView: View { } } -@available(macOS 10.15.0, *) private struct ToggleTestView: View { let spy: () -> Void @State private var toggleValue = false @@ -226,7 +214,7 @@ private struct ToggleTestView: View { } } -@available(macOS 11.0, *) +@available(macOS 11, *) private struct ColorWellTestView: View { @State private var color = Color.black let spy: () -> Void @@ -239,10 +227,13 @@ private struct ColorWellTestView: View { } } -@available(macOS 10.15.0, *) class AppKitTests: XCTestCase { func testList() { + if #available(macOS 11, *) { + return // TODO: verify whether List still uses NSTableView under the hood in macOS >=11 + } + let expectation1 = XCTestExpectation() let expectation2 = XCTestExpectation() let cellExpectation1 = XCTestExpectation() @@ -320,17 +311,14 @@ class AppKitTests: XCTestCase { TestUtils.present(view: view) wait(for: [expectation], timeout: TestUtils.Constants.timeout) } - - func testTextEditor() { - if #available(macOS 11.0, *) { - let expectation = XCTestExpectation() - let view = TextEditorTestView(spy: { - expectation.fulfill() - }) - TestUtils.present(view: view) - wait(for: [expectation], timeout: TestUtils.Constants.timeout) - } + func testTextEditor() { + let expectation = XCTestExpectation() + let view = TextEditorTestView(spy: { + expectation.fulfill() + }) + TestUtils.present(view: view) + wait(for: [expectation], timeout: TestUtils.Constants.timeout) } func testSlider() { @@ -382,9 +370,12 @@ class AppKitTests: XCTestCase { TestUtils.present(view: view) wait(for: [expectation], timeout: TestUtils.Constants.timeout) } - + func testButton() { - + + if #available(macOS 12, *) { + return // TODO: verify whether Button still uses NSButton under the hood in macOS >=12 + } let expectation = XCTestExpectation() let view = ButtonTestView(spy: { expectation.fulfill() @@ -405,14 +396,12 @@ class AppKitTests: XCTestCase { func testColorPicker() { - if #available(macOS 11.0, *) { - let expectation = XCTestExpectation() - let view = ColorWellTestView(spy: { - expectation.fulfill() - }) - TestUtils.present(view: view) - wait(for: [expectation], timeout: TestUtils.Constants.timeout) - } + let expectation = XCTestExpectation() + let view = ColorWellTestView(spy: { + expectation.fulfill() + }) + TestUtils.present(view: view) + wait(for: [expectation], timeout: TestUtils.Constants.timeout) } } #endif diff --git a/IntrospectTests/Info.plist b/IntrospectTests/Info.plist deleted file mode 100644 index 5e0761dbe..000000000 --- a/IntrospectTests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 0.1.3 - CFBundleVersion - 1 - - diff --git a/IntrospectTests/UIKitTests.swift b/IntrospectTests/UIKitTests.swift index ff59be312..22fa2afa8 100644 --- a/IntrospectTests/UIKitTests.swift +++ b/IntrospectTests/UIKitTests.swift @@ -4,7 +4,6 @@ import SwiftUI @testable import Introspect -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) enum TestUtils { enum Constants { static let timeout: TimeInterval = 3 @@ -33,7 +32,6 @@ enum TestUtils { } } -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) private struct NavigationTestView: View { let spy: () -> Void var body: some View { @@ -49,7 +47,6 @@ private struct NavigationTestView: View { } } -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) private struct SplitNavigationTestView: View { let spy: () -> Void var body: some View { @@ -65,7 +62,6 @@ private struct SplitNavigationTestView: View { } } -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) private struct ViewControllerTestView: View { let spy: () -> Void var body: some View { @@ -80,7 +76,6 @@ private struct ViewControllerTestView: View { } } -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) private struct NavigationRootTestView: View { let spy: () -> Void var body: some View { @@ -95,7 +90,6 @@ private struct NavigationRootTestView: View { } } -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) private struct TabTestView: View { @State private var selection = 0 let spy: () -> Void @@ -110,7 +104,6 @@ private struct TabTestView: View { } } -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) private struct TabRootTestView: View { @State private var selection = 0 let spy: () -> Void @@ -125,25 +118,34 @@ private struct TabRootTestView: View { } } -@available(iOS 14.0, tvOS 14.0, watchOS 7.0, *) -@available(macOS, unavailable) +@available(iOS 14.0, tvOS 14.0, *) private struct PageTabViewStyleTestView: View { let spy: (UICollectionView, UIScrollView) -> Void var body: some View { - TabView { - Text("Item 1") - .tag(0) - } - .tabViewStyle(PageTabViewStyle()) - .introspectPagedTabView { collectionView, scrollView in - spy(collectionView, scrollView) + if #available(iOS 16, tvOS 16, *) { + TabView { + Text("Item 1") + .tag(0) + } + .tabViewStyle(PageTabViewStyle()) + .introspectCollectionView { collectionView in + spy(collectionView, collectionView) + } + } else { + TabView { + Text("Item 1") + .tag(0) + } + .tabViewStyle(PageTabViewStyle()) + .introspectPagedTabView { collectionView, scrollView in + spy(collectionView, scrollView) + } } } } -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) private struct ListTestView: View { let spy1: () -> Void @@ -152,7 +154,7 @@ private struct ListTestView: View { let spyCell2: () -> Void var body: some View { - if #available(iOS 16, tvOS 16, macOS 13, *) { + if #available(iOS 16, tvOS 16, *) { List { Text("Item 1") Text("Item 2") @@ -192,7 +194,6 @@ private struct ListTestView: View { } } -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) private struct ScrollTestView: View { let spy1: (UIScrollView) -> Void @@ -216,7 +217,6 @@ private struct ScrollTestView: View { } } -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) private struct NestedScrollTestView: View { let spy1: (UIScrollView) -> Void @@ -241,7 +241,6 @@ private struct NestedScrollTestView: View { } } -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) private struct TextFieldTestView: View { let spy1: (UITextField) -> Void let spy2: (UITextField) -> Void @@ -272,7 +271,7 @@ private struct TextFieldTestView: View { } } -@available(iOS 14.0, macCatalyst 14.0, macOS 11.0, tvOS 13.0, *) +@available(iOS 14.0, macCatalyst 14.0, *) @available(tvOS, unavailable, message: "TextEditor is not available in tvOS.") private struct TextEditorTestView: View { let spy: () -> Void @@ -285,7 +284,6 @@ private struct TextEditorTestView: View { } } -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) @available(tvOS, unavailable) private struct ToggleTestView: View { let spy: () -> Void @@ -298,7 +296,6 @@ private struct ToggleTestView: View { } } -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) @available(tvOS, unavailable) private struct SliderTestView: View { let spy: () -> Void @@ -311,7 +308,6 @@ private struct SliderTestView: View { } } -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) @available(tvOS, unavailable) private struct StepperTestView: View { let spy: () -> Void @@ -325,7 +321,6 @@ private struct StepperTestView: View { } } -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) @available(tvOS, unavailable) private struct DatePickerTestView: View { let spy: () -> Void @@ -340,7 +335,6 @@ private struct DatePickerTestView: View { } } -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) private struct SegmentedControlTestView: View { @State private var pickerValue = 0 let spy: () -> Void @@ -357,12 +351,12 @@ private struct SegmentedControlTestView: View { } } -@available(iOS 14.0, tvOS 13.0, macOS 11.0, *) +@available(iOS 14.0, macCatalyst 14.0, *) @available(tvOS, unavailable) private struct ColorWellTestView: View { @State private var color = Color.black let spy: () -> Void - + var body: some View { ColorPicker("Picker", selection: $color) .introspectColorWell { colorWell in @@ -371,7 +365,6 @@ private struct ColorWellTestView: View { } } -@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *) class UIKitTests: XCTestCase { func testNavigation() { @@ -414,7 +407,10 @@ class UIKitTests: XCTestCase { } func testList() { - + if #available(tvOS 16, *) { + return // TODO: verify whether List still uses NSTableView under the hood in tvOS 16 + } + let expectation1 = XCTestExpectation() let expectation2 = XCTestExpectation() let cellExpectation1 = XCTestExpectation() @@ -591,7 +587,7 @@ class UIKitTests: XCTestCase { wait(for: [expectation], timeout: TestUtils.Constants.timeout) } - @available(iOS 14.0, macCatalyst 14.0, macOS 11.0, *) + @available(iOS 14.0, macCatalyst 14.0, *) @available(tvOS, unavailable, message: "TextEditor is not available in tvOS.") func testTextEditor() { @@ -603,7 +599,7 @@ class UIKitTests: XCTestCase { wait(for: [expectation], timeout: TestUtils.Constants.timeout) } - @available(iOS 14.0, macCatalyst 14.0, macOS 11.0, *) + @available(iOS 14.0, macCatalyst 14.0, *) @available(tvOS, unavailable, message: "ColorPicker is not available in tvOS.") func testColorPicker() { @@ -615,7 +611,7 @@ class UIKitTests: XCTestCase { wait(for: [expectation], timeout: TestUtils.Constants.timeout) } - @available(iOS 14.0, tvOS 14.0, watchOS 7.0, *) + @available(iOS 14.0, tvOS 14.0, *) func testPagedTabView() throws { var collectionView1: UICollectionView? @@ -633,7 +629,11 @@ class UIKitTests: XCTestCase { let unwrappedCollectionView = try XCTUnwrap(collectionView1) let unwrappedScrollView = try XCTUnwrap(scrollView1) - XCTAssertTrue(unwrappedCollectionView.subviews.contains(where: { $0 === unwrappedScrollView })) + if #available(iOS 16, tvOS 16, *) { + XCTAssertTrue(unwrappedCollectionView == unwrappedScrollView) + } else { + XCTAssertTrue(unwrappedCollectionView.subviews.contains(where: { $0 === unwrappedScrollView })) + } } #endif } diff --git a/Package.swift b/Package.swift index d39f203b5..5d715970d 100644 --- a/Package.swift +++ b/Package.swift @@ -1,30 +1,28 @@ -// swift-tools-version:5.1 +// swift-tools-version:5.4 import PackageDescription let package = Package( name: "Introspect", platforms: [ - .macOS(.v10_13), - .iOS(.v11), - .tvOS(.v11) + .iOS(.v13), + .macOS(.v10_15), + .tvOS(.v13), ], products: [ .library(name: "Introspect", targets: ["Introspect"]), .library(name: "Introspect-Static", type: .static, targets: ["Introspect"]), .library(name: "Introspect-Dynamic", type: .dynamic, targets: ["Introspect"]), ], - dependencies: [], targets: [ .target( name: "Introspect", - dependencies: [], - path: "Introspect" + path: "Introspect" // TODO: rename to Sources for v1.0 ), .testTarget( name: "IntrospectTests", dependencies: ["Introspect"], - path: "IntrospectTests" - ) + path: "IntrospectTests" // TODO: rename to Tests for v1.0 + ), ] ) diff --git a/Podfile b/Podfile deleted file mode 100644 index 08bdf02ce..000000000 --- a/Podfile +++ /dev/null @@ -1,9 +0,0 @@ -platform :ios, "13.0" -inhibit_all_warnings! -use_frameworks! - -# Disable sending stats (takes too much time) -ENV["COCOAPODS_DISABLE_STATS"] = "true" - -target "IntrospectExamples" -pod "Reveal-SDK", "24", :configurations => ['Debug'] diff --git a/Podfile.lock b/Podfile.lock deleted file mode 100644 index bcf762566..000000000 --- a/Podfile.lock +++ /dev/null @@ -1,16 +0,0 @@ -PODS: - - Reveal-SDK (24) - -DEPENDENCIES: - - Reveal-SDK (= 24) - -SPEC REPOS: - trunk: - - Reveal-SDK - -SPEC CHECKSUMS: - Reveal-SDK: 5d7e56b8f018c0a88b3a2c10bf68d598bbd3b071 - -PODFILE CHECKSUM: 3558c3b60c01e8cff4f7b0ac1bd6ef19441afc81 - -COCOAPODS: 1.10.0 diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 07d836dd5..e292af6da 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -3,31 +3,45 @@ skip_docs platform :ios do lane :test do |options| - puts 'Running on GitHub CI.' if options[:ci] == 'github' - multi_scan( - devices: ["iPhone 8", "iPad (8th generation)"], - scheme: "Introspect iOS", - skip_build: options[:ci] == 'github', - try_count: 3, - quit_simulators: false - ) + version = options[:version].to_i + + devices = case version + when 14 + [ + "iPhone 11 Pro (14.5)", + "iPad Pro (11-inch) (3rd generation) (14.5)", + "Apple TV (14.5)", + ] + when 15 + [ + "iPhone 11 Pro (15.5)", + "iPad Pro (11-inch) (3rd generation) (15.5)", + "Apple TV (15.4)", + ] + when 16 + [ + "iPhone 14 Pro (16.2)", + "iPad Pro (11-inch) (4th generation) (16.2)", + "Apple TV (16.1)", + ] + else + raise "Unsupported iOS version: #{version}" + end - multi_scan( - devices: ["Apple TV"], - scheme: "Introspect tvOS", - skip_build: options[:ci] == 'github', - try_count: 3, - quit_simulators: false + run_tests( + scheme: "Introspect", + devices: devices, + ensure_devices_found: true, + force_quit_simulator: true, + disable_concurrent_testing: true, ) end end -platform :mac do +platform :macos do lane :test do |options| - puts 'Running on GitHub CI.' if options[:ci] == 'github' - scan( - scheme: "Introspect macOS", - skip_build: options[:ci] == 'github' + spm( + command: "test", ) end end diff --git a/fastlane/Pluginfile b/fastlane/Pluginfile index 139f3a778..273a6b6f4 100644 --- a/fastlane/Pluginfile +++ b/fastlane/Pluginfile @@ -1,5 +1,3 @@ # Autogenerated by fastlane # # Ensure this file is checked in to source control! - -gem 'fastlane-plugin-test_center' diff --git a/renovate.json b/renovate.json deleted file mode 100644 index f45d8f110..000000000 --- a/renovate.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": [ - "config:base" - ] -} From 21327d039166be8fad3b521d28ce5a698dbf6227 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sun, 12 Feb 2023 00:26:21 +0000 Subject: [PATCH 014/116] Cleanup (#186) --- .github/workflows/ci.yml | 3 --- Introspect.podspec | 2 +- Introspect/ViewExtensions.swift | 11 +++++------ IntrospectTests/AppKitTests.swift | 2 +- IntrospectTests/UIKitTests.swift | 12 ++++++------ 5 files changed, 13 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1837e6e99..e9ee98d77 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,9 +51,6 @@ jobs: with: xcode-version: ${{ matrix.xcode }} - # - name: Export macOS SDK - # run: echo SDKROOT=$(xcrun --sdk macosx --show-sdk-path) >> $GITHUB_ENV - - name: Install Homebrew dependencies run: brew install xcbeautify diff --git a/Introspect.podspec b/Introspect.podspec index 53e3d8891..7cc35d683 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.2' + spec.swift_version = '5.4' spec.ios.deployment_target = '13.0' spec.tvos.deployment_target = '13.0' spec.osx.deployment_target = '10.15' diff --git a/Introspect/ViewExtensions.swift b/Introspect/ViewExtensions.swift index 18d27f0e1..b48aa7b53 100644 --- a/Introspect/ViewExtensions.swift +++ b/Introspect/ViewExtensions.swift @@ -107,7 +107,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.0, tvOS 14.0, macOS 11.0, *) { + if #available(iOS 14, tvOS 14, *) { return introspect(selector: TargetViewSelector.siblingOfTypeOrAncestor, customize: customize) } else { return introspect(selector: TargetViewSelector.siblingContainingOrAncestor, customize: customize) @@ -117,8 +117,7 @@ extension View { /// Finds the horizontal `UIScrollView` from a `SwiftUI.TabBarView` with tab style `SwiftUI.PageTabViewStyle`. /// /// Customize is called with a `UICollectionView` wrapper, and the horizontal `UIScrollView`. - @available(iOS 14.0, tvOS 14.0, *) - @available(macOS, unavailable) + @available(iOS 14, tvOS 14, *) public func introspectPagedTabView(customize: @escaping (UICollectionView, UIScrollView) -> ()) -> some View { return introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, customize: { (collectionView: UICollectionView) in for subview in collectionView.subviews { @@ -170,7 +169,7 @@ extension View { } /// Finds a `UIColorWell` from a `SwiftUI.ColorPicker` - @available(iOS 14.0, macCatalyst 14.0, *) + @available(iOS 14, *) @available(tvOS, unavailable) public func introspectColorWell(customize: @escaping (UIColorWell) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) @@ -204,7 +203,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.0, *) { + if #available(macOS 11, *) { return introspect(selector: TargetViewSelector.siblingOfTypeOrAncestor, customize: customize) } else { return introspect(selector: TargetViewSelector.siblingContainingOrAncestor, customize: customize) @@ -252,7 +251,7 @@ extension View { } /// Finds a `NSColorWell` from a `SwiftUI.ColorPicker` - @available(macOS 11.0, *) + @available(macOS 11, *) public func introspectColorWell(customize: @escaping (NSColorWell) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) } diff --git a/IntrospectTests/AppKitTests.swift b/IntrospectTests/AppKitTests.swift index f7996b876..e4d380610 100644 --- a/IntrospectTests/AppKitTests.swift +++ b/IntrospectTests/AppKitTests.swift @@ -110,7 +110,7 @@ private struct TextFieldTestView: View { } } -@available(macOS 11.0, *) +@available(macOS 11, *) private struct TextEditorTestView: View { let spy: () -> Void @State private var textEditorValue = "" diff --git a/IntrospectTests/UIKitTests.swift b/IntrospectTests/UIKitTests.swift index 22fa2afa8..c2f4aa175 100644 --- a/IntrospectTests/UIKitTests.swift +++ b/IntrospectTests/UIKitTests.swift @@ -118,7 +118,7 @@ private struct TabRootTestView: View { } } -@available(iOS 14.0, tvOS 14.0, *) +@available(iOS 14, tvOS 14, *) private struct PageTabViewStyleTestView: View { let spy: (UICollectionView, UIScrollView) -> Void @@ -271,7 +271,7 @@ private struct TextFieldTestView: View { } } -@available(iOS 14.0, macCatalyst 14.0, *) +@available(iOS 14, *) @available(tvOS, unavailable, message: "TextEditor is not available in tvOS.") private struct TextEditorTestView: View { let spy: () -> Void @@ -351,7 +351,7 @@ private struct SegmentedControlTestView: View { } } -@available(iOS 14.0, macCatalyst 14.0, *) +@available(iOS 14.0, *) @available(tvOS, unavailable) private struct ColorWellTestView: View { @State private var color = Color.black @@ -587,7 +587,7 @@ class UIKitTests: XCTestCase { wait(for: [expectation], timeout: TestUtils.Constants.timeout) } - @available(iOS 14.0, macCatalyst 14.0, *) + @available(iOS 14, *) @available(tvOS, unavailable, message: "TextEditor is not available in tvOS.") func testTextEditor() { @@ -599,7 +599,7 @@ class UIKitTests: XCTestCase { wait(for: [expectation], timeout: TestUtils.Constants.timeout) } - @available(iOS 14.0, macCatalyst 14.0, *) + @available(iOS 14, *) @available(tvOS, unavailable, message: "ColorPicker is not available in tvOS.") func testColorPicker() { @@ -611,7 +611,7 @@ class UIKitTests: XCTestCase { wait(for: [expectation], timeout: TestUtils.Constants.timeout) } - @available(iOS 14.0, tvOS 14.0, *) + @available(iOS 14, tvOS 14, *) func testPagedTabView() throws { var collectionView1: UICollectionView? From b765618f25deb9f92eece43d690551ac7a6505c5 Mon Sep 17 00:00:00 2001 From: Marco Quinten Date: Sun, 12 Feb 2023 01:30:26 +0100 Subject: [PATCH 015/116] Update CHANGELOG.md (#152) --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41efcd5d4..8a3da2603 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ Changelog ## master +- Added `.introspectPagedTabView` on iOS +- Updated dependencies + +## [0.1.4] + - Added `.introspectSplitViewController()` on iOS - Fixed iPad tests - Added iPad to CI From be01237389446d5531c78c057b5e6f91c1563a0a Mon Sep 17 00:00:00 2001 From: MichaelJBerk <49624366+MichaelJBerk@users.noreply.github.com> Date: Sun, 12 Feb 2023 12:24:18 -0500 Subject: [PATCH 016/116] Add `introspectMapView` (#125) Co-authored-by: David Roman <2538074+davdroman@users.noreply.github.com> --- Introspect/ViewExtensions.swift | 12 ++++++++ IntrospectTests/AppKitTests.swift | 23 ++++++++++++++ IntrospectTests/UIKitTests.swift | 50 ++++++++++++++++++++++--------- 3 files changed, 71 insertions(+), 14 deletions(-) diff --git a/Introspect/ViewExtensions.swift b/Introspect/ViewExtensions.swift index b48aa7b53..6399e7573 100644 --- a/Introspect/ViewExtensions.swift +++ b/Introspect/ViewExtensions.swift @@ -257,3 +257,15 @@ extension View { } } #endif + +#if canImport(MapKit) +import MapKit + +extension View { + /// Finds an `MKMapView` from a `SwiftUI.Map` + @available(iOS 14, tvOS 14, macOS 11, *) + public func introspectMapView(customize: @escaping (MKMapView) -> ()) -> some View { + introspect(selector: TargetViewSelector.siblingContaining, customize: customize) + } +} +#endif diff --git a/IntrospectTests/AppKitTests.swift b/IntrospectTests/AppKitTests.swift index e4d380610..3f59db94d 100644 --- a/IntrospectTests/AppKitTests.swift +++ b/IntrospectTests/AppKitTests.swift @@ -227,6 +227,20 @@ private struct ColorWellTestView: View { } } +import MapKit +@available(macOS 11, *) +private struct MapTestView: View { + @State private var coordinateRegion = MKCoordinateRegion(.world) + let spy: () -> Void + + var body: some View { + Map(coordinateRegion: $coordinateRegion) + .introspectMapView { mapView in + self.spy() + } + } +} + class AppKitTests: XCTestCase { func testList() { @@ -403,5 +417,14 @@ class AppKitTests: XCTestCase { TestUtils.present(view: view) wait(for: [expectation], timeout: TestUtils.Constants.timeout) } + + func testMapView() { + let expectation = XCTestExpectation() + let view = MapTestView(spy: { + expectation.fulfill() + }) + TestUtils.present(view: view) + wait(for: [expectation], timeout: TestUtils.Constants.timeout) + } } #endif diff --git a/IntrospectTests/UIKitTests.swift b/IntrospectTests/UIKitTests.swift index c2f4aa175..c269810c4 100644 --- a/IntrospectTests/UIKitTests.swift +++ b/IntrospectTests/UIKitTests.swift @@ -365,6 +365,20 @@ private struct ColorWellTestView: View { } } +import MapKit +@available(iOS 14, tvOS 14, *) +private struct MapTestView: View { + @State private var coordinateRegion = MKCoordinateRegion(.world) + let spy: () -> Void + + var body: some View { + Map(coordinateRegion: $coordinateRegion) + .introspectMapView { mapView in + self.spy() + } + } +} + class UIKitTests: XCTestCase { func testNavigation() { @@ -525,28 +539,28 @@ class UIKitTests: XCTestCase { TestUtils.present(view: view) wait(for: [expectation], timeout: TestUtils.Constants.timeout) } - - #if os(iOS) - func testSplitNavigation() { + + func testRootNavigation() { let expectation = XCTestExpectation() - let view = SplitNavigationTestView(spy: { + let view = NavigationRootTestView(spy: { expectation.fulfill() }) TestUtils.present(view: view) wait(for: [expectation], timeout: TestUtils.Constants.timeout) } - - func testRootNavigation() { - + + #if !os(tvOS) + func testSplitNavigation() { + let expectation = XCTestExpectation() - let view = NavigationRootTestView(spy: { + let view = SplitNavigationTestView(spy: { expectation.fulfill() }) TestUtils.present(view: view) wait(for: [expectation], timeout: TestUtils.Constants.timeout) } - + func testToggle() { let expectation = XCTestExpectation() @@ -556,7 +570,7 @@ class UIKitTests: XCTestCase { TestUtils.present(view: view) wait(for: [expectation], timeout: TestUtils.Constants.timeout) } - + func testSlider() { let expectation = XCTestExpectation() @@ -566,7 +580,7 @@ class UIKitTests: XCTestCase { TestUtils.present(view: view) wait(for: [expectation], timeout: TestUtils.Constants.timeout) } - + func testStepper() { let expectation = XCTestExpectation() @@ -576,7 +590,7 @@ class UIKitTests: XCTestCase { TestUtils.present(view: view) wait(for: [expectation], timeout: TestUtils.Constants.timeout) } - + func testDatePicker() { let expectation = XCTestExpectation() @@ -588,7 +602,6 @@ class UIKitTests: XCTestCase { } @available(iOS 14, *) - @available(tvOS, unavailable, message: "TextEditor is not available in tvOS.") func testTextEditor() { let expectation = XCTestExpectation() @@ -600,7 +613,6 @@ class UIKitTests: XCTestCase { } @available(iOS 14, *) - @available(tvOS, unavailable, message: "ColorPicker is not available in tvOS.") func testColorPicker() { let expectation = XCTestExpectation() @@ -636,5 +648,15 @@ class UIKitTests: XCTestCase { } } #endif + + @available(iOS 14, tvOS 14, *) + func testMapView() { + let expectation = XCTestExpectation() + let view = MapTestView(spy: { + expectation.fulfill() + }) + TestUtils.present(view: view) + wait(for: [expectation], timeout: TestUtils.Constants.timeout) + } } #endif From a1a97a8aea6009e62041e482013120f019a206c8 Mon Sep 17 00:00:00 2001 From: Ian Sampson Date: Sun, 12 Feb 2023 11:42:59 -0800 Subject: [PATCH 017/116] Add `introspectSearchController` (#129) Co-authored-by: David Roman <2538074+davdroman@users.noreply.github.com> --- .../Showcase.xcodeproj/project.pbxproj | 4 ++ Examples/Showcase/Showcase/ContentView.swift | 37 ++++++++++++++----- Examples/Showcase/Showcase/Helpers.swift | 9 +++++ Introspect/ViewExtensions.swift | 12 ++++++ IntrospectTests/UIKitTests.swift | 36 +++++++++++++++++- 5 files changed, 88 insertions(+), 10 deletions(-) create mode 100644 Examples/Showcase/Showcase/Helpers.swift diff --git a/Examples/Showcase/Showcase.xcodeproj/project.pbxproj b/Examples/Showcase/Showcase.xcodeproj/project.pbxproj index f57b87d6b..21c20d3a3 100644 --- a/Examples/Showcase/Showcase.xcodeproj/project.pbxproj +++ b/Examples/Showcase/Showcase.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 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 */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -21,6 +22,7 @@ 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 */ /* Begin PBXFrameworksBuildPhase section */ @@ -58,6 +60,7 @@ D530720429983D9300F1936C /* Showcase.entitlements */, D53071F629983CEF00F1936C /* App.swift */, D53071F829983CEF00F1936C /* ContentView.swift */, + D5B829742999738200920EBD /* Helpers.swift */, D53071FA29983CF000F1936C /* Assets.xcassets */, D53071FC29983CF000F1936C /* Preview Content */, ); @@ -153,6 +156,7 @@ buildActionMask = 2147483647; files = ( D53071F929983CEF00F1936C /* ContentView.swift in Sources */, + D5B829752999738200920EBD /* Helpers.swift in Sources */, D53071F729983CEF00F1936C /* App.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Examples/Showcase/Showcase/ContentView.swift b/Examples/Showcase/Showcase/ContentView.swift index 3cfe549df..24f9c3f6a 100644 --- a/Examples/Showcase/Showcase/ContentView.swift +++ b/Examples/Showcase/Showcase/ContentView.swift @@ -72,15 +72,34 @@ struct ListShowcase: View { struct NavigationShowcase: View { var body: some View { NavigationView { - VStack { - Text("Customized") - } - #if !os(tvOS) - .navigationBarTitle(Text("Customized"), displayMode: .inline) - #endif - .introspectNavigationController { nvc in - nvc.navigationBar.backgroundColor = .red - } + Text("Customized") + .do { + if #available(iOS 15, tvOS 15, *) { + $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) + .navigationBarTitle(Text("Customized"), displayMode: .inline) + #endif + .introspectNavigationController { navigationController in + navigationController.navigationBar.backgroundColor = .red + } + } + .introspectSplitViewController { splitViewController in + splitViewController.preferredDisplayMode = .oneOverSecondary } } } diff --git a/Examples/Showcase/Showcase/Helpers.swift b/Examples/Showcase/Showcase/Helpers.swift new file mode 100644 index 000000000..5670498b1 --- /dev/null +++ b/Examples/Showcase/Showcase/Helpers.swift @@ -0,0 +1,9 @@ +import SwiftUI + +extension View { + func `do`( + @ViewBuilder transform: (Self) -> TransformedView + ) -> TransformedView { + transform(self) + } +} diff --git a/Introspect/ViewExtensions.swift b/Introspect/ViewExtensions.swift index 6399e7573..0a797180b 100644 --- a/Introspect/ViewExtensions.swift +++ b/Introspect/ViewExtensions.swift @@ -84,6 +84,18 @@ extension View { customize: customize )) } + + /// Finds a `UISearchController` from a `SwiftUI.View` with a `.searchable` modifier + @available(iOS 15, *) + @available(tvOS, unavailable) + public func introspectSearchController(customize: @escaping (UISearchController) -> ()) -> some View { + introspectNavigationController { navigationController in + let navigationBar = navigationController.navigationBar + if let searchController = navigationBar.topItem?.searchController { + customize(searchController) + } + } + } /// Finds a `UITableView` from a `SwiftUI.List`, or `SwiftUI.List` child. public func introspectTableView(customize: @escaping (UITableView) -> ()) -> some View { diff --git a/IntrospectTests/UIKitTests.swift b/IntrospectTests/UIKitTests.swift index c269810c4..f9bee7101 100644 --- a/IntrospectTests/UIKitTests.swift +++ b/IntrospectTests/UIKitTests.swift @@ -379,6 +379,28 @@ private struct MapTestView: View { } } +#if swift(>=5.5) && !os(tvOS) // swift check needed for some reason for tvOS 14 testing not to fail on CI +@available(iOS 15, *) +@available(tvOS, unavailable) +private struct SearchControllerTestView: View { + @State var searchText = "" + let spy: () -> Void + + var body: some View { + NavigationView { + EmptyView() + .searchable(text: $searchText) + .introspectSearchController { searchController in + self.spy() + } + } + .introspectSplitViewController { splitViewController in + splitViewController.preferredDisplayMode = .oneOverSecondary + } + } +} +#endif + class UIKitTests: XCTestCase { func testNavigation() { @@ -647,8 +669,20 @@ class UIKitTests: XCTestCase { XCTAssertTrue(unwrappedCollectionView.subviews.contains(where: { $0 === unwrappedScrollView })) } } - #endif + #if swift(>=5.5) && !os(tvOS) + @available(iOS 15, *) + func testSearchController() { + let expectation = XCTestExpectation() + let view = SearchControllerTestView(spy: { + expectation.fulfill() + }) + TestUtils.present(view: view) + wait(for: [expectation], timeout: TestUtils.Constants.timeout) + } + #endif + #endif + @available(iOS 14, tvOS 14, *) func testMapView() { let expectation = XCTestExpectation() From e0536ffe50c3f8b14631982626350ded1afacaae Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 13 Feb 2023 23:07:11 +0000 Subject: [PATCH 018/116] Add support for NSplitView (#100) Co-authored-by: David Roman <2538074+davdroman@users.noreply.github.com> --- Introspect/ViewExtensions.swift | 33 ++++++++++++++++++------------- IntrospectTests/AppKitTests.swift | 26 +++++++++++++++++++++++- README.md | 8 +++----- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/Introspect/ViewExtensions.swift b/Introspect/ViewExtensions.swift index 0a797180b..a9ba68d35 100644 --- a/Introspect/ViewExtensions.swift +++ b/Introspect/ViewExtensions.swift @@ -45,20 +45,20 @@ extension View { /// Finds a `UISplitViewController` from a `SwiftUI.NavigationView` with style `DoubleColumnNavigationViewStyle`. public func introspectSplitViewController(customize: @escaping (UISplitViewController) -> ()) -> some View { - inject(UIKitIntrospectionViewController( - selector: { introspectionViewController in - - // Search in ancestors - if let splitViewController = introspectionViewController.splitViewController { - return splitViewController - } - - // Search in siblings - return Introspect.previousSibling(containing: UISplitViewController.self, from: introspectionViewController) - }, - customize: customize - )) - } + inject(UIKitIntrospectionViewController( + selector: { introspectionViewController in + + // Search in ancestors + if let splitViewController = introspectionViewController.splitViewController { + return splitViewController + } + + // Search in siblings + return Introspect.previousSibling(containing: UISplitViewController.self, from: introspectionViewController) + }, + customize: customize + )) + } /// Finds the containing `UIViewController` of a SwiftUI view. public func introspectViewController(customize: @escaping (UIViewController) -> ()) -> some View { @@ -202,6 +202,11 @@ extension View { customize: customize )) } + + /// Finds a `NSSplitViewController` from a `SwiftUI.NavigationView` with style `DoubleColumnNavigationViewStyle`. + public func introspectSplitView(customize: @escaping (NSSplitView) -> ()) -> some View { + return introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, customize: customize) + } /// Finds a `NSTableView` from a `SwiftUI.List`, or `SwiftUI.List` child. public func introspectTableView(customize: @escaping (NSTableView) -> ()) -> some View { diff --git a/IntrospectTests/AppKitTests.swift b/IntrospectTests/AppKitTests.swift index 3f59db94d..dc0de1372 100644 --- a/IntrospectTests/AppKitTests.swift +++ b/IntrospectTests/AppKitTests.swift @@ -23,6 +23,20 @@ enum TestUtils { } } +private struct SplitNavigationTestView: View { + let spy: () -> Void + var body: some View { + NavigationView { + VStack { + EmptyView() + } + } + .introspectSplitView { splitView in + self.spy() + } + } +} + private struct ListTestView: View { let spy1: () -> Void @@ -242,7 +256,17 @@ private struct MapTestView: View { } class AppKitTests: XCTestCase { - + + func testSplitNavigation() { + + let expectation = XCTestExpectation() + let view = SplitNavigationTestView(spy: { + expectation.fulfill() + }) + TestUtils.present(view: view) + wait(for: [expectation], timeout: TestUtils.Constants.timeout) + } + func testList() { if #available(macOS 11, *) { return // TODO: verify whether List still uses NSTableView under the hood in macOS >=11 diff --git a/README.md b/README.md index 1995b16b7..b2a856dce 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ SwiftUI | UIKit | AppKit | Introspect --- | --- | --- | --- NavigationView (StackNavigationViewStyle) | UINavigationController | _N/A_ | `.introspectNavigationController()` NavigationView (DoubleColumnNavigationViewStyle) | UISplitViewController | _N/A_ | `.introspectSplitViewController()` +NavigationView (DoubleColumnNavigationViewStyle) | _N/A_ | NSSplitView | `.introspectSplitView()` _Any embedded view_ | UIViewController | _N/A_ | `.introspectViewController()` ScrollView | UIScrollView | NSScrollView | `.introspectScrollView()` List | UITableView | NSTableView | `.introspectTableView()` @@ -188,13 +189,10 @@ $ bundle exec pod trunk push . -[CircleCI_Status]: https://circleci.com/gh/siteline/SwiftUI-Introspect.svg?style=svg&circle-token=6f995f204d4d417d31f79e7257f6e1ecf430ae07 -[CircleCI_URL]: https://circleci.com/gh/siteline/SwiftUI-Introspect +[GithubCI_Status]: https://github.com/siteline/swiftui-introspect/actions/workflows/ci.yml/badge.svg?branch=master -[GithubCI_Status]: https://github.com/siteline/swiftui-introspect/actions/workflows/build-and-test.yml/badge.svg?branch=master - -[GithubCI_URL]: https://github.com/siteline/SwiftUI-Introspect/actions/workflows/build-and-test.yml +[GithubCI_URL]: https://github.com/siteline/SwiftUI-Introspect/actions/workflows/ci.yml [Siteline_Badge]: https://badgen.net/badge/Built%20by/Siteline/blue?icon=https://uploads-ssl.webflow.com/5f4513afbbfc64c4777fcccf/5f525b122370d681879e170e_siteline-icon.svg From 8339f0afe49a3d010466fad490858107c1dc56bf Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 14 Feb 2023 00:33:31 +0000 Subject: [PATCH 019/116] Update changelog (#190) --- CHANGELOG.md | 14 +++++-- Gemfile | 3 ++ Gemfile.lock | 97 +++++++++++++++++++++++++++++++++++++++++++++ README.md | 42 +++++++++----------- fastlane/Pluginfile | 3 -- 5 files changed, 129 insertions(+), 30 deletions(-) create mode 100644 Gemfile create mode 100644 Gemfile.lock delete mode 100644 fastlane/Pluginfile diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a3da2603..932e6991a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,16 @@ Changelog ## master -- Added `.introspectPagedTabView` on iOS -- Updated dependencies +- Added: `introspectCollectionView/introspectCollectionViewCell` (#169) +- Added: `introspectSearchController` (#129) +- Added: `introspectPagedTabView` (#117) +- Added: `introspectMapView` (#125) +- Added: `introspectSplitView` on macOS (#100) +- Added: explicitly static/dynamic SPM library products (#168) +- Fixed: view controller introspection (#165) +- Fixed: issue where introspecting within a LazyVStack would silently fail #153 +- Infrastructure: test coverage now spans iOS/tvOS 14/15/16 and macOS 11/12 (#185) +- Infrastructure: removed CircleCI in favor of GitHub Actions (#182, #183) ## [0.1.4] @@ -64,7 +72,7 @@ Changelog ## [0.0.2] - Added documentation for all methods. - + ## [0.0.1] - First release. diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..0341aea27 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" + +gem "cocoapods", "1.11.3" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 000000000..e086031d4 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,97 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.6) + rexml + activesupport (6.1.7.2) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + zeitwerk (~> 2.3) + addressable (2.8.1) + public_suffix (>= 2.0.2, < 6.0) + algoliasearch (1.27.5) + httpclient (~> 2.8, >= 2.8.3) + json (>= 1.5.1) + atomos (0.1.3) + claide (1.1.0) + cocoapods (1.11.3) + addressable (~> 2.8) + claide (>= 1.0.2, < 2.0) + cocoapods-core (= 1.11.3) + cocoapods-deintegrate (>= 1.0.3, < 2.0) + cocoapods-downloader (>= 1.4.0, < 2.0) + cocoapods-plugins (>= 1.0.0, < 2.0) + cocoapods-search (>= 1.0.0, < 2.0) + cocoapods-trunk (>= 1.4.0, < 2.0) + cocoapods-try (>= 1.1.0, < 2.0) + colored2 (~> 3.1) + escape (~> 0.0.4) + fourflusher (>= 2.3.0, < 3.0) + gh_inspector (~> 1.0) + molinillo (~> 0.8.0) + nap (~> 1.0) + ruby-macho (>= 1.0, < 3.0) + xcodeproj (>= 1.21.0, < 2.0) + cocoapods-core (1.11.3) + activesupport (>= 5.0, < 7) + addressable (~> 2.8) + algoliasearch (~> 1.0) + concurrent-ruby (~> 1.1) + fuzzy_match (~> 2.0.4) + nap (~> 1.0) + netrc (~> 0.11) + public_suffix (~> 4.0) + typhoeus (~> 1.0) + cocoapods-deintegrate (1.0.5) + cocoapods-downloader (1.6.3) + cocoapods-plugins (1.0.0) + nap + cocoapods-search (1.0.1) + cocoapods-trunk (1.6.0) + nap (>= 0.8, < 2.0) + netrc (~> 0.11) + cocoapods-try (1.2.0) + colored2 (3.1.2) + concurrent-ruby (1.2.0) + escape (0.0.4) + ethon (0.16.0) + ffi (>= 1.15.0) + ffi (1.15.5) + fourflusher (2.3.1) + fuzzy_match (2.0.4) + gh_inspector (1.1.3) + httpclient (2.8.3) + i18n (1.12.0) + concurrent-ruby (~> 1.0) + json (2.6.3) + minitest (5.17.0) + molinillo (0.8.0) + nanaimo (0.3.0) + nap (1.1.0) + netrc (0.11.0) + public_suffix (4.0.7) + rexml (3.2.5) + ruby-macho (2.5.1) + typhoeus (1.4.0) + ethon (>= 0.9.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + xcodeproj (1.22.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.3.0) + rexml (~> 3.2.4) + zeitwerk (2.6.7) + +PLATFORMS + arm64-darwin-22 + +DEPENDENCIES + cocoapods (= 1.11.3) + +BUNDLED WITH + 2.4.6 diff --git a/README.md b/README.md index b2a856dce..bc42ddecb 100644 --- a/README.md +++ b/README.md @@ -114,9 +114,9 @@ ScrollView { ```swift NavigationView { Text("Item 2") - .introspectNavigationController { navigationController in - navigationController.navigationBar.backgroundColor = .red - } + .introspectNavigationController { navigationController in + navigationController.navigationBar.backgroundColor = .red + } } ``` @@ -124,9 +124,9 @@ NavigationView { ```swift TextField("Text Field", text: $textFieldValue) -.introspectTextField { textField in - textField.layer.backgroundColor = UIColor.red.cgColor -} + .introspectTextField { textField in + textField.layer.backgroundColor = UIColor.red.cgColor + } ``` Implement your own selector @@ -165,27 +165,21 @@ You can use any of the following [methods](https://github.com/timbersoftware/Swi Releasing --------- - - Increment version number: +1. Update changelog with new version +2. Bump version in `Introspect.podspec` +3. PR and merge changes +4. Tag new version: -``` -$ bundle exec fastlane run increment_version_number bump_type:minor # major|minor|patch -``` + ```sh + $ git tag -a -m "" + $ git push origin --tags + ``` - - Update changelog with new version - - Bump version in `Introspect.podspec` - - Commit and push changes - - Tag new version: +5. Push to CocoaPods trunk: -``` -$ git tag -a -m "" -$ git push origin --tags -``` - - - Push to cocoapods trunk: - -``` -$ bundle exec pod trunk push . -``` + ```sh + $ bundle exec pod trunk push . + ``` diff --git a/fastlane/Pluginfile b/fastlane/Pluginfile deleted file mode 100644 index 273a6b6f4..000000000 --- a/fastlane/Pluginfile +++ /dev/null @@ -1,3 +0,0 @@ -# Autogenerated by fastlane -# -# Ensure this file is checked in to source control! From fc40fb11d4922d6e936a3d2abaa54bcab4fc877e Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 14 Feb 2023 01:05:40 +0000 Subject: [PATCH 020/116] Bump to 0.2.0 (#191) --- CHANGELOG.md | 3 +++ Introspect.podspec | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 932e6991a..f7e9dbf65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Changelog ## master +## [0.2.0] + - Added: `introspectCollectionView/introspectCollectionViewCell` (#169) - Added: `introspectSearchController` (#129) - Added: `introspectPagedTabView` (#117) @@ -77,6 +79,7 @@ Changelog - First release. +[0.2.0]: https://github.com/timbersoftware/SwiftUI-Introspect/releases/tag/0.2.0 [0.1.3]: https://github.com/timbersoftware/SwiftUI-Introspect/releases/tag/0.1.3 [0.1.2]: https://github.com/timbersoftware/SwiftUI-Introspect/releases/tag/0.1.2 [0.1.1]: https://github.com/timbersoftware/SwiftUI-Introspect/releases/tag/0.1.1 diff --git a/Introspect.podspec b/Introspect.podspec index 7cc35d683..a03922c6b 100644 --- a/Introspect.podspec +++ b/Introspect.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'Introspect' - spec.version = '0.1.3' + spec.version = '0.2.0' spec.license = { type: 'MIT' } spec.homepage = 'https://github.com/siteline/SwiftUI-Introspect.git' spec.authors = { 'Lois Di Qual' => 'lois@siteline.com' } From dd06d8b77eca7f7f17429aaf5d35919682be28c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=B7=BB=E5=AE=9D?= <33550945+okmyself@users.noreply.github.com> Date: Wed, 15 Feb 2023 23:05:49 +0800 Subject: [PATCH 021/116] Fix memory leak in #161 and regression in #194 (#192) Co-authored-by: David Roman <2538074+davdroman@users.noreply.github.com> --- Introspect/UIKitIntrospectionView.swift | 11 ++++++++++- Introspect/UIKitIntrospectionViewController.swift | 13 +++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Introspect/UIKitIntrospectionView.swift b/Introspect/UIKitIntrospectionView.swift index d999a7ef2..bb2648055 100644 --- a/Introspect/UIKitIntrospectionView.swift +++ b/Introspect/UIKitIntrospectionView.swift @@ -63,9 +63,18 @@ public struct UIKitIntrospectionView: UIViewRepresentabl return view } + /// In some cases, UIView does not call `moveToWindowHandler`, + /// so the `moveToWindowHandler` call in updateUIView must be preserved. public func updateUIView( _ uiView: IntrospectionUIView, context: UIViewRepresentableContext - ) {} + ) { + uiView.moveToWindowHandler?() + } + + /// Avoid memory leaks. + public static func dismantleUIView(_ uiView: IntrospectionUIView, coordinator: ()) { + uiView.moveToWindowHandler = nil + } } #endif diff --git a/Introspect/UIKitIntrospectionViewController.swift b/Introspect/UIKitIntrospectionViewController.swift index daeafd108..bbf6f06db 100644 --- a/Introspect/UIKitIntrospectionViewController.swift +++ b/Introspect/UIKitIntrospectionViewController.swift @@ -52,9 +52,18 @@ public struct UIKitIntrospectionViewController - ) {} + ) { + (viewController.view as? IntrospectionUIView)?.moveToWindowHandler?() + } + + /// Avoid memory leaks. + public static func dismantleUIViewController(_ viewController: IntrospectionUIViewController, coordinator: ()) { + (viewController.view as? IntrospectionUIView)?.moveToWindowHandler = nil + } } #endif From 7a00ea983ab35346048f7aa23f2422a7890a8361 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Wed, 15 Feb 2023 15:28:08 +0000 Subject: [PATCH 022/116] Bump to 0.2.1 (#195) --- CHANGELOG.md | 4 ++++ Introspect.podspec | 2 +- Introspect/UIKitIntrospectionView.swift | 5 +++-- Introspect/UIKitIntrospectionViewController.swift | 5 +++-- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7e9dbf65..e39821617 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ Changelog ## master +## [0.2.1] + +- Fixed: memory leak in #161 and regression in #194 (#192) + ## [0.2.0] - Added: `introspectCollectionView/introspectCollectionViewCell` (#169) diff --git a/Introspect.podspec b/Introspect.podspec index a03922c6b..a75dab37d 100644 --- a/Introspect.podspec +++ b/Introspect.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'Introspect' - spec.version = '0.2.0' + spec.version = '0.2.1' spec.license = { type: 'MIT' } spec.homepage = 'https://github.com/siteline/SwiftUI-Introspect.git' spec.authors = { 'Lois Di Qual' => 'lois@siteline.com' } diff --git a/Introspect/UIKitIntrospectionView.swift b/Introspect/UIKitIntrospectionView.swift index bb2648055..236abae92 100644 --- a/Introspect/UIKitIntrospectionView.swift +++ b/Introspect/UIKitIntrospectionView.swift @@ -63,8 +63,9 @@ public struct UIKitIntrospectionView: UIViewRepresentabl return view } - /// In some cases, UIView does not call `moveToWindowHandler`, - /// so the `moveToWindowHandler` call in updateUIView must be preserved. + /// SwiftUI state changes after `makeUIView` will trigger this function, not + /// `makeUIView`, so we need to call the handler again to allow re-customization + /// based on the newest state. public func updateUIView( _ uiView: IntrospectionUIView, context: UIViewRepresentableContext diff --git a/Introspect/UIKitIntrospectionViewController.swift b/Introspect/UIKitIntrospectionViewController.swift index bbf6f06db..4d8f1d344 100644 --- a/Introspect/UIKitIntrospectionViewController.swift +++ b/Introspect/UIKitIntrospectionViewController.swift @@ -52,8 +52,9 @@ public struct UIKitIntrospectionViewController From 9a860a08250fe9355db157d437d403df915a0246 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Wed, 15 Feb 2023 20:53:21 +0000 Subject: [PATCH 023/116] Hotfix 0.2.1 (#196) This actually fixes the regression in #194. My bad for not checking better. A good area of improvement for automated tests. --- Introspect/UIKitIntrospectionView.swift | 11 +++++++---- Introspect/UIKitIntrospectionViewController.swift | 5 ++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Introspect/UIKitIntrospectionView.swift b/Introspect/UIKitIntrospectionView.swift index 236abae92..9d9765fe9 100644 --- a/Introspect/UIKitIntrospectionView.swift +++ b/Introspect/UIKitIntrospectionView.swift @@ -67,15 +67,18 @@ public struct UIKitIntrospectionView: UIViewRepresentabl /// `makeUIView`, so we need to call the handler again to allow re-customization /// based on the newest state. public func updateUIView( - _ uiView: IntrospectionUIView, + _ view: IntrospectionUIView, context: UIViewRepresentableContext ) { - uiView.moveToWindowHandler?() + guard let targetView = self.selector(view) else { + return + } + self.customize(targetView) } /// Avoid memory leaks. - public static func dismantleUIView(_ uiView: IntrospectionUIView, coordinator: ()) { - uiView.moveToWindowHandler = nil + public static func dismantleUIView(_ view: IntrospectionUIView, coordinator: ()) { + view.moveToWindowHandler = nil } } #endif diff --git a/Introspect/UIKitIntrospectionViewController.swift b/Introspect/UIKitIntrospectionViewController.swift index 4d8f1d344..a0e20d7bc 100644 --- a/Introspect/UIKitIntrospectionViewController.swift +++ b/Introspect/UIKitIntrospectionViewController.swift @@ -59,7 +59,10 @@ public struct UIKitIntrospectionViewController ) { - (viewController.view as? IntrospectionUIView)?.moveToWindowHandler?() + guard let targetView = self.selector(viewController) else { + return + } + self.customize(targetView) } /// Avoid memory leaks. From 0ee6d36368422aa2fef62fc41a86a179abe46ef1 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Wed, 15 Feb 2023 20:56:29 +0000 Subject: [PATCH 024/116] Bump to 0.2.2 (#197) --- CHANGELOG.md | 4 ++++ Introspect.podspec | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e39821617..d53e373a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ Changelog ## master +## [0.2.2] + +- Hotfix: #192 (#196) + ## [0.2.1] - Fixed: memory leak in #161 and regression in #194 (#192) diff --git a/Introspect.podspec b/Introspect.podspec index a75dab37d..9b9eff466 100644 --- a/Introspect.podspec +++ b/Introspect.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'Introspect' - spec.version = '0.2.1' + spec.version = '0.2.2' spec.license = { type: 'MIT' } spec.homepage = 'https://github.com/siteline/SwiftUI-Introspect.git' spec.authors = { 'Lois Di Qual' => 'lois@siteline.com' } From a77c0ac5c255c02de8f88ea617df7bb3132d6310 Mon Sep 17 00:00:00 2001 From: Guilherme Souza Date: Sat, 18 Feb 2023 09:33:07 -0300 Subject: [PATCH 025/116] Fix `introspectPagedTabView` on iOS 16 (#200) --- Introspect/ViewExtensions.swift | 20 +++++++++++++------- IntrospectTests/UIKitTests.swift | 25 +++++++------------------ 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/Introspect/ViewExtensions.swift b/Introspect/ViewExtensions.swift index a9ba68d35..56f2fcc0c 100644 --- a/Introspect/ViewExtensions.swift +++ b/Introspect/ViewExtensions.swift @@ -131,14 +131,20 @@ extension View { /// Customize is called with a `UICollectionView` wrapper, and the horizontal `UIScrollView`. @available(iOS 14, tvOS 14, *) public func introspectPagedTabView(customize: @escaping (UICollectionView, UIScrollView) -> ()) -> some View { - return introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, customize: { (collectionView: UICollectionView) in - for subview in collectionView.subviews { - if NSStringFromClass(type(of: subview)).contains("EmbeddedScrollView"), let scrollView = subview as? UIScrollView { - customize(collectionView, scrollView) - break + if #available(iOS 16, *) { + return introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, customize: { (collectionView: UICollectionView) in + customize(collectionView, collectionView) + }) + } else { + return introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, customize: { (collectionView: UICollectionView) in + for subview in collectionView.subviews { + if NSStringFromClass(type(of: subview)).contains("EmbeddedScrollView"), let scrollView = subview as? UIScrollView { + customize(collectionView, scrollView) + break + } } - } - }) + }) + } } /// Finds a `UITextField` from a `SwiftUI.TextField` diff --git a/IntrospectTests/UIKitTests.swift b/IntrospectTests/UIKitTests.swift index f9bee7101..c4b0b5125 100644 --- a/IntrospectTests/UIKitTests.swift +++ b/IntrospectTests/UIKitTests.swift @@ -124,24 +124,13 @@ private struct PageTabViewStyleTestView: View { let spy: (UICollectionView, UIScrollView) -> Void var body: some View { - if #available(iOS 16, tvOS 16, *) { - TabView { - Text("Item 1") - .tag(0) - } - .tabViewStyle(PageTabViewStyle()) - .introspectCollectionView { collectionView in - spy(collectionView, collectionView) - } - } else { - TabView { - Text("Item 1") - .tag(0) - } - .tabViewStyle(PageTabViewStyle()) - .introspectPagedTabView { collectionView, scrollView in - spy(collectionView, scrollView) - } + TabView { + Text("Item 1") + .tag(0) + } + .tabViewStyle(PageTabViewStyle()) + .introspectPagedTabView { collectionView, scrollView in + spy(collectionView, scrollView) } } } From 5c764b2a61af44d617c62309e8e7b7bc62b0874e Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sat, 18 Feb 2023 23:05:47 +0000 Subject: [PATCH 026/116] Auto-deploy to CocoaPods (#201) --- .github/workflows/cd.yml | 24 ++++++++++ .github/workflows/ci.yml | 19 ++++++-- Gemfile | 3 -- Gemfile.lock | 97 ---------------------------------------- Introspect.podspec | 2 +- README.md | 14 ++---- 6 files changed, 43 insertions(+), 116 deletions(-) create mode 100644 .github/workflows/cd.yml delete mode 100644 Gemfile delete mode 100644 Gemfile.lock diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 000000000..293791c8c --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,24 @@ +name: cd + +on: + push: + tags: + - '*' + +jobs: + deploy: + runs-on: macos-latest + 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 + 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 + env: + COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9ee98d77..49e4f0e8f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,21 @@ concurrency: cancel-in-progress: true jobs: + lint-podspec: + name: lint podspec + runs-on: macos-latest + steps: + - name: Git Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 # required to be able to find Git tags + + - name: Lint Podspec + run: | + set -eo pipefail + export LIB_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) + pod lib lint --allow-warnings + ci: name: ${{ matrix.platform[0] }} ${{ matrix.platform[1] }} runs-on: ${{ matrix.os }} @@ -41,7 +56,6 @@ jobs: - platform: [macos, 12] os: macos-12 xcode: 14.0.1 - steps: - name: Git Checkout uses: actions/checkout@v3 @@ -54,8 +68,5 @@ jobs: - name: Install Homebrew dependencies run: brew install xcbeautify - - name: Lint Podspec - run: pod lib lint - - name: Run Tests run: fastlane ${{ matrix.platform[0] }} test version:${{ matrix.platform[1] }} diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 0341aea27..000000000 --- a/Gemfile +++ /dev/null @@ -1,3 +0,0 @@ -source "https://rubygems.org" - -gem "cocoapods", "1.11.3" diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index e086031d4..000000000 --- a/Gemfile.lock +++ /dev/null @@ -1,97 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - CFPropertyList (3.0.6) - rexml - activesupport (6.1.7.2) - concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 1.6, < 2) - minitest (>= 5.1) - tzinfo (~> 2.0) - zeitwerk (~> 2.3) - addressable (2.8.1) - public_suffix (>= 2.0.2, < 6.0) - algoliasearch (1.27.5) - httpclient (~> 2.8, >= 2.8.3) - json (>= 1.5.1) - atomos (0.1.3) - claide (1.1.0) - cocoapods (1.11.3) - addressable (~> 2.8) - claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.11.3) - cocoapods-deintegrate (>= 1.0.3, < 2.0) - cocoapods-downloader (>= 1.4.0, < 2.0) - cocoapods-plugins (>= 1.0.0, < 2.0) - cocoapods-search (>= 1.0.0, < 2.0) - cocoapods-trunk (>= 1.4.0, < 2.0) - cocoapods-try (>= 1.1.0, < 2.0) - colored2 (~> 3.1) - escape (~> 0.0.4) - fourflusher (>= 2.3.0, < 3.0) - gh_inspector (~> 1.0) - molinillo (~> 0.8.0) - nap (~> 1.0) - ruby-macho (>= 1.0, < 3.0) - xcodeproj (>= 1.21.0, < 2.0) - cocoapods-core (1.11.3) - activesupport (>= 5.0, < 7) - addressable (~> 2.8) - algoliasearch (~> 1.0) - concurrent-ruby (~> 1.1) - fuzzy_match (~> 2.0.4) - nap (~> 1.0) - netrc (~> 0.11) - public_suffix (~> 4.0) - typhoeus (~> 1.0) - cocoapods-deintegrate (1.0.5) - cocoapods-downloader (1.6.3) - cocoapods-plugins (1.0.0) - nap - cocoapods-search (1.0.1) - cocoapods-trunk (1.6.0) - nap (>= 0.8, < 2.0) - netrc (~> 0.11) - cocoapods-try (1.2.0) - colored2 (3.1.2) - concurrent-ruby (1.2.0) - escape (0.0.4) - ethon (0.16.0) - ffi (>= 1.15.0) - ffi (1.15.5) - fourflusher (2.3.1) - fuzzy_match (2.0.4) - gh_inspector (1.1.3) - httpclient (2.8.3) - i18n (1.12.0) - concurrent-ruby (~> 1.0) - json (2.6.3) - minitest (5.17.0) - molinillo (0.8.0) - nanaimo (0.3.0) - nap (1.1.0) - netrc (0.11.0) - public_suffix (4.0.7) - rexml (3.2.5) - ruby-macho (2.5.1) - typhoeus (1.4.0) - ethon (>= 0.9.0) - tzinfo (2.0.6) - concurrent-ruby (~> 1.0) - xcodeproj (1.22.0) - CFPropertyList (>= 2.3.3, < 4.0) - atomos (~> 0.1.3) - claide (>= 1.0.2, < 2.0) - colored2 (~> 3.1) - nanaimo (~> 0.3.0) - rexml (~> 3.2.4) - zeitwerk (2.6.7) - -PLATFORMS - arm64-darwin-22 - -DEPENDENCIES - cocoapods (= 1.11.3) - -BUNDLED WITH - 2.4.6 diff --git a/Introspect.podspec b/Introspect.podspec index 9b9eff466..5344f47e6 100644 --- a/Introspect.podspec +++ b/Introspect.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'Introspect' - spec.version = '0.2.2' + spec.version = ENV['LIB_VERSION'] spec.license = { type: 'MIT' } spec.homepage = 'https://github.com/siteline/SwiftUI-Introspect.git' spec.authors = { 'Lois Di Qual' => 'lois@siteline.com' } diff --git a/README.md b/README.md index bc42ddecb..7e710dadf 100644 --- a/README.md +++ b/README.md @@ -166,22 +166,14 @@ Releasing --------- 1. Update changelog with new version -2. Bump version in `Introspect.podspec` -3. PR and merge changes -4. Tag new version: +2. PR as 'Bump to X.Y.Z' and merge it +3. Tag new version: ```sh - $ git tag -a -m "" + $ git tag X.Y.Z $ git push origin --tags ``` -5. Push to CocoaPods trunk: - - ```sh - $ bundle exec pod trunk push . - ``` - - [GithubCI_Status]: https://github.com/siteline/swiftui-introspect/actions/workflows/ci.yml/badge.svg?branch=master From c18951c747ab62af7c15e17a81bd37d4fd5a9979 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sat, 18 Feb 2023 23:08:06 +0000 Subject: [PATCH 027/116] Bump to 0.2.3 (#202) --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d53e373a9..03194f1b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ Changelog ## master +## [0.2.3] + +- Fixed: `introspectPagedTabView` on iOS 16 (#200) +- Infrastructure: auto-deploy to CocoaPods (#201) + ## [0.2.2] - Hotfix: #192 (#196) From acd6f8d7234c3bd7b57f408ddfa31bb161e2ea28 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 14 Mar 2023 20:24:20 +0000 Subject: [PATCH 028/116] [CI] Symlink older SDKs to use in newer Xcode versions (#208) --- .github/workflows/ci.yml | 47 ++++++++++++++++++++++---- IntrospectTests/UIKitTests.swift | 47 ++++++++++++-------------- fastlane/Fastfile | 58 ++++++++++++++++++++------------ 3 files changed, 98 insertions(+), 54 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 49e4f0e8f..fb6c3a37e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,24 +38,45 @@ jobs: - [ios, 14] - [ios, 15] - [ios, 16] + + - [tvos, 14] + - [tvos, 15] + - [tvos, 16] + - [macos, 11] - [macos, 12] include: - platform: [ios, 14] os: macos-11 - xcode: 12.5.1 + 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] + - platform: [ios, 15] os: macos-12 - xcode: 13.4.1 + 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] + - platform: [ios, 16] os: macos-12 - xcode: 14.2 + xcode_version: 14.2 + - platform: [tvos, 16] + os: macos-12 + xcode_version: 14.2 + - platform: [macos, 11] os: macos-11 - xcode: 13.0 + xcode_version: 13.2.1 - platform: [macos, 12] os: macos-12 - xcode: 14.0.1 + xcode_version: 14.2 steps: - name: Git Checkout uses: actions/checkout@v3 @@ -63,10 +84,22 @@ jobs: - name: Select Xcode version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: ${{ matrix.xcode }} + xcode-version: ${{ matrix.xcode_version }} + + - 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 run: brew install xcbeautify - name: Run Tests - run: fastlane ${{ matrix.platform[0] }} test version:${{ matrix.platform[1] }} + run: fastlane test platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }} + env: + SKIP_SLOW_FASTLANE_WARNINGS: 1 + FASTLANE_SKIP_UPDATE_CHECK: 1 diff --git a/IntrospectTests/UIKitTests.swift b/IntrospectTests/UIKitTests.swift index c4b0b5125..22a7417af 100644 --- a/IntrospectTests/UIKitTests.swift +++ b/IntrospectTests/UIKitTests.swift @@ -368,28 +368,6 @@ private struct MapTestView: View { } } -#if swift(>=5.5) && !os(tvOS) // swift check needed for some reason for tvOS 14 testing not to fail on CI -@available(iOS 15, *) -@available(tvOS, unavailable) -private struct SearchControllerTestView: View { - @State var searchText = "" - let spy: () -> Void - - var body: some View { - NavigationView { - EmptyView() - .searchable(text: $searchText) - .introspectSearchController { searchController in - self.spy() - } - } - .introspectSplitViewController { splitViewController in - splitViewController.preferredDisplayMode = .oneOverSecondary - } - } -} -#endif - class UIKitTests: XCTestCase { func testNavigation() { @@ -659,9 +637,29 @@ class UIKitTests: XCTestCase { } } - #if swift(>=5.5) && !os(tvOS) - @available(iOS 15, *) + @available(tvOS, unavailable) func testSearchController() { + guard #available(iOS 15, *) else { + return + } + struct SearchControllerTestView: View { + @State var searchText = "" + let spy: () -> Void + + var body: some View { + NavigationView { + EmptyView() + .searchable(text: $searchText) + .introspectSearchController { searchController in + self.spy() + } + } + .introspectSplitViewController { splitViewController in + splitViewController.preferredDisplayMode = .oneOverSecondary + } + } + } + let expectation = XCTestExpectation() let view = SearchControllerTestView(spy: { expectation.fulfill() @@ -670,7 +668,6 @@ class UIKitTests: XCTestCase { wait(for: [expectation], timeout: TestUtils.Constants.timeout) } #endif - #endif @available(iOS 14, tvOS 14, *) func testMapView() { diff --git a/fastlane/Fastfile b/fastlane/Fastfile index e292af6da..90d8c25fd 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -1,47 +1,61 @@ -default_platform(:ios) skip_docs -platform :ios do - lane :test do |options| - version = options[:version].to_i +lane :test do |options| + platform = options[:platform].to_s + version = options[:version].to_i + case platform + when "macos" + spm( + command: "test", + ) + next + when "ios" devices = case version when 14 [ "iPhone 11 Pro (14.5)", "iPad Pro (11-inch) (3rd generation) (14.5)", - "Apple TV (14.5)", ] when 15 [ "iPhone 11 Pro (15.5)", "iPad Pro (11-inch) (3rd generation) (15.5)", - "Apple TV (15.4)", ] when 16 [ "iPhone 14 Pro (16.2)", "iPad Pro (11-inch) (4th generation) (16.2)", - "Apple TV (16.1)", ] else raise "Unsupported iOS version: #{version}" end - - run_tests( - scheme: "Introspect", - devices: devices, - ensure_devices_found: true, - force_quit_simulator: true, - disable_concurrent_testing: true, - ) + 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 -end -platform :macos do - lane :test do |options| - spm( - command: "test", - ) - end + run_tests( + scheme: "Introspect", + devices: devices, + ensure_devices_found: true, + force_quit_simulator: true, + disable_concurrent_testing: true, + ) end From 54d0aa0d314e649a08b525717ae64da48d51c121 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 14 Mar 2023 23:28:52 +0000 Subject: [PATCH 029/116] Bump minimum Swift version to 5.5 (#209) --- CHANGELOG.md | 3 +++ Package.swift | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03194f1b0..9d1eacce1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ Changelog ## master +- Changed: minimum language version required is now Swift 5.5 (#209) +- Infrastructure: symlink older SDKs to use in newer Xcode versions (#208) + ## [0.2.3] - Fixed: `introspectPagedTabView` on iOS 16 (#200) diff --git a/Package.swift b/Package.swift index 5d715970d..0204e00cf 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.4 +// swift-tools-version:5.5 import PackageDescription From a2b9069cb3738261a2f024a16d1de809b0eb0b01 Mon Sep 17 00:00:00 2001 From: ryohey Date: Mon, 1 May 2023 18:35:19 +0900 Subject: [PATCH 030/116] 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 7e710dadf..c02e48d23 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 031/116] 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 cbc756263..09e8b36ed 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 56f2fcc0c..b33934031 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 dc0de1372..5a14e78ce 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 22a7417af..64ecdf0f4 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 032/116] 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 9d1eacce1..0a7025e67 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 033/116] 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 5344f47e6..ba45cbe28 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 034/116] Bump to 0.3.1 (#221) --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a7025e67..6bcf7f068 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 035/116] All-new implementation, API, and module (#207) --- .github/workflows/cd.yml | 12 +- .github/workflows/ci.yml | 108 ++- .../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 | 305 +++++-- 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 | 133 +-- 117 files changed, 6360 insertions(+), 271 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 293791c8c..9bd0fef3f 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 fb6c3a37e..77050d496 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 + - name: Install xcbeautify + run: brew install xcbeautify + + - if: ${{ matrix.install }} + name: Install Required Runtime run: | - echo "Creating Runtimes folder if needed..." - sudo mkdir -p /Library/Developer/CoreSimulator/Profiles/Runtimes + brew install xcodesorg/made/xcodes + sudo xcodes runtimes install '${{ matrix.runtime }}' - 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: List Available Simulators + run: xcrun simctl list devices available - - name: Install Homebrew dependencies - run: brew install xcbeautify + - 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 - - 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: ${{ 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 21c20d3a3..08f01b9d5 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 80c68beb9..109f41cd7 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 88579ae08..f9cecf9f7 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 eb8789700..000000000 --- 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 13613e3ee..000000000 --- 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 73c00596a..000000000 --- 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 24f9c3f6a..dafb15184 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" + Text(".introspect(.view, ...)") + .lineLimit(1) + .minimumScaleFactor(0.5) + .padding(.horizontal, 12) + .font(.system(.subheadline, design: .monospaced)) } } - } -} - -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 - } - } + .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 5670498b1..580bb9fb6 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 73c00596a..000000000 --- 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 9c257254e..b36954942 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 aebc9608b..825b51713 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 0204e00cf..1e3086163 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 000000000..4b1cf8b1d --- /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 c02e48d23..93d804f07 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 000000000..08012ba89 --- /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 000000000..4f4139843 --- /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 000000000..29970071b --- /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 000000000..332ef5ff6 --- /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 000000000..a89c412f1 --- /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 000000000..09f883d82 --- /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 000000000..bee247691 --- /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 000000000..943cd75df --- /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 000000000..0f9e37ae7 --- /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 000000000..b6d9a97c4 --- /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 000000000..a5d04ac7f --- /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 000000000..7ef7eab12 --- /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 000000000..690748f32 --- /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 000000000..991ac172b --- /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 000000000..cf7a70f92 --- /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 000000000..a8b333da9 --- /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 000000000..a3bbdb1c5 --- /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 000000000..8f5e3de5a --- /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 000000000..4136a1af9 --- /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 000000000..b96fab04d --- /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 000000000..b40d282d4 --- /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 000000000..9a09fd6bd --- /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 000000000..6820bf7ba --- /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 000000000..b0b2b5972 --- /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 000000000..7aa1028e9 --- /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 000000000..41231a017 --- /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 000000000..b6e1083d4 --- /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 000000000..b8e98c603 --- /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 000000000..e8f93d6c6 --- /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 000000000..aa97a11f0 --- /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 000000000..38f017bcd --- /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 000000000..9a56d078f --- /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 000000000..edd6cf7b7 --- /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 000000000..0b7a06d2c --- /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 000000000..2c7d6e17e --- /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 000000000..d4d952a66 --- /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 000000000..1f16a7fc5 --- /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 000000000..0c904eee1 --- /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 000000000..4c6aefdaf --- /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 000000000..629d146cd --- /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 000000000..d95d65b3d --- /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 000000000..579a1162b --- /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 000000000..27a0012cc --- /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 000000000..b530bcd52 --- /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 000000000..bb2e01ebb --- /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 000000000..f4ca18877 --- /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 000000000..c571e4e91 --- /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 000000000..cf3fa7c8f --- /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 000000000..a70085090 --- /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 000000000..a53636126 --- /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 000000000..919434a62 --- /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 000000000..18d981003 --- /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 000000000..5368e40e0 --- /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 000000000..a41c13307 --- /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 000000000..79a8cf602 --- /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 000000000..4bdbb063e --- /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 000000000..87d5d263d --- /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 000000000..e2ba2934a --- /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 000000000..9a8e3bb87 --- /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 000000000..a8824f67e --- /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 000000000..04c941254 --- /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 000000000..d7ce68959 --- /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 000000000..7285072da --- /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 000000000..bca2770a8 --- /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 000000000..6451e1a96 --- /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 000000000..b512f9568 --- /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 000000000..f196abc8a --- /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 000000000..0aede8f8f --- /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 000000000..ee388fe8b --- /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 000000000..90f0389fc --- /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 000000000..e7132b822 --- /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 000000000..87fb2675d --- /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 000000000..b112e95fd --- /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 000000000..07cb69a85 --- /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 000000000..a37bb8302 --- /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 000000000..2b35df7f4 --- /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 000000000..6e89763f4 --- /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 000000000..ae17cbac4 --- /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 000000000..ae9a97c00 --- /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 000000000..5915c6f37 --- /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 000000000..e990ea77b --- /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 000000000..6041a848e --- /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 000000000..682bf7112 --- /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 000000000..b847aab93 --- /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 000000000..32214643f --- /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 000000000..211f83460 --- /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 000000000..c17e28aa7 --- /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 000000000..51ac0893c --- /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 000000000..7b55c03d2 --- /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 000000000..b89047d33 --- /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 000000000..b5126db47 --- /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 000000000..cd877fad9 --- /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 000000000..8ffb641a8 --- /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 000000000..9636d9356 --- /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 000000000..855ebf3f6 --- /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 000000000..a1daddfbf --- /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 000000000..51145970b --- /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 000000000..02cd70166 --- /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 000000000..a8e06508a --- /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 90d8c25fd..0c35de1e8 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 + + 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 - when "tvos" - devices = case version - when 14 - [ - "Apple TV (14.5)", - ] - when 15 - [ - "Apple TV (15.4)", - ] - when 16 - [ - "Apple TV (16.1)", - ] + 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 tvOS version: #{version}" + raise "Unsupported scheme: #{scheme}" end else - raise "Unsupported platform: #{platform}" + 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 - - run_tests( - scheme: "Introspect", - devices: devices, - ensure_devices_found: true, - force_quit_simulator: true, - disable_concurrent_testing: true, - ) 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 036/116] Bump to 0.4.0 (#226) --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bcf7f068..45cbfe48f 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 037/116] 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 a8e06508a..8d3cf7637 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 038/116] Unify `introspect` modifiers (#232) --- Sources/Introspect.swift | 207 +++++++++++++++----------------- Sources/IntrospectionView.swift | 101 ++++++++++------ Sources/PlatformView.swift | 30 ++--- Sources/Voodoo.swift | 9 ++ 4 files changed, 186 insertions(+), 161 deletions(-) create mode 100644 Sources/Voodoo.swift diff --git a/Sources/Introspect.swift b/Sources/Introspect.swift index 08012ba89..f78bec6c3 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~ } + } + + @_spi(Internals) + public func nearestCommonAncestor(with other: Base) -> Base? { + var nearestAncestor: Base? = self~ - while let currentView = nearestAncestor, !other.isDescendant(of: currentView) { - nearestAncestor = currentView.superview + 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 } - } -} + 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 + } -extension PlatformViewController { - fileprivate func receiver( - ofType type: PlatformSpecificViewController.Type - ) -> PlatformSpecificViewController? { - self.hostingView? - .allChildren(ofType: PlatformSpecificViewController.self) - .filter { !($0 is IntrospectionPlatformViewController) } + return commonAncestor + .descendantsBetween(backEntity~, and: frontEntity~) + .compactMap { $0 as? PlatformSpecificEntity } .first } - fileprivate func ancestor( - ofType type: PlatformSpecificViewController.Type - ) -> PlatformSpecificViewController? { - self.parents + fileprivate func ancestor( + ofType type: PlatformSpecificEntity.Type + ) -> PlatformSpecificEntity? { + self.ancestors .lazy - .filter { !($0 is IntrospectionPlatformViewController) } - .compactMap { $0 as? PlatformSpecificViewController } + .compactMap { $0 as? PlatformSpecificEntity } .first } } -extension PlatformViewController { - private var parents: some Sequence { - sequence(first: self, next: \.parent).dropFirst() +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 } - private var hostingView: PlatformViewController? { - self.parents.first(where: { - let type = String(reflecting: type(of: $0)) - return type.hasPrefix("SwiftUI.") && type.contains("Hosting") - }) + @_spi(Internals) + public var descendants: [PlatformViewController] { + children } - 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)) + @_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 } - return result + 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 29970071b..28cacbab5 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 } + 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) - func makeUIView(context: Context) -> UIView { - let view = UIView() + override func viewDidLoad() { + super.viewDidLoad() 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 } - } + 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) - } - #elseif canImport(AppKit) - if introspectionViewController.isViewLoaded { - return selector(introspectionViewController.view) + 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) } - #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 a89c412f1..0863186fa 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 000000000..d6df93528 --- /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 039/116] 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 f78bec6c3..d0979fe34 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 000000000..ea691aa68 --- /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 28cacbab5..096509863 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 09f883d82..9684a5527 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 040/116] 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 dafb15184..70032fcd8 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 d0979fe34..953f999d7 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 0b7a06d2c..1ee82d7c4 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 000000000..226f57886 --- /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 a53636126..5d95579c4 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 000000000..98045023f --- /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 79a8cf602..1570ad9aa 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 07cb69a85..77ff2589f 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 2b35df7f4..5902b7891 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 b847aab93..b18835657 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 51145970b..4cf9a3ac5 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 02cd70166..050f4177e 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() - } - } -} -#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.") + EmptyView() } - .multilineTextAlignment(.center) - .padding() - #if os(macOS) - .fixedSize() - #endif } } diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 0c35de1e8..987b4c2dd 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 041/116] Bump to 0.5.0 (#236) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45cbfe48f..ae5c24737 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 042/116] Fix `SwiftUIIntrospect.podspec` (#237) --- SwiftUIIntrospect.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SwiftUIIntrospect.podspec b/SwiftUIIntrospect.podspec index cf3fa7c8f..d947ab799 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 043/116] Bump to 0.5.1 (#238) --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae5c24737..fc11223ce 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 044/116] 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 ea691aa68..742ed132c 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 - } - if - scope.contains(.receiver), - let entry = entity.receiver(ofType: Entry.self, anchorID: anchorID), - let target = selector(entry) - { - return target + .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(.ancestor), - let entry = entity.ancestor(ofType: Entry.self), - let target = selector(entry) - { - return target - } - return nil - } + ) + } + + 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 } - init(_ selector: @escaping (IntrospectionPlatformViewController, IntrospectionScope, IntrospectionAnchorID) -> Target?) { - self.selector = selector + @_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 096509863..ea969090f 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 045/116] 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 b0b2b5972..a7eec2079 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 7aa1028e9..a8917609b 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 41231a017..e8e8d3af8 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 b6e1083d4..4507dddd1 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 1f16a7fc5..ae229110e 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 046/116] Bump to 0.5.2 (#241) --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc11223ce..f9c8d0fcf 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 047/116] 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 70032fcd8..6d00095b7 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 332ef5ff6..194b59745 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 5cb94cf21..718108649 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 943cd75df..97f65471a 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 0f9e37ae7..bd4ac0c04 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 b6d9a97c4..efc36cf79 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 a5d04ac7f..85acc6462 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 7ef7eab12..f8a328fef 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 690748f32..8d9236c3f 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 991ac172b..ae84ab17c 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 cf7a70f92..f38bae284 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 a8b333da9..09800f941 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 a3bbdb1c5..74f54b519 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 8f5e3de5a..396f65d3a 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 4136a1af9..f64ffc2bb 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 b96fab04d..7661b5e98 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 b40d282d4..5d6f2c3d9 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 9a09fd6bd..2e33b2e59 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 6820bf7ba..8a6412780 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 a7eec2079..bd669a0af 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 a8917609b..0cc431f97 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 e8e8d3af8..b31bf3348 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 4507dddd1..798a2d221 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 b8e98c603..9ff617f87 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 e8f93d6c6..9a85f6346 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 aa97a11f0..b6de6d08e 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 38f017bcd..1a7f988ca 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 edd6cf7b7..91711857e 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 1ee82d7c4..13ade58a2 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 2c7d6e17e..6c5fd01f5 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 d4d952a66..da2ea55c4 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 ae229110e..10012170c 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 0c904eee1..9bf1136e0 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 4c6aefdaf..5534c4b1c 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 629d146cd..b451ae0ff 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 d95d65b3d..ae5c750c1 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 579a1162b..b0e29e3f1 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 27a0012cc..dfd763313 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 b530bcd52..cf77f860b 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 bb2e01ebb..1fcf049f2 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 f4ca18877..494c62a57 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 c571e4e91..2415e8cd8 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 a41c13307..0c736fa33 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 4bdbb063e..acbb3922e 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 87d5d263d..0f550a2d5 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 e2ba2934a..2ba777591 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 9a8e3bb87..abdea4a48 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 a8824f67e..fca60c790 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 04c941254..415692cce 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 d7ce68959..1439d2553 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 7285072da..b7d346198 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 bca2770a8..84a644e0e 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 6451e1a96..abbfa3989 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 b512f9568..cb9b1f80b 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 f196abc8a..642f1344d 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 0aede8f8f..4a8c29a45 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 ee388fe8b..ef09afa35 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 90f0389fc..074a857ba 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 e7132b822..f8767cca3 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 87fb2675d..0c783cf6f 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 b112e95fd..3199b9dbc 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 77ff2589f..ddeff1d5a 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 a37bb8302..f32d4a062 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 5902b7891..b40ac5ddf 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 6e89763f4..51635d840 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 ae17cbac4..d8dc312a3 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 ae9a97c00..903ab89be 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 5915c6f37..4efdd61c8 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 e990ea77b..b5d58baad 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 6041a848e..b1751efdd 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 682bf7112..5170c42f9 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 b18835657..334e1cf96 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 32214643f..7c85709a3 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 211f83460..af5436f79 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 c17e28aa7..01851974f 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 51ac0893c..eac652c0d 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 7b55c03d2..6a1f3230d 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 b89047d33..91243fca3 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 b5126db47..b68ce7e0c 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 cd877fad9..d6b008f2a 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 8ffb641a8..9278124e6 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 9636d9356..d2f589615 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 855ebf3f6..cdf83a9c2 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 a1daddfbf..f27b35190 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 4cf9a3ac5..133029b42 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 8d3cf7637..fd87e48cc 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 } ``` From 01cf328bdbc79218c4016aeec480526f44035c54 Mon Sep 17 00:00:00 2001 From: Kevin Bradley Date: Tue, 6 Jun 2023 05:44:09 -0700 Subject: [PATCH 048/116] Fix `UIColorWell` build error on tvOS 13 (#217) Co-authored-by: Kevin Bradley Co-authored-by: David Roman <2538074+davdroman@users.noreply.github.com> --- Introspect/ViewExtensions.swift | 2 ++ IntrospectTests/UIKitTests.swift | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Introspect/ViewExtensions.swift b/Introspect/ViewExtensions.swift index b33934031..ca8ea4a97 100644 --- a/Introspect/ViewExtensions.swift +++ b/Introspect/ViewExtensions.swift @@ -187,11 +187,13 @@ extension View { } /// Finds a `UIColorWell` from a `SwiftUI.ColorPicker` + #if os(iOS) @available(iOS 14, *) @available(tvOS, unavailable) public func introspectColorWell(customize: @escaping (UIColorWell) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) } + #endif } #endif diff --git a/IntrospectTests/UIKitTests.swift b/IntrospectTests/UIKitTests.swift index 64ecdf0f4..dcb0bfcbf 100644 --- a/IntrospectTests/UIKitTests.swift +++ b/IntrospectTests/UIKitTests.swift @@ -367,6 +367,7 @@ private struct SegmentedControlTestView: View { } } +#if os(iOS) @available(iOS 14.0, *) @available(tvOS, unavailable) private struct ColorWellTestView: View { @@ -380,6 +381,7 @@ private struct ColorWellTestView: View { } } } +#endif import MapKit @available(iOS 14, tvOS 14, *) From c29b50a475120fdfa22573622464a7346aaf99a4 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 6 Jun 2023 14:19:22 +0100 Subject: [PATCH 049/116] Bump to 0.6.0 (#244) --- CHANGELOG.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9c8d0fcf..e6d9da749 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,17 +3,33 @@ Changelog ## master +## [0.6.0] + +### SwiftUIIntrospect + +- Added: iOS 17 / tvOS 17 / macOS 14 compatibility (#243) + +### Introspect + +- Fixed: `UIColorWell` build error on tvOS 13 (#217) + ## [0.5.2] +### SwiftUIIntrospect + - Added: selector overrides (#239) - Changed: optimized ancestor controller selectors (#240) ## [0.5.1] +### SwiftUIIntrospect + - Fixed: SwiftUIIntrospect.podspec (#237) ## [0.5.0] +### SwiftUIIntrospect + - Added: support for custom selectors (#233) - Changed: unified introspect modifiers into one (#232) - Fixed: `searchField` introspection (#234) @@ -21,7 +37,7 @@ Changelog ## [0.4.0] -- Added: all-new implementation, API, and module (#207) +- Added: all-new implementation, API, and module (SwiftUIIntrospect) (#207) ## [0.3.1] From a5a1d7c305e9d9082958de04d521be57a88d431a Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Thu, 8 Jun 2023 01:49:22 +0100 Subject: [PATCH 050/116] Optimize receiver lookup algorithm (#246) --- Sources/Introspect.swift | 61 +++++-------------- Sources/IntrospectionSelector.swift | 26 ++++---- Sources/IntrospectionView.swift | 94 +++++++++++++++++++---------- Sources/Utils.swift | 28 +++++++++ Sources/Voodoo.swift | 9 --- 5 files changed, 117 insertions(+), 101 deletions(-) create mode 100644 Sources/Utils.swift delete mode 100644 Sources/Voodoo.swift diff --git a/Sources/Introspect.swift b/Sources/Introspect.swift index 953f999d7..cd699d7db 100644 --- a/Sources/Introspect.swift +++ b/Sources/Introspect.swift @@ -20,17 +20,18 @@ extension View { customize: @escaping (PlatformSpecificEntity) -> Void ) -> some View { if let platform = platforms.first(where: \.isCurrent) { - let anchorID = IntrospectionAnchorID() + let introspectionViewID = IntrospectionViewID() self.background( IntrospectionAnchorView( - id: anchorID + id: introspectionViewID ) .frame(width: 0, height: 0) ) .overlay( IntrospectionView( - selector: { entity in - (platform.selector ?? .default)(entity, scope ?? viewType.scope, anchorID) + id: introspectionViewID, + selector: { controller in + (platform.selector ?? .default)(controller, scope ?? viewType.scope) }, customize: customize ) @@ -53,9 +54,6 @@ public protocol PlatformEntity: AnyObject { @_spi(Internals) func isDescendant(of other: Base) -> Bool - - @_spi(Internals) - func entityWithTag(_ tag: Int) -> Base? } extension PlatformEntity { @@ -65,8 +63,8 @@ extension PlatformEntity { } @_spi(Internals) - public var allDescendants: [Base] { - self.descendants.reduce([self~]) { $0 + $1.allDescendants~ } + public var allDescendants: some Sequence { + recursiveSequence([self~], children: { $0.descendants~ }).dropFirst() } func nearestCommonAncestor(with other: Base) -> Base? { @@ -79,37 +77,26 @@ extension PlatformEntity { return nearestAncestor } - func descendantsBetween(_ bottomEntity: Base, and topEntity: Base) -> [Base] { - var result: [Base] = [] - var entered = false - - for descendant in self.allDescendants { - if descendant === bottomEntity { - entered = true - } else if descendant === topEntity { - break - } else if entered { - result.append(descendant) - } - } - - return result + func allDescendants(between bottomEntity: Base, and topEntity: Base) -> some Sequence { + self.allDescendants + .lazy + .drop(while: { $0 !== bottomEntity }) + .prefix(while: { $0 !== topEntity }) } func receiver( - ofType type: PlatformSpecificEntity.Type, - anchorID: IntrospectionAnchorID + ofType type: PlatformSpecificEntity.Type ) -> PlatformSpecificEntity? { let frontEntity = self guard - let backEntity = Array(frontEntity.ancestors).last?.entityWithTag(anchorID.hashValue), + let backEntity = frontEntity.introspectionAnchorEntity, let commonAncestor = backEntity.nearestCommonAncestor(with: frontEntity~) else { return nil } return commonAncestor - .descendantsBetween(backEntity~, and: frontEntity~) + .allDescendants(between: backEntity~, and: frontEntity~) .compactMap { $0 as? PlatformSpecificEntity } .first } @@ -134,11 +121,6 @@ extension PlatformView: PlatformEntity { public var descendants: [PlatformView] { subviews } - - @_spi(Internals) - public func entityWithTag(_ tag: Int) -> PlatformView? { - viewWithTag(tag) - } } extension PlatformViewController: PlatformEntity { @@ -156,17 +138,4 @@ extension PlatformViewController: PlatformEntity { 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/IntrospectionSelector.swift b/Sources/IntrospectionSelector.swift index 742ed132c..3b93efa12 100644 --- a/Sources/IntrospectionSelector.swift +++ b/Sources/IntrospectionSelector.swift @@ -6,20 +6,20 @@ public struct IntrospectionSelector { @_spi(Internals) public static func from(_ entryType: Entry.Type, selector: @escaping (Entry) -> Target?) -> Self { .init( - receiverSelector: { controller, anchorID in - controller.as(Entry.self)?.receiver(ofType: Entry.self, anchorID: anchorID).flatMap(selector) + receiverSelector: { controller in + controller.as(Entry.Base.self)?.receiver(ofType: Entry.self).flatMap(selector) }, ancestorSelector: { controller in - controller.as(Entry.self)?.ancestor(ofType: Entry.self).flatMap(selector) + controller.as(Entry.Base.self)?.ancestor(ofType: Entry.self).flatMap(selector) } ) } - private var receiverSelector: (IntrospectionPlatformViewController, IntrospectionAnchorID) -> Target? + private var receiverSelector: (IntrospectionPlatformViewController) -> Target? private var ancestorSelector: (IntrospectionPlatformViewController) -> Target? private init( - receiverSelector: @escaping (IntrospectionPlatformViewController, IntrospectionAnchorID) -> Target?, + receiverSelector: @escaping (IntrospectionPlatformViewController) -> Target?, ancestorSelector: @escaping (IntrospectionPlatformViewController) -> Target? ) { self.receiverSelector = receiverSelector @@ -27,7 +27,7 @@ public struct IntrospectionSelector { } @_spi(Internals) - public func withReceiverSelector(_ selector: @escaping (PlatformViewController, IntrospectionAnchorID) -> Target?) -> Self { + public func withReceiverSelector(_ selector: @escaping (PlatformViewController) -> Target?) -> Self { var copy = self copy.receiverSelector = selector return copy @@ -40,14 +40,10 @@ public struct IntrospectionSelector { return copy } - func callAsFunction( - _ controller: IntrospectionPlatformViewController, - _ scope: IntrospectionScope, - _ anchorID: IntrospectionAnchorID - ) -> Target? { + func callAsFunction(_ controller: IntrospectionPlatformViewController, _ scope: IntrospectionScope) -> Target? { if scope.contains(.receiver), - let target = receiverSelector(controller, anchorID) + let target = receiverSelector(controller) { return target } @@ -62,14 +58,14 @@ public struct IntrospectionSelector { } extension PlatformViewController { - func `as`(_ entityType: Entity.Type) -> (any PlatformEntity)? { - if Entity.Base.self == PlatformView.self { + func `as`(_ baseType: Base.Type) -> (any PlatformEntity)? { + if Base.self == PlatformView.self { #if canImport(UIKit) return viewIfLoaded #elseif canImport(AppKit) return isViewLoaded ? view : nil #endif - } else if Entity.Base.self == PlatformViewController.self { + } else if Base.self == PlatformViewController.self { return self } return nil diff --git a/Sources/IntrospectionView.swift b/Sources/IntrospectionView.swift index ea969090f..681666c79 100644 --- a/Sources/IntrospectionView.swift +++ b/Sources/IntrospectionView.swift @@ -1,7 +1,30 @@ import SwiftUI -@_spi(Internals) -public typealias IntrospectionAnchorID = UUID +typealias IntrospectionViewID = UUID + +fileprivate enum IntrospectionStore { + static var shared: [IntrospectionViewID: Pair] = [:] + + struct Pair { + weak var controller: IntrospectionPlatformViewController? + weak var anchor: IntrospectionAnchorPlatformViewController? + } +} + +extension PlatformEntity { + var introspectionAnchorEntity: Base? { + if let introspectionController = self as? IntrospectionPlatformViewController { + return IntrospectionStore.shared[introspectionController.id]?.anchor~ + } + if + let view = self as? PlatformView, + let introspectionController = view.introspectionController + { + return IntrospectionStore.shared[introspectionController.id]?.anchor?.view~ + } + return nil + } +} /// ⚓️ struct IntrospectionAnchorView: PlatformViewControllerRepresentable { @@ -14,9 +37,9 @@ struct IntrospectionAnchorView: PlatformViewControllerRepresentable { @Binding private var observed: Void // workaround for state changes not triggering view updates - let id: IntrospectionAnchorID + let id: IntrospectionViewID - init(id: IntrospectionAnchorID) { + init(id: IntrospectionViewID) { self._observed = .constant(()) self.id = id } @@ -31,11 +54,9 @@ struct IntrospectionAnchorView: PlatformViewControllerRepresentable { } final class IntrospectionAnchorPlatformViewController: PlatformViewController { - let id: IntrospectionAnchorID - - init(id: IntrospectionAnchorID) { - self.id = id + init(id: IntrospectionViewID) { super.init(nibName: nil, bundle: nil) + IntrospectionStore.shared[id, default: .init()].anchor = self } @available(*, unavailable) @@ -43,24 +64,9 @@ final class IntrospectionAnchorPlatformViewController: PlatformViewController { fatalError("init(coder:) has not been implemented") } - #if canImport(UIKit) - override func viewDidLoad() { - super.viewDidLoad() - view.tag = id.hashValue - } - #elseif canImport(AppKit) - final class TaggableView: NSView { - private var _tag: Int? - override var tag: Int { - get { _tag ?? super.tag } - set { _tag = newValue } - } - } - + #if canImport(AppKit) && !targetEnvironment(macCatalyst) override func loadView() { - let view = TaggableView() - view.tag = id.hashValue - self.view = view + view = NSView() } #endif } @@ -78,14 +84,17 @@ struct IntrospectionView: PlatformViewControllerRepresen @Binding private var observed: Void // workaround for state changes not triggering view updates + private let id: IntrospectionViewID private let selector: (IntrospectionPlatformViewController) -> Target? private let customize: (Target) -> Void init( + id: IntrospectionViewID, selector: @escaping (IntrospectionPlatformViewController) -> Target?, customize: @escaping (Target) -> Void ) { self._observed = .constant(()) + self.id = id self.selector = selector self.customize = customize } @@ -95,7 +104,7 @@ struct IntrospectionView: PlatformViewControllerRepresen } func makePlatformViewController(context: Context) -> IntrospectionPlatformViewController { - let controller = IntrospectionPlatformViewController { controller in + let controller = IntrospectionPlatformViewController(id: id) { controller in guard let target = selector(controller) else { return } @@ -128,9 +137,14 @@ struct IntrospectionView: PlatformViewControllerRepresen } final class IntrospectionPlatformViewController: PlatformViewController { + let id: IntrospectionViewID var handler: (() -> Void)? = nil - fileprivate init(handler: ((IntrospectionPlatformViewController) -> Void)?) { + fileprivate init( + id: IntrospectionViewID, + handler: ((IntrospectionPlatformViewController) -> Void)? + ) { + self.id = id super.init(nibName: nil, bundle: nil) self.handler = { [weak self] in guard let self = self else { @@ -138,6 +152,7 @@ final class IntrospectionPlatformViewController: PlatformViewController { } handler?(self) } + IntrospectionStore.shared[id, default: .init()].controller = self } @available(*, unavailable) @@ -146,13 +161,14 @@ final class IntrospectionPlatformViewController: PlatformViewController { } #if canImport(UIKit) - override func didMove(toParent parent: UIViewController?) { - super.didMove(toParent: parent) + override func viewDidLoad() { + super.viewDidLoad() + view.introspectionController = self handler?() } - override func viewDidLoad() { - super.viewDidLoad() + override func didMove(toParent parent: UIViewController?) { + super.didMove(toParent: parent) handler?() } @@ -168,6 +184,7 @@ final class IntrospectionPlatformViewController: PlatformViewController { #elseif canImport(AppKit) override func loadView() { view = NSView() + view.introspectionController = self } override func viewDidLoad() { @@ -181,3 +198,18 @@ final class IntrospectionPlatformViewController: PlatformViewController { } #endif } + +import ObjectiveC + +extension PlatformView { + fileprivate var introspectionController: IntrospectionPlatformViewController? { + get { + let key = unsafeBitCast(Selector(#function), to: UnsafeRawPointer.self) + return objc_getAssociatedObject(self, key) as? IntrospectionPlatformViewController + } + set { + let key = unsafeBitCast(Selector(#function), to: UnsafeRawPointer.self) + objc_setAssociatedObject(self, key, newValue, .OBJC_ASSOCIATION_ASSIGN) + } + } +} diff --git a/Sources/Utils.swift b/Sources/Utils.swift new file mode 100644 index 000000000..7c1fc017b --- /dev/null +++ b/Sources/Utils.swift @@ -0,0 +1,28 @@ +postfix operator ~ + +postfix func ~ (lhs: LHS) -> T { + lhs as! T +} + +postfix func ~ (lhs: LHS?) -> T? { + lhs as? T +} + +func recursiveSequence(_ sequence: S, children: @escaping (S.Element) -> S) -> AnySequence { + AnySequence { + var mainIterator = sequence.makeIterator() + // Current iterator, or `nil` if all sequences are exhausted: + var iterator: AnyIterator? + + return AnyIterator { + guard let iterator, let element = iterator.next() else { + if let element = mainIterator.next() { + iterator = recursiveSequence(children(element), children: children).makeIterator() + return element + } + return nil + } + return element + } + } +} diff --git a/Sources/Voodoo.swift b/Sources/Voodoo.swift deleted file mode 100644 index d6df93528..000000000 --- a/Sources/Voodoo.swift +++ /dev/null @@ -1,9 +0,0 @@ -postfix operator ~ - -postfix func ~ (lhs: LHS) -> T { - lhs as! T -} - -postfix func ~ (lhs: LHS?) -> T? { - lhs as? T -} From 80356a6b96e74f7885300e4b25753a28a5850b4c Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Thu, 8 Jun 2023 12:40:41 +0100 Subject: [PATCH 051/116] [CI] Retry runtime download on timeout or error (#247) --- .github/workflows/ci.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 77050d496..c308fa8cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,10 +106,16 @@ jobs: run: brew install xcbeautify - if: ${{ matrix.install }} - name: Install Required Runtime - run: | - brew install xcodesorg/made/xcodes - sudo xcodes runtimes install '${{ matrix.runtime }}' + name: Install xcodes + run: brew install xcodesorg/made/xcodes + + - if: ${{ matrix.install }} + name: Install Required Runtime (${{ matrix.runtime }}) + uses: nick-fields/retry@v2 + with: + timeout_minutes: 12 + max_attempts: 3 + command: sudo xcodes runtimes install '${{ matrix.runtime }}' - name: List Available Simulators run: xcrun simctl list devices available From d7b1d71ee3f24f0dc4c655b1bb086f93d8c22339 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sat, 10 Jun 2023 19:50:56 +0100 Subject: [PATCH 052/116] Refactor `.introspect` to use `ViewModifier` (#253) --- Sources/Introspect.swift | 51 ++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/Sources/Introspect.swift b/Sources/Introspect.swift index cd699d7db..245280a6e 100644 --- a/Sources/Introspect.swift +++ b/Sources/Introspect.swift @@ -12,33 +12,50 @@ public struct IntrospectionScope: OptionSet { } extension View { - @ViewBuilder public func introspect( _ viewType: SwiftUIViewType, on platforms: (PlatformViewVersions)..., scope: IntrospectionScope? = nil, customize: @escaping (PlatformSpecificEntity) -> Void ) -> some View { + self.modifier(IntrospectModifier(viewType, platforms: platforms, scope: scope, customize: customize)) + } +} + +struct IntrospectModifier: ViewModifier { + let id = IntrospectionViewID() + let scope: IntrospectionScope + let selector: IntrospectionSelector? + let customize: (PlatformSpecificEntity) -> Void + + init( + _ viewType: SwiftUIViewType, + platforms: [PlatformViewVersions], + scope: IntrospectionScope?, + customize: @escaping (PlatformSpecificEntity) -> Void + ) { + self.scope = scope ?? viewType.scope if let platform = platforms.first(where: \.isCurrent) { - let introspectionViewID = IntrospectionViewID() - self.background( - IntrospectionAnchorView( - id: introspectionViewID + self.selector = platform.selector ?? .default + } else { + self.selector = nil + } + self.customize = customize + } + + func body(content: Content) -> some View { + if let selector { + content + .background( + IntrospectionAnchorView(id: id) + .frame(width: 0, height: 0) ) - .frame(width: 0, height: 0) - ) - .overlay( - IntrospectionView( - id: introspectionViewID, - selector: { controller in - (platform.selector ?? .default)(controller, scope ?? viewType.scope) - }, - customize: customize + .overlay( + IntrospectionView(id: id, selector: { selector($0, scope) }, customize: customize) + .frame(width: 0, height: 0) ) - .frame(width: 0, height: 0) - ) } else { - self + content } } } From 84196bab1c7f05ad8c3c2a5bfb3058b1211e189f Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sat, 10 Jun 2023 21:22:41 +0100 Subject: [PATCH 053/116] Bump to 0.6.1 (#255) --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6d9da749..39c16be0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ Changelog ## master +## [0.6.1] + +- Improved: optimized receiver lookup algorithm (#246) +- Infrastructure: refactored `.introspect` to use `ViewModifier` (#253) +- Infrastructure: retry runtime download on timeout or error on CI (#247) + ## [0.6.0] ### SwiftUIIntrospect From a5039387f2fbb6ea1a1f7915d5eeb05815feb653 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 19 Jun 2023 17:26:10 +0100 Subject: [PATCH 054/116] [CI] Fix iOS/tvOS 13 checks (#257) --- .github/workflows/ci.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c308fa8cf..f56e5d0a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,6 +57,8 @@ jobs: include: - platform: [ios, 13] runtime: iOS 13.7 + os: macos-12 + xcode: 14.2 install: true - platform: [ios, 14] runtime: iOS 14.5 @@ -70,6 +72,8 @@ jobs: - platform: [tvos, 13] runtime: tvOS 13.4 + os: macos-12 + xcode: 14.2 install: true - platform: [tvos, 14] runtime: tvOS 14.5 @@ -117,8 +121,8 @@ jobs: max_attempts: 3 command: sudo xcodes runtimes install '${{ matrix.runtime }}' - - name: List Available Simulators - run: xcrun simctl list devices available + - name: List Available Runtimes and Simulators + run: xcrun simctl list - if: ${{ join(matrix.platform, ' ') != 'macos 11' }} name: Build Showcase From 8d85155bbcb73609b5290f4ea147b017abc2b602 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 19 Jun 2023 18:56:07 +0100 Subject: [PATCH 055/116] Add SPI manifest (#256) --- .spi.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .spi.yml diff --git a/.spi.yml b/.spi.yml new file mode 100644 index 000000000..030c08949 --- /dev/null +++ b/.spi.yml @@ -0,0 +1,4 @@ +version: 1 +builder: + configs: + - documentation_targets: [SwiftUIIntrospect] From 5fc6b0e7a3c3a4b7f12dbe92e4bcb1fdba8c0390 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Wed, 21 Jun 2023 19:27:58 +0100 Subject: [PATCH 056/116] Documentation for new SwiftUIIntrospect module (#258) --- .github/workflows/ci.yml | 2 +- Examples/Showcase/Showcase/Helpers.swift | 17 +++++-- Sources/ViewTypes/Button.swift | 18 ++++++-- Sources/ViewTypes/ColorPicker.swift | 24 ++++++++-- Sources/ViewTypes/DatePicker.swift | 24 ++++++++-- .../DatePickerWithCompactStyle.swift | 25 ++++++++-- .../ViewTypes/DatePickerWithFieldStyle.swift | 21 +++++++-- .../DatePickerWithGraphicalStyleType.swift | 25 ++++++++-- .../DatePickerWithStepperFieldStyle.swift | 21 +++++++-- .../ViewTypes/DatePickerWithWheelStyle.swift | 21 +++++++-- Sources/ViewTypes/Form.swift | 25 ++++++++-- Sources/ViewTypes/FormWithGroupedStyle.swift | 28 ++++++++++- Sources/ViewTypes/List.swift | 27 ++++++++++- Sources/ViewTypes/ListCell.swift | 27 ++++++++++- Sources/ViewTypes/ListWithBorderedStyle.swift | 23 ++++++++-- Sources/ViewTypes/ListWithGroupedStyle.swift | 26 +++++++++-- .../ViewTypes/ListWithInsetGroupedStyle.swift | 26 +++++++++-- Sources/ViewTypes/ListWithInsetStyle.swift | 30 ++++++++++-- Sources/ViewTypes/ListWithSidebarStyle.swift | 30 ++++++++++-- Sources/ViewTypes/NavigationSplitView.swift | 28 ++++++++++- Sources/ViewTypes/NavigationStack.swift | 18 +++++++- .../NavigationViewWithColumnsStyle.swift | 27 ++++++++++- .../NavigationViewWithStackStyle.swift | 19 +++++++- Sources/ViewTypes/PickerWithMenuStyle.swift | 25 ++++++++-- .../ViewTypes/PickerWithSegmentedStyle.swift | 27 ++++++++++- Sources/ViewTypes/PickerWithWheelStyle.swift | 25 ++++++++-- .../ProgressViewWithCircularStyle.swift | 21 ++++++++- .../ProgressViewWithLinearStyle.swift | 21 ++++++++- Sources/ViewTypes/ScrollView.swift | 22 ++++++++- Sources/ViewTypes/SearchField.swift | 22 ++++++++- Sources/ViewTypes/Slider.swift | 24 ++++++++-- Sources/ViewTypes/Stepper.swift | 24 ++++++++-- Sources/ViewTypes/TabView.swift | 23 +++++++++- Sources/ViewTypes/TabViewWithPageStyle.swift | 22 +++++++-- Sources/ViewTypes/Table.swift | 44 ++++++++++++++++-- Sources/ViewTypes/TextEditor.swift | 24 ++++++++-- Sources/ViewTypes/TextField.swift | 22 ++++++++- .../ViewTypes/TextFieldWithVerticalAxis.swift | 26 ++++++++++- Sources/ViewTypes/Toggle.swift | 24 ++++++++-- Sources/ViewTypes/ToggleWithButtonStyle.swift | 21 +++++++-- .../ViewTypes/ToggleWithCheckboxStyle.swift | 21 +++++++-- Sources/ViewTypes/ToggleWithSwitchStyle.swift | 25 ++++++++-- Sources/ViewTypes/View.swift | 19 +++++++- docs/SwiftUIIntrospect.md | 46 +++++++++++++++++-- 44 files changed, 943 insertions(+), 117 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f56e5d0a3..312fc6f23 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -117,7 +117,7 @@ jobs: name: Install Required Runtime (${{ matrix.runtime }}) uses: nick-fields/retry@v2 with: - timeout_minutes: 12 + timeout_minutes: 15 max_attempts: 3 command: sudo xcodes runtimes install '${{ matrix.runtime }}' diff --git a/Examples/Showcase/Showcase/Helpers.swift b/Examples/Showcase/Showcase/Helpers.swift index 580bb9fb6..ae85f5c15 100644 --- a/Examples/Showcase/Showcase/Helpers.swift +++ b/Examples/Showcase/Showcase/Helpers.swift @@ -1,10 +1,17 @@ import SwiftUI extension View { - @ViewBuilder - func modifier( - @ViewBuilder transform: (Self) -> TransformedView - ) -> TransformedView { - transform(self) + /// Modify a view with a `ViewBuilder` closure. + /// + /// This represents a streamlining of the + /// [`modifier`](https://developer.apple.com/documentation/swiftui/view/modifier(_:)) + + /// [`ViewModifier`](https://developer.apple.com/documentation/swiftui/viewmodifier) pattern. + /// + /// - Note: Useful only when you don't need to reuse the closure. + /// If you do, turn the closure into a proper modifier. + public func modifier( + @ViewBuilder _ modifier: (Self) -> ModifiedContent + ) -> ModifiedContent { + modifier(self) } } diff --git a/Sources/ViewTypes/Button.swift b/Sources/ViewTypes/Button.swift index 718108649..108ba6f30 100644 --- a/Sources/ViewTypes/Button.swift +++ b/Sources/ViewTypes/Button.swift @@ -1,10 +1,22 @@ -#if os(macOS) import SwiftUI -// MARK: SwiftUI.Button - +/// An abstract representation of the `Button` type in SwiftUI. +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// Button("Action", action: {}) +/// #if os(macOS) +/// .introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSButton +/// } +/// #endif +/// } +/// } +/// ``` public struct ButtonType: IntrospectableViewType {} +#if os(macOS) extension IntrospectableViewType where Self == ButtonType { public static var button: Self { .init() } } diff --git a/Sources/ViewTypes/ColorPicker.swift b/Sources/ViewTypes/ColorPicker.swift index 97f65471a..d1970d547 100644 --- a/Sources/ViewTypes/ColorPicker.swift +++ b/Sources/ViewTypes/ColorPicker.swift @@ -1,10 +1,28 @@ -#if !os(tvOS) import SwiftUI -// MARK: SwiftUI.ColorPicker - +/// An abstract representation of the `ColorPicker` type in SwiftUI. +/// +/// ```swift +/// struct ContentView: View { +/// @State var color = Color.red +/// +/// var body: some View { +/// ColorPicker("Pick a color", selection: $color) +/// #if os(iOS) +/// .introspect(.colorPicker, on: .iOS(.v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIColorPicker +/// } +/// #elseif os(macOS) +/// .introspect(.colorPicker, on: .macOS(.v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSColorPicker +/// } +/// #endif +/// } +/// } +/// ``` public struct ColorPickerType: IntrospectableViewType {} +#if !os(tvOS) extension IntrospectableViewType where Self == ColorPickerType { public static var colorPicker: Self { .init() } } diff --git a/Sources/ViewTypes/DatePicker.swift b/Sources/ViewTypes/DatePicker.swift index bd4ac0c04..e9a48f428 100644 --- a/Sources/ViewTypes/DatePicker.swift +++ b/Sources/ViewTypes/DatePicker.swift @@ -1,10 +1,28 @@ -#if os(iOS) || os(macOS) import SwiftUI -// MARK: SwiftUI.DatePicker - +/// An abstract representation of the `DatePicker` type in SwiftUI. +/// +/// ```swift +/// struct ContentView: View { +/// @State var date = Date() +/// +/// var body: some View { +/// DatePicker("Pick a date", selection: $date) +/// #if os(iOS) +/// .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIDatePicker +/// } +/// #elseif os(macOS) +/// .introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSDatePicker +/// } +/// #endif +/// } +/// } +/// ``` public struct DatePickerType: IntrospectableViewType {} +#if os(iOS) || os(macOS) extension IntrospectableViewType where Self == DatePickerType { public static var datePicker: Self { .init() } } diff --git a/Sources/ViewTypes/DatePickerWithCompactStyle.swift b/Sources/ViewTypes/DatePickerWithCompactStyle.swift index efc36cf79..972766057 100644 --- a/Sources/ViewTypes/DatePickerWithCompactStyle.swift +++ b/Sources/ViewTypes/DatePickerWithCompactStyle.swift @@ -1,14 +1,33 @@ -#if os(iOS) || os(macOS) import SwiftUI -// MARK: SwiftUI.DatePicker { ... }.datePickerStyle(.compact) - +/// An abstract representation of the `DatePicker` type in SwiftUI, with `.compact` style. +/// +/// ```swift +/// struct ContentView: View { +/// @State var date = Date() +/// +/// var body: some View { +/// DatePicker("Pick a date", selection: $date) +/// .datePickerStyle(.compact) +/// #if os(iOS) +/// .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIDatePicker +/// } +/// #elseif os(macOS) +/// .introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSDatePicker +/// } +/// #endif +/// } +/// } +/// ``` public struct DatePickerWithCompactStyleType: IntrospectableViewType { public enum Style { case compact } } +#if os(iOS) || os(macOS) extension IntrospectableViewType where Self == DatePickerWithCompactStyleType { public static func datePicker(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/DatePickerWithFieldStyle.swift b/Sources/ViewTypes/DatePickerWithFieldStyle.swift index 85acc6462..b47f1930a 100644 --- a/Sources/ViewTypes/DatePickerWithFieldStyle.swift +++ b/Sources/ViewTypes/DatePickerWithFieldStyle.swift @@ -1,14 +1,29 @@ -#if os(macOS) import SwiftUI -// MARK: SwiftUI.DatePicker { ... }.datePickerStyle(.field) - +/// An abstract representation of the `DatePicker` type in SwiftUI, with `.field` style. +/// +/// ```swift +/// struct ContentView: View { +/// @State var date = Date() +/// +/// var body: some View { +/// DatePicker("Pick a date", selection: $date) +/// .datePickerStyle(.field) +/// #if os(macOS) +/// .introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSDatePicker +/// } +/// #endif +/// } +/// } +/// ``` public struct DatePickerWithFieldStyleType: IntrospectableViewType { public enum Style { case field } } +#if os(macOS) extension IntrospectableViewType where Self == DatePickerWithFieldStyleType { public static func datePicker(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/DatePickerWithGraphicalStyleType.swift b/Sources/ViewTypes/DatePickerWithGraphicalStyleType.swift index f8a328fef..b292ce613 100644 --- a/Sources/ViewTypes/DatePickerWithGraphicalStyleType.swift +++ b/Sources/ViewTypes/DatePickerWithGraphicalStyleType.swift @@ -1,14 +1,33 @@ -#if os(iOS) || os(macOS) import SwiftUI -// MARK: SwiftUI.DatePicker { ... }.datePickerStyle(.graphical) - +/// An abstract representation of the `DatePicker` type in SwiftUI, with `.graphical` style. +/// +/// ```swift +/// struct ContentView: View { +/// @State var date = Date() +/// +/// var body: some View { +/// DatePicker("Pick a date", selection: $date) +/// .datePickerStyle(.graphical) +/// #if os(iOS) +/// .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIDatePicker +/// } +/// #elseif os(macOS) +/// .introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSDatePicker +/// } +/// #endif +/// } +/// } +/// ``` public struct DatePickerWithGraphicalStyleType: IntrospectableViewType { public enum Style { case graphical } } +#if os(iOS) || os(macOS) extension IntrospectableViewType where Self == DatePickerWithGraphicalStyleType { public static func datePicker(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift b/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift index 8d9236c3f..911fc2c96 100644 --- a/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift +++ b/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift @@ -1,14 +1,29 @@ -#if os(macOS) import SwiftUI -// MARK: SwiftUI.DatePicker { ... }.datePickerStyle(.stepperField) - +/// An abstract representation of the `DatePicker` type in SwiftUI, with `.stepperField` style. +/// +/// ```swift +/// struct ContentView: View { +/// @State var date = Date() +/// +/// var body: some View { +/// DatePicker("Pick a date", selection: $date) +/// .datePickerStyle(.stepperField) +/// #if os(macOS) +/// .introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSDatePicker +/// } +/// #endif +/// } +/// } +/// ``` public struct DatePickerWithStepperFieldStyleType: IntrospectableViewType { public enum Style { case stepperField } } +#if os(macOS) extension IntrospectableViewType where Self == DatePickerWithStepperFieldStyleType { public static func datePicker(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/DatePickerWithWheelStyle.swift b/Sources/ViewTypes/DatePickerWithWheelStyle.swift index ae84ab17c..6f3ced386 100644 --- a/Sources/ViewTypes/DatePickerWithWheelStyle.swift +++ b/Sources/ViewTypes/DatePickerWithWheelStyle.swift @@ -1,14 +1,29 @@ -#if os(iOS) import SwiftUI -// MARK: SwiftUI.DatePicker { ... }.datePickerStyle(.wheel) - +/// An abstract representation of the `DatePicker` type in SwiftUI, with `.wheel` style. +/// +/// ```swift +/// struct ContentView: View { +/// @State var date = Date() +/// +/// var body: some View { +/// DatePicker("Pick a date", selection: $date) +/// .datePickerStyle(.wheel) +/// #if os(iOS) +/// .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIDatePicker +/// } +/// #endif +/// } +/// } +/// ``` public struct DatePickerWithWheelStyleType: IntrospectableViewType { public enum Style { case wheel } } +#if os(iOS) extension IntrospectableViewType where Self == DatePickerWithWheelStyleType { public static func datePicker(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/Form.swift b/Sources/ViewTypes/Form.swift index f38bae284..16cbd2e7b 100644 --- a/Sources/ViewTypes/Form.swift +++ b/Sources/ViewTypes/Form.swift @@ -1,10 +1,29 @@ -#if !os(macOS) import SwiftUI -// MARK: SwiftUI.Form - +/// An abstract representation of the `Form` type in SwiftUI. +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// Form { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// #if os(iOS) || os(tvOS) +/// .introspect(.form, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UITableView +/// } +/// .introspect(.form, on: .iOS(.v16, .v17)) { +/// print(type(of: $0)) // UICollectionView +/// } +/// #endif +/// } +/// } +/// ``` public struct FormType: IntrospectableViewType {} +#if !os(macOS) extension IntrospectableViewType where Self == FormType { public static var form: Self { .init() } } diff --git a/Sources/ViewTypes/FormWithGroupedStyle.swift b/Sources/ViewTypes/FormWithGroupedStyle.swift index 09800f941..3a35b76d2 100644 --- a/Sources/ViewTypes/FormWithGroupedStyle.swift +++ b/Sources/ViewTypes/FormWithGroupedStyle.swift @@ -1,7 +1,31 @@ import SwiftUI -// MARK: SwiftUI.Form { ... }.formStyle(.grouped) - +/// An abstract representation of the `Form` type in SwiftUI, with `.grouped` style. +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// Form { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// .formStyle(.grouped) +/// #if os(iOS) || os(tvOS) +/// .introspect(.form(style: .grouped), on: .iOS(.v16, .v17)) { +/// print(type(of: $0)) // UITableView +/// } +/// .introspect(.form(style: .grouped), on: .tvOS(.v16, .v17)) { +/// print(type(of: $0)) // UICollectionView +/// } +/// #elseif os(macOS) +/// .introspect(.form(style: .grouped), on: .macOS(.v13, .v14)) { +/// print(type(of: $0)) // NSScrollView +/// } +/// #endif +/// } +/// } +/// ``` public struct FormWithGroupedStyleType: IntrospectableViewType { public enum Style { case grouped diff --git a/Sources/ViewTypes/List.swift b/Sources/ViewTypes/List.swift index 74f54b519..8badfb0e1 100644 --- a/Sources/ViewTypes/List.swift +++ b/Sources/ViewTypes/List.swift @@ -1,7 +1,30 @@ import SwiftUI -// MARK: SwiftUI.List - +/// An abstract representation of the `List` type in SwiftUI. +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// List { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// #if os(iOS) || os(tvOS) +/// .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UITableView +/// } +/// .introspect(.list, on: .iOS(.v16, .v17)) { +/// print(type(of: $0)) // UICollectionView +/// } +/// #elseif os(macOS) +/// .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSTableView +/// } +/// #endif +/// } +/// } +/// ``` public struct ListType: IntrospectableViewType { public enum Style { case plain diff --git a/Sources/ViewTypes/ListCell.swift b/Sources/ViewTypes/ListCell.swift index 396f65d3a..f1f9e6625 100644 --- a/Sources/ViewTypes/ListCell.swift +++ b/Sources/ViewTypes/ListCell.swift @@ -1,7 +1,30 @@ import SwiftUI -// MARK: SwiftUI.List { Cell() } - +/// An abstract representation of a `List` cell type in SwiftUI. +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// List { +/// ForEach(1...3, id: \.self) { int in +/// Text("Item \(int)") +/// #if os(iOS) || os(tvOS) +/// .introspect(.listCell, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UITableViewCell +/// } +/// .introspect(.listCell, on: .iOS(.v16, .v17)) { +/// print(type(of: $0)) // UICollectionViewCell +/// } +/// #elseif os(macOS) +/// .introspect(.listCell, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSTableCellView +/// } +/// #endif +/// } +/// } +/// } +/// } +/// ``` public struct ListCellType: IntrospectableViewType { public var scope: IntrospectionScope { .ancestor } } diff --git a/Sources/ViewTypes/ListWithBorderedStyle.swift b/Sources/ViewTypes/ListWithBorderedStyle.swift index f64ffc2bb..252e75492 100644 --- a/Sources/ViewTypes/ListWithBorderedStyle.swift +++ b/Sources/ViewTypes/ListWithBorderedStyle.swift @@ -1,14 +1,31 @@ -#if os(macOS) import SwiftUI -// MARK: SwiftUI.List { ... }.listStyle(.bordered) - +/// An abstract representation of the `List` type in SwiftUI, with `.bordered` style. +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// List { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// .listStyle(.bordered) +/// #if os(macOS) +/// .introspect(.list(style: .bordered), on: .macOS(.v12, .v13, .v14)) { +/// print(type(of: $0)) // NSTableView +/// } +/// #endif +/// } +/// } +/// ``` public struct ListWithBorderedStyleType: IntrospectableViewType { public enum Style { case bordered } } +#if os(macOS) extension IntrospectableViewType where Self == ListWithBorderedStyleType { public static func list(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/ListWithGroupedStyle.swift b/Sources/ViewTypes/ListWithGroupedStyle.swift index 7661b5e98..2ebdbeaaa 100644 --- a/Sources/ViewTypes/ListWithGroupedStyle.swift +++ b/Sources/ViewTypes/ListWithGroupedStyle.swift @@ -1,14 +1,34 @@ -#if os(iOS) || os(tvOS) import SwiftUI -// MARK: SwiftUI.List { ... }.listStyle(.grouped) - +/// An abstract representation of the `List` type in SwiftUI, with `.grouped` style. +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// List { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// .listStyle(.grouped) +/// #if os(iOS) || os(tvOS) +/// .introspect(.list(style: .grouped), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UITableView +/// } +/// .introspect(.list(style: .grouped), on: .iOS(.v16, .v17)) { +/// print(type(of: $0)) // UICollectionView +/// } +/// #endif +/// } +/// } +/// ``` public struct ListWithGroupedStyleType: IntrospectableViewType { public enum Style { case grouped } } +#if os(iOS) || os(tvOS) extension IntrospectableViewType where Self == ListWithGroupedStyleType { public static func list(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/ListWithInsetGroupedStyle.swift b/Sources/ViewTypes/ListWithInsetGroupedStyle.swift index 5d6f2c3d9..5e26727e9 100644 --- a/Sources/ViewTypes/ListWithInsetGroupedStyle.swift +++ b/Sources/ViewTypes/ListWithInsetGroupedStyle.swift @@ -1,14 +1,34 @@ -#if os(iOS) import SwiftUI -// MARK: SwiftUI.List { ... }.listStyle(.insetGrouped) - +/// An abstract representation of the `List` type in SwiftUI, with `.insetGrouped` style. +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// List { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// .listStyle(.insetGrouped) +/// #if os(iOS) +/// .introspect(.list(style: .insetGrouped), on: .iOS(.v14, .v15)) { +/// print(type(of: $0)) // UITableView +/// } +/// .introspect(.list(style: .insetGrouped), on: .iOS(.v16, .v17)) { +/// print(type(of: $0)) // UICollectionView +/// } +/// #endif +/// } +/// } +/// ``` public struct ListWithInsetGroupedStyleType: IntrospectableViewType { public enum Style { case insetGrouped } } +#if os(iOS) extension IntrospectableViewType where Self == ListWithInsetGroupedStyleType { public static func list(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/ListWithInsetStyle.swift b/Sources/ViewTypes/ListWithInsetStyle.swift index 2e33b2e59..62269d352 100644 --- a/Sources/ViewTypes/ListWithInsetStyle.swift +++ b/Sources/ViewTypes/ListWithInsetStyle.swift @@ -1,14 +1,38 @@ -#if os(iOS) || os(macOS) import SwiftUI -// MARK: SwiftUI.List { ... }.listStyle(.inset) - +/// An abstract representation of the `List` type in SwiftUI, with `.inset` style. +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// List { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// .listStyle(.inset) +/// #if os(iOS) +/// .introspect(.list(style: .inset), on: .iOS(.v14, .v15)) { +/// print(type(of: $0)) // UITableView +/// } +/// .introspect(.list(style: .inset), on: .iOS(.v16, .v17)) { +/// print(type(of: $0)) // UICollectionView +/// } +/// #elseif os(macOS) +/// .introspect(.list(style: .inset), on: .macOS(.v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSTableView +/// } +/// #endif +/// } +/// } +/// ``` public struct ListWithInsetStyleType: IntrospectableViewType { public enum Style { case inset } } +#if os(iOS) || os(macOS) extension IntrospectableViewType where Self == ListWithInsetStyleType { public static func list(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/ListWithSidebarStyle.swift b/Sources/ViewTypes/ListWithSidebarStyle.swift index 8a6412780..fede61ff1 100644 --- a/Sources/ViewTypes/ListWithSidebarStyle.swift +++ b/Sources/ViewTypes/ListWithSidebarStyle.swift @@ -1,14 +1,38 @@ -#if os(iOS) || os(macOS) import SwiftUI -// MARK: SwiftUI.List { ... }.listStyle(.sidebar) - +/// An abstract representation of the `List` type in SwiftUI, with `.sidebar` style. +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// List { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// .listStyle(.sidebar) +/// #if os(iOS) +/// .introspect(.list(style: .sidebar), on: .iOS(.v14, .v15)) { +/// print(type(of: $0)) // UITableView +/// } +/// .introspect(.list(style: .sidebar), on: .iOS(.v16, .v17)) { +/// print(type(of: $0)) // UICollectionView +/// } +/// #elseif os(macOS) +/// .introspect(.list(style: .sidebar), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSTableView +/// } +/// #endif +/// } +/// } +/// ``` public struct ListWithSidebarStyleType: IntrospectableViewType { public enum Style { case sidebar } } +#if os(iOS) || os(macOS) extension IntrospectableViewType where Self == ListWithSidebarStyleType { public static func list(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/NavigationSplitView.swift b/Sources/ViewTypes/NavigationSplitView.swift index bd669a0af..5673b09df 100644 --- a/Sources/ViewTypes/NavigationSplitView.swift +++ b/Sources/ViewTypes/NavigationSplitView.swift @@ -1,7 +1,31 @@ import SwiftUI -// MARK: SwiftUI.NavigationSplitView - +/// An abstract representation of the `NavigationSplitView` type in SwiftUI. +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// NavigationSplitView { +/// Text("Root") +/// } detail: { +/// Text("Detail") +/// } +/// #if os(iOS) +/// .introspect(.navigationSplitView, on: .iOS(.v16, .v17)) { +/// print(type(of: $0)) // UISplitViewController +/// } +/// #elseif os(tvOS) +/// .introspect(.navigationSplitView, on: .tvOS(.v16, .v17)) { +/// print(type(of: $0)) // UINavigationController +/// } +/// #elseif os(macOS) +/// .introspect(.navigationSplitView, on: .macOS(.v13, .v14)) { +/// print(type(of: $0)) // NSSplitView +/// } +/// #endif +/// } +/// } +/// ``` public struct NavigationSplitViewType: IntrospectableViewType {} extension IntrospectableViewType where Self == NavigationSplitViewType { diff --git a/Sources/ViewTypes/NavigationStack.swift b/Sources/ViewTypes/NavigationStack.swift index 0cc431f97..d4ecfad20 100644 --- a/Sources/ViewTypes/NavigationStack.swift +++ b/Sources/ViewTypes/NavigationStack.swift @@ -1,7 +1,21 @@ import SwiftUI -// MARK: SwiftUI.NavigationStack - +/// An abstract representation of the `NavigationStack` type in SwiftUI. +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// NavigationStack { +/// Text("Root") +/// } +/// #if os(iOS) || os(tvOS) +/// .introspect(.navigationStack, on: .iOS(.v16, .v17), .tvOS(.v16, .v17)) { +/// print(type(of: $0)) // UINavigationController +/// } +/// #endif +/// } +/// } +/// ``` public struct NavigationStackType: IntrospectableViewType {} extension IntrospectableViewType where Self == NavigationStackType { diff --git a/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift b/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift index b31bf3348..c21639cac 100644 --- a/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift +++ b/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift @@ -1,7 +1,30 @@ import SwiftUI -// MARK: SwiftUI.NavigationView { ... }.navigationViewStyle(.columns) - +/// An abstract representation of the `NavigationView` type in SwiftUI, with `.columns` style. +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// NavigationView { +/// Text("Root") +/// } +/// .navigationViewStyle(DoubleColumnNavigationViewStyle()) +/// #if os(iOS) +/// .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UISplitViewController +/// } +/// #elseif os(tvOS) +/// .introspect(.navigationView(style: .columns), on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UINavigationController +/// } +/// #elseif os(macOS) +/// .introspect(.navigationView(style: .columns), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSSplitView +/// } +/// #endif +/// } +/// } +/// ``` public struct NavigationViewWithColumnsStyleType: IntrospectableViewType { public enum Style { case columns diff --git a/Sources/ViewTypes/NavigationViewWithStackStyle.swift b/Sources/ViewTypes/NavigationViewWithStackStyle.swift index 798a2d221..c83d357ff 100644 --- a/Sources/ViewTypes/NavigationViewWithStackStyle.swift +++ b/Sources/ViewTypes/NavigationViewWithStackStyle.swift @@ -1,7 +1,22 @@ import SwiftUI -// MARK: SwiftUI.NavigationView { ... }.navigationViewStyle(.stack) - +/// An abstract representation of the `NavigationView` type in SwiftUI, with `.stack` style. +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// NavigationView { +/// Text("Root") +/// } +/// .navigationViewStyle(.stack) +/// #if os(iOS) || os(tvOS) +/// .introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UINavigationController +/// } +/// #endif +/// } +/// } +/// ``` public struct NavigationViewWithStackStyleType: IntrospectableViewType { public enum Style { case stack diff --git a/Sources/ViewTypes/PickerWithMenuStyle.swift b/Sources/ViewTypes/PickerWithMenuStyle.swift index 9ff617f87..dc804df75 100644 --- a/Sources/ViewTypes/PickerWithMenuStyle.swift +++ b/Sources/ViewTypes/PickerWithMenuStyle.swift @@ -1,14 +1,33 @@ -#if os(macOS) import SwiftUI -// MARK: SwiftUI.Picker { ... }.pickerStyle(.menu) - +/// An abstract representation of the `Picker` type in SwiftUI, with `.menu` style. +/// +/// ```swift +/// struct ContentView: View { +/// @State var selection = "1" +/// +/// var body: some View { +/// Picker("Pick a number", selection: $selection) { +/// 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, .v14)) { +/// print(type(of: $0)) // NSPopUpButton +/// } +/// #endif +/// } +/// } +/// ``` public struct PickerWithMenuStyleType: IntrospectableViewType { public enum Style { case menu } } +#if os(macOS) extension IntrospectableViewType where Self == PickerWithMenuStyleType { public static func picker(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/PickerWithSegmentedStyle.swift b/Sources/ViewTypes/PickerWithSegmentedStyle.swift index 9a85f6346..20ecfecd0 100644 --- a/Sources/ViewTypes/PickerWithSegmentedStyle.swift +++ b/Sources/ViewTypes/PickerWithSegmentedStyle.swift @@ -1,7 +1,30 @@ import SwiftUI -// MARK: SwiftUI.Picker { ... }.pickerStyle(.segmented) - +/// An abstract representation of the `Picker` type in SwiftUI, with `.segmented` style. +/// +/// ```swift +/// struct ContentView: View { +/// @State var selection = "1" +/// +/// var body: some View { +/// Picker("Pick a number", selection: $selection) { +/// 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, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UISegmentedControl +/// } +/// #elseif os(macOS) +/// .introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSSegmentedControl +/// } +/// #endif +/// } +/// } +/// ``` public struct PickerWithSegmentedStyleType: IntrospectableViewType { public enum Style { case segmented diff --git a/Sources/ViewTypes/PickerWithWheelStyle.swift b/Sources/ViewTypes/PickerWithWheelStyle.swift index b6de6d08e..587f2d413 100644 --- a/Sources/ViewTypes/PickerWithWheelStyle.swift +++ b/Sources/ViewTypes/PickerWithWheelStyle.swift @@ -1,14 +1,33 @@ -#if os(iOS) import SwiftUI -// MARK: SwiftUI.Picker { ... }.pickerStyle(.wheel) - +/// An abstract representation of the `Picker` type in SwiftUI, with `.wheel` style. +/// +/// ```swift +/// struct ContentView: View { +/// @State var selection = "1" +/// +/// var body: some View { +/// Picker("Pick a number", selection: $selection) { +/// 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, .v17)) { +/// print(type(of: $0)) // UIPickerView +/// } +/// #endif +/// } +/// } +/// ``` public struct PickerWithWheelStyleType: IntrospectableViewType { public enum Style { case wheel } } +#if os(iOS) extension IntrospectableViewType where Self == PickerWithWheelStyleType { public static func picker(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/ProgressViewWithCircularStyle.swift b/Sources/ViewTypes/ProgressViewWithCircularStyle.swift index 1a7f988ca..0f4bcae9f 100644 --- a/Sources/ViewTypes/ProgressViewWithCircularStyle.swift +++ b/Sources/ViewTypes/ProgressViewWithCircularStyle.swift @@ -1,7 +1,24 @@ import SwiftUI -// MARK: SwiftUI.ProgressView().progressViewStyle(.circular) - +/// An abstract representation of the `ProgressView` type in SwiftUI, with `.circular` style. +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// ProgressView(value: 0.5) +/// .progressViewStyle(.circular) +/// #if os(iOS) || os(tvOS) +/// .introspect(.progressView(style: .circular), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIActivityIndicatorView +/// } +/// #elseif os(macOS) +/// .introspect(.progressView(style: .circular), on: .macOS(.v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSProgressIndicator +/// } +/// #endif +/// } +/// } +/// ``` public struct ProgressViewWithCircularStyleType: IntrospectableViewType { public enum Style { case circular diff --git a/Sources/ViewTypes/ProgressViewWithLinearStyle.swift b/Sources/ViewTypes/ProgressViewWithLinearStyle.swift index 093f26c01..d9c2dd365 100644 --- a/Sources/ViewTypes/ProgressViewWithLinearStyle.swift +++ b/Sources/ViewTypes/ProgressViewWithLinearStyle.swift @@ -1,7 +1,24 @@ import SwiftUI -// MARK: SwiftUI.ProgressView().progressViewStyle(.linear) - +/// An abstract representation of the `ProgressView` type in SwiftUI, with `.linear` style. +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// ProgressView(value: 0.5) +/// .progressViewStyle(.linear) +/// #if os(iOS) || os(tvOS) +/// .introspect(.progressView(style: .linear), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIProgressView +/// } +/// #elseif os(macOS) +/// .introspect(.progressView(style: .linear), on: .macOS(.v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSProgressIndicator +/// } +/// #endif +/// } +/// } +/// ``` public struct ProgressViewWithLinearStyleType: IntrospectableViewType { public enum Style { case linear diff --git a/Sources/ViewTypes/ScrollView.swift b/Sources/ViewTypes/ScrollView.swift index 91711857e..b58e79c97 100644 --- a/Sources/ViewTypes/ScrollView.swift +++ b/Sources/ViewTypes/ScrollView.swift @@ -1,7 +1,25 @@ import SwiftUI -// MARK: SwiftUI.ScrollView - +/// An abstract representation of the `ScrollView` type in SwiftUI. +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// ScrollView { +/// Text("Item") +/// } +/// #if os(iOS) || os(tvOS) +/// .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIScrollView +/// } +/// #elseif os(macOS) +/// .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSScrollView +/// } +/// #endif +/// } +/// } +/// ``` public struct ScrollViewType: IntrospectableViewType {} extension IntrospectableViewType where Self == ScrollViewType { diff --git a/Sources/ViewTypes/SearchField.swift b/Sources/ViewTypes/SearchField.swift index 13ade58a2..8e381648d 100644 --- a/Sources/ViewTypes/SearchField.swift +++ b/Sources/ViewTypes/SearchField.swift @@ -1,7 +1,25 @@ import SwiftUI -// MARK: SwiftUI.View.searchable(...) - +/// An abstract representation of the search field displayed via the `.searchable` modifier in SwiftUI. +/// +/// ```swift +/// struct ContentView: View { +/// @State var searchTerm = "" +/// +/// var body: some View { +/// NavigationView { +/// Text("Root") +/// .searchable(text: $searchTerm) +/// } +/// .navigationViewStyle(.stack) +/// #if os(iOS) || os(tvOS) +/// .introspect(.searchField, on: .iOS(.v15, .v16, .v17), .tvOS(.v15, .v16, .v17)) { +/// print(type(of: $0)) // UISearchBar +/// } +/// #endif +/// } +/// } +/// ``` public struct SearchFieldType: IntrospectableViewType {} extension IntrospectableViewType where Self == SearchFieldType { diff --git a/Sources/ViewTypes/Slider.swift b/Sources/ViewTypes/Slider.swift index 6c5fd01f5..16747546b 100644 --- a/Sources/ViewTypes/Slider.swift +++ b/Sources/ViewTypes/Slider.swift @@ -1,10 +1,28 @@ -#if !os(tvOS) import SwiftUI -// MARK: SwiftUI.Slider - +/// An abstract representation of the `Slider` type in SwiftUI. +/// +/// ```swift +/// struct ContentView: View { +/// @State var selection = 0.5 +/// +/// var body: some View { +/// Slider(value: $selection, in: 0...1) +/// #if os(iOS) +/// .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UISlider +/// } +/// #elseif os(macOS) +/// .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSSlider +/// } +/// #endif +/// } +/// } +/// ``` public struct SliderType: IntrospectableViewType {} +#if !os(tvOS) extension IntrospectableViewType where Self == SliderType { public static var slider: Self { .init() } } diff --git a/Sources/ViewTypes/Stepper.swift b/Sources/ViewTypes/Stepper.swift index da2ea55c4..61b584e97 100644 --- a/Sources/ViewTypes/Stepper.swift +++ b/Sources/ViewTypes/Stepper.swift @@ -1,10 +1,28 @@ -#if !os(tvOS) import SwiftUI -// MARK: SwiftUI.Stepper - +/// An abstract representation of the `Stepper` type in SwiftUI. +/// +/// ```swift +/// struct ContentView: View { +/// @State var selection = 5 +/// +/// var body: some View { +/// Stepper("Select a number", value: $selection, in: 0...10) +/// #if os(iOS) +/// .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIStepper +/// } +/// #elseif os(macOS) +/// .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSStepper +/// } +/// #endif +/// } +/// } +/// ``` public struct StepperType: IntrospectableViewType {} +#if !os(tvOS) extension IntrospectableViewType where Self == StepperType { public static var stepper: Self { .init() } } diff --git a/Sources/ViewTypes/TabView.swift b/Sources/ViewTypes/TabView.swift index 10012170c..5966130e8 100644 --- a/Sources/ViewTypes/TabView.swift +++ b/Sources/ViewTypes/TabView.swift @@ -1,7 +1,26 @@ import SwiftUI -// MARK: SwiftUI.TabView - +/// An abstract representation of the `TabView` type in SwiftUI. +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// TabView { +/// Text("Tab 1").tabItem { Text("Tab 1") } +/// Text("Tab 2").tabItem { Text("Tab 2") } +/// } +/// #if os(iOS) || os(tvOS) +/// .introspect(.tabView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UITabBarController +/// } +/// #elseif os(macOS) +/// .introspect(.tabView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSTabView +/// } +/// #endif +/// } +/// } +/// ``` public struct TabViewType: IntrospectableViewType {} extension IntrospectableViewType where Self == TabViewType { diff --git a/Sources/ViewTypes/TabViewWithPageStyle.swift b/Sources/ViewTypes/TabViewWithPageStyle.swift index 9bf1136e0..62cefffb5 100644 --- a/Sources/ViewTypes/TabViewWithPageStyle.swift +++ b/Sources/ViewTypes/TabViewWithPageStyle.swift @@ -1,14 +1,30 @@ -#if !os(macOS) import SwiftUI -// MARK: SwiftUI.TabView { ... }.tabViewStyle(.page) - +/// An abstract representation of the `TabView` type in SwiftUI, with `.page` style. +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// TabView { +/// Text("Page 1").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red) +/// Text("Page 2").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.blue) +/// } +/// .tabViewStyle(.page(indexDisplayMode: .always)) +/// #if os(iOS) || os(tvOS) +/// .introspect(.tabView(style: .page), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UICollectionView +/// } +/// #endif +/// } +/// } +/// ``` public struct TabViewWithPageStyleType: IntrospectableViewType { public enum Style { case page } } +#if !os(macOS) extension IntrospectableViewType where Self == TabViewWithPageStyleType { public static func tabView(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/Table.swift b/Sources/ViewTypes/Table.swift index 5534c4b1c..ea002aff0 100644 --- a/Sources/ViewTypes/Table.swift +++ b/Sources/ViewTypes/Table.swift @@ -1,10 +1,48 @@ -#if os(iOS) || os(macOS) import SwiftUI -// MARK: SwiftUI.Table - +/// An abstract representation of the `Table` type in SwiftUI, with any style. +/// +/// ```swift +/// struct ContentView: View { +/// struct Purchase: Identifiable { +/// let id = UUID() +/// let price: Decimal +/// } +/// +/// 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)) +/// } +/// #if os(iOS) +/// .introspect(.table, on: .iOS(.v16, .v17)) { +/// print(type(of: $0)) // UICollectionView +/// } +/// #elseif os(macOS) +/// .introspect(.table, on: .macOS(.v12, .v13, .v14)) { +/// print(type(of: $0)) // NSTableView +/// } +/// #endif +/// } +/// } +/// ``` public struct TableType: IntrospectableViewType {} +#if os(iOS) || os(macOS) extension IntrospectableViewType where Self == TableType { public static var table: Self { .init() } } diff --git a/Sources/ViewTypes/TextEditor.swift b/Sources/ViewTypes/TextEditor.swift index b451ae0ff..b7c81c067 100644 --- a/Sources/ViewTypes/TextEditor.swift +++ b/Sources/ViewTypes/TextEditor.swift @@ -1,10 +1,28 @@ -#if !os(tvOS) import SwiftUI -// MARK: SwiftUI.TextEditor - +/// An abstract representation of the `TextEditor` type in SwiftUI. +/// +/// ```swift +/// struct ContentView: View { +/// @State var text = "Lorem ipsum" +/// +/// var body: some View { +/// TextEditor(text: $text) +/// #if os(iOS) +/// .introspect(.textEditor, on: .iOS(.v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UITextView +/// } +/// #elseif os(macOS) +/// .introspect(.textEditor, on: .macOS(.v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSTextView +/// } +/// #endif +/// } +/// } +/// ``` public struct TextEditorType: IntrospectableViewType {} +#if !os(tvOS) extension IntrospectableViewType where Self == TextEditorType { public static var textEditor: Self { .init() } } diff --git a/Sources/ViewTypes/TextField.swift b/Sources/ViewTypes/TextField.swift index ae5c750c1..ae6d0f11b 100644 --- a/Sources/ViewTypes/TextField.swift +++ b/Sources/ViewTypes/TextField.swift @@ -1,7 +1,25 @@ import SwiftUI -// MARK: SwiftUI.TextField - +/// An abstract representation of the `TextField` type in SwiftUI. +/// +/// ```swift +/// struct ContentView: View { +/// @State var text = "Lorem ipsum" +/// +/// var body: some View { +/// TextField("Text Field", text: $text) +/// #if os(iOS) || os(tvOS) +/// .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UITextField +/// } +/// #elseif os(macOS) +/// .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSTextField +/// } +/// #endif +/// } +/// } +/// ``` public struct TextFieldType: IntrospectableViewType {} extension IntrospectableViewType where Self == TextFieldType { diff --git a/Sources/ViewTypes/TextFieldWithVerticalAxis.swift b/Sources/ViewTypes/TextFieldWithVerticalAxis.swift index b0e29e3f1..983a8dfcb 100644 --- a/Sources/ViewTypes/TextFieldWithVerticalAxis.swift +++ b/Sources/ViewTypes/TextFieldWithVerticalAxis.swift @@ -1,7 +1,29 @@ import SwiftUI -// MARK: SwiftUI.TextField(..., axis: .vertical) - +/// An abstract representation of the `TextField` type in SwiftUI, with `.vertical` axis. +/// +/// ```swift +/// struct ContentView: View { +/// @State var text = "Lorem ipsum" +/// +/// var body: some View { +/// TextField("Text Field", text: $text, axis: .vertical) +/// #if os(iOS) +/// .introspect(.textField(axis: .vertical), on: .iOS(.v16, .v17)) { +/// print(type(of: $0)) // UITextView +/// } +/// #elseif os(tvOS) +/// .introspect(.textField(axis: .vertical), on: .tvOS(.v16, .v17)) { +/// print(type(of: $0)) // UITextField +/// } +/// #elseif os(macOS) +/// .introspect(.textField(axis: .vertical), on: .macOS(.v13, .v14)) { +/// print(type(of: $0)) // NSTextField +/// } +/// #endif +/// } +/// } +/// ``` public struct TextFieldWithVerticalAxisType: IntrospectableViewType { public enum Axis { case vertical diff --git a/Sources/ViewTypes/Toggle.swift b/Sources/ViewTypes/Toggle.swift index dfd763313..335c3c5dd 100644 --- a/Sources/ViewTypes/Toggle.swift +++ b/Sources/ViewTypes/Toggle.swift @@ -1,10 +1,28 @@ -#if !os(tvOS) import SwiftUI -// MARK: SwiftUI.Toggle - +/// An abstract representation of the `Toggle` type in SwiftUI. +/// +/// ```swift +/// struct ContentView: View { +/// @State var isOn = false +/// +/// var body: some View { +/// Toggle("Toggle", isOn: $isOn) +/// #if os(iOS) +/// .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UISwitch +/// } +/// #elseif os(macOS) +/// .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSButton +/// } +/// #endif +/// } +/// } +/// ``` public struct ToggleType: IntrospectableViewType {} +#if !os(tvOS) extension IntrospectableViewType where Self == ToggleType { public static var toggle: Self { .init() } } diff --git a/Sources/ViewTypes/ToggleWithButtonStyle.swift b/Sources/ViewTypes/ToggleWithButtonStyle.swift index cf77f860b..0851d6084 100644 --- a/Sources/ViewTypes/ToggleWithButtonStyle.swift +++ b/Sources/ViewTypes/ToggleWithButtonStyle.swift @@ -1,14 +1,29 @@ -#if os(macOS) import SwiftUI -// MARK: SwiftUI.Toggle(...).toggleStyle(.button) - +/// An abstract representation of the `Toggle` type in SwiftUI, with `.button` style. +/// +/// ```swift +/// struct ContentView: View { +/// @State var isOn = false +/// +/// var body: some View { +/// Toggle("Toggle", isOn: $isOn) +/// .toggleStyle(.button) +/// #if os(macOS) +/// .introspect(.toggle(style: .button), on: .macOS(.v12, .v13, .v14)) { +/// print(type(of: $0)) // NSButton +/// } +/// #endif +/// } +/// } +/// ``` public struct ToggleWithButtonStyleType: IntrospectableViewType { public enum Style { case button } } +#if os(macOS) extension IntrospectableViewType where Self == ToggleWithButtonStyleType { public static func toggle(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/ToggleWithCheckboxStyle.swift b/Sources/ViewTypes/ToggleWithCheckboxStyle.swift index 1fcf049f2..11098bc46 100644 --- a/Sources/ViewTypes/ToggleWithCheckboxStyle.swift +++ b/Sources/ViewTypes/ToggleWithCheckboxStyle.swift @@ -1,14 +1,29 @@ -#if os(macOS) import SwiftUI -// MARK: SwiftUI.Toggle(...).toggleStyle(.checkbox) - +/// An abstract representation of the `Toggle` type in SwiftUI, with `.checkbox` style. +/// +/// ```swift +/// struct ContentView: View { +/// @State var isOn = false +/// +/// var body: some View { +/// Toggle("Checkbox", isOn: $isOn) +/// .toggleStyle(.checkbox) +/// #if os(macOS) +/// .introspect(.toggle(style: .checkbox), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSButton +/// } +/// #endif +/// } +/// } +/// ``` public struct ToggleWithCheckboxStyleType: IntrospectableViewType { public enum Style { case checkbox } } +#if os(macOS) extension IntrospectableViewType where Self == ToggleWithCheckboxStyleType { public static func toggle(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/ToggleWithSwitchStyle.swift b/Sources/ViewTypes/ToggleWithSwitchStyle.swift index 494c62a57..b49780149 100644 --- a/Sources/ViewTypes/ToggleWithSwitchStyle.swift +++ b/Sources/ViewTypes/ToggleWithSwitchStyle.swift @@ -1,14 +1,33 @@ -#if !os(tvOS) import SwiftUI -// MARK: SwiftUI.Toggle(...).toggleStyle(.switch) - +/// An abstract representation of the `Toggle` type in SwiftUI, with `.switch` style. +/// +/// ```swift +/// struct ContentView: View { +/// @State var isOn = false +/// +/// var body: some View { +/// Toggle("Switch", isOn: $isOn) +/// .toggleStyle(.switch) +/// #if os(iOS) +/// .introspect(.toggle(style: .switch), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UISwitch +/// } +/// #elseif os(macOS) +/// .introspect(.toggle(style: .switch), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSSwitch +/// } +/// #endif +/// } +/// } +/// ``` public struct ToggleWithSwitchStyleType: IntrospectableViewType { public enum Style { case `switch` } } +#if !os(tvOS) extension IntrospectableViewType where Self == ToggleWithSwitchStyleType { public static func toggle(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/View.swift b/Sources/ViewTypes/View.swift index 2415e8cd8..19bc86fe7 100644 --- a/Sources/ViewTypes/View.swift +++ b/Sources/ViewTypes/View.swift @@ -1,7 +1,22 @@ import SwiftUI -// MARK: SwiftUI.View - +/// An abstract representation of a generic view type in SwiftUI. +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// NavigationView { +/// Text("Root") +/// #if os(iOS) || os(tvOS) +/// .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIViewController +/// } +/// #endif +/// } +/// .navigationViewStyle(.stack) +/// } +/// } +/// ``` public struct ViewType: IntrospectableViewType { public var scope: IntrospectionScope { [.receiver, .ancestor] } } diff --git a/docs/SwiftUIIntrospect.md b/docs/SwiftUIIntrospect.md index fd87e48cc..8abed591a 100644 --- a/docs/SwiftUIIntrospect.md +++ b/docs/SwiftUIIntrospect.md @@ -64,7 +64,7 @@ Install ```swift let package = Package( dependencies: [ - .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.4.0"), + .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.6.1"), ], targets: [ .target(name: <#Target Name#>, dependencies: [ @@ -85,9 +85,47 @@ 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. +- [`Button`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/buttontype) +- [`ColorPicker`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/colorpickertype) +- [`DatePicker`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickertype) +- [`DatePicker` with `.compact` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithcompactstyletype) +- [`DatePicker` with `.field` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithfieldstyletype) +- [`DatePicker` with `.graphical` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithgraphicalstyletype) +- [`DatePicker` with `.stepperField` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithstepperfieldstyletype) +- [`DatePicker` with `.wheel` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithwheelstyletype) +- [`Form`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/formtype) +- [`Form` with `.grouped` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/formwithgroupedstyletype) +- [`List`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listtype) +- [`List` with `.bordered` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithborderedstyletype) +- [`List` with `.grouped` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithgroupedstyletype) +- [`List` with `.insetGrouped` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithinsetgroupedstyletype) +- [`List` with `.inset` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithinsetstyletype) +- [`List` with `.sidebar` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithsidebarstyletype) +- [`ListCell`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listcelltype) +- [`NavigationSplitView`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/navigationsplitviewtype) +- [`NavigationStack`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/navigationstacktype) +- [`NavigationView` with `.columns` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/NavigationViewWithColumnsStyleType) +- [`NavigationView` with `.stack` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/NavigationViewWithStackStyleType) +- [`Picker` with `.menu` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithmenustyletype) +- [`Picker` with `.segmented` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithsegmentedstyletype) +- [`Picker` with `.wheel` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithwheelstyletype) +- [`ProgressView` with `.circular` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/progressviewwithcircularstyletests) +- [`ProgressView` with `.linear` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/progressviewwithlinearstyletests) +- [`ScrollView`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/scrollviewtype) +- [`.searchable`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/searchfieldtype) +- [`Slider`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/slidertype) +- [`Stepper`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/steppertype) +- [`Table`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/tabletype) +- [`TabView`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/tabviewtype) +- [`TabView` with `.page` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/TabViewWithPageStyleType) +- [`TextEditor`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/texteditortype) +- [`TextField`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/textfieldtype) +- [`TextField` with `.vertical` axis](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/TextFieldWithVerticalAxisType) +- [`Toggle`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/toggletype) +- [`Toggle` with `button` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/togglewithbuttonstyletype) +- [`Toggle` with `checkbox` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/togglewithcheckboxstyletype) +- [`Toggle` with `switch` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/togglewithswitchstyletype) +- [`View`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/viewtype) **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). From 56065b729481abce73fe606e218024bc77d055cd Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Wed, 21 Jun 2023 20:18:02 +0100 Subject: [PATCH 057/116] Bump to 0.6.2 [skip ci] (#259) --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39c16be0e..ea4cd5d8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,17 @@ Changelog ## master +## [0.6.2] + +### SwiftUIIntrospect + +- Documentation: added docs for all view types (#258) +- Infrastructure: fixed iOS/tvOS 13 checks on CI (#257) + ## [0.6.1] +### SwiftUIIntrospect + - Improved: optimized receiver lookup algorithm (#246) - Infrastructure: refactored `.introspect` to use `ViewModifier` (#253) - Infrastructure: retry runtime download on timeout or error on CI (#247) From 82be8c30840d9a62c6553f7240d5c78398558053 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Thu, 22 Jun 2023 09:39:36 +0100 Subject: [PATCH 058/116] Documentation fixes [skip ci] (#260) --- docs/SwiftUIIntrospect.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/SwiftUIIntrospect.md b/docs/SwiftUIIntrospect.md index 8abed591a..451683e9f 100644 --- a/docs/SwiftUIIntrospect.md +++ b/docs/SwiftUIIntrospect.md @@ -39,7 +39,7 @@ ScrollView { - 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. +> Although the introspection method itself is very solid and unlikely to break in itself, 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`: @@ -109,8 +109,8 @@ Introspection - [`Picker` with `.menu` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithmenustyletype) - [`Picker` with `.segmented` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithsegmentedstyletype) - [`Picker` with `.wheel` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithwheelstyletype) -- [`ProgressView` with `.circular` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/progressviewwithcircularstyletests) -- [`ProgressView` with `.linear` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/progressviewwithlinearstyletests) +- [`ProgressView` with `.circular` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/progressviewwithcircularstyletype) +- [`ProgressView` with `.linear` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/progressviewwithlinearstyletype) - [`ScrollView`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/scrollviewtype) - [`.searchable`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/searchfieldtype) - [`Slider`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/slidertype) From 4302c3f96f6523f693ee2cd028eaed2b1a0ad6ec Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 23 Jun 2023 17:56:08 +0100 Subject: [PATCH 059/116] Disable accessibility for introspection views (#261) --- .github/workflows/ci.yml | 9 ++++----- CHANGELOG.md | 5 +++++ Sources/Introspect.swift | 2 ++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 312fc6f23..521efe83b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,20 +106,19 @@ jobs: with: xcode-version: ${{ matrix.xcode || '14.3' }} + - name: Install tea + uses: teaxyz/setup@v0 + - name: Install xcbeautify run: brew install xcbeautify - - if: ${{ matrix.install }} - name: Install xcodes - run: brew install xcodesorg/made/xcodes - - if: ${{ matrix.install }} name: Install Required Runtime (${{ matrix.runtime }}) uses: nick-fields/retry@v2 with: timeout_minutes: 15 max_attempts: 3 - command: sudo xcodes runtimes install '${{ matrix.runtime }}' + command: sudo tea xcodes runtimes install '${{ matrix.runtime }}' - name: List Available Runtimes and Simulators run: xcrun simctl list diff --git a/CHANGELOG.md b/CHANGELOG.md index ea4cd5d8e..be65f8211 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ Changelog ## master +### SwiftUIIntrospect + +- Changed: disabled accessibility for introspection views (#261) +- Infrastructure: use [`xcodes`](https://github.com/XcodesOrg/xcodes) via [`tea`](https://github.com/teaxyz/cli) on CI (#261) + ## [0.6.2] ### SwiftUIIntrospect diff --git a/Sources/Introspect.swift b/Sources/Introspect.swift index 245280a6e..02c255364 100644 --- a/Sources/Introspect.swift +++ b/Sources/Introspect.swift @@ -49,10 +49,12 @@ struct IntrospectModifier Date: Sun, 25 Jun 2023 22:38:52 +0100 Subject: [PATCH 060/116] Split code samples by OS (#262) --- CHANGELOG.md | 1 + Package.swift | 2 +- Package@swift-5.7.swift | 2 +- Sources/ViewTypes/Button.swift | 12 ++++- Sources/ViewTypes/ColorPicker.swift | 21 +++++++-- Sources/ViewTypes/DatePicker.swift | 19 ++++++-- .../DatePickerWithCompactStyle.swift | 22 ++++++++-- .../ViewTypes/DatePickerWithFieldStyle.swift | 12 ++++- .../DatePickerWithGraphicalStyleType.swift | 22 ++++++++-- .../DatePickerWithStepperFieldStyle.swift | 12 ++++- .../ViewTypes/DatePickerWithWheelStyle.swift | 13 +++++- Sources/ViewTypes/Form.swift | 28 ++++++++++-- Sources/ViewTypes/FormWithGroupedStyle.swift | 35 +++++++++++++-- Sources/ViewTypes/List.swift | 38 ++++++++++++++-- Sources/ViewTypes/ListCell.swift | 38 ++++++++++++++-- Sources/ViewTypes/ListWithBorderedStyle.swift | 12 ++++- Sources/ViewTypes/ListWithGroupedStyle.swift | 29 ++++++++++-- .../ViewTypes/ListWithInsetGroupedStyle.swift | 13 +++++- Sources/ViewTypes/ListWithInsetStyle.swift | 25 +++++++++-- Sources/ViewTypes/ListWithSidebarStyle.swift | 24 ++++++++-- Sources/ViewTypes/NavigationSplitView.swift | 35 +++++++++++++-- Sources/ViewTypes/NavigationStack.swift | 26 +++++++++-- .../NavigationViewWithColumnsStyle.swift | 32 ++++++++++++-- .../NavigationViewWithStackStyle.swift | 27 ++++++++++-- Sources/ViewTypes/PickerWithMenuStyle.swift | 12 ++++- .../ViewTypes/PickerWithSegmentedStyle.swift | 44 +++++++++++++++++-- Sources/ViewTypes/PickerWithWheelStyle.swift | 13 +++++- .../ProgressViewWithCircularStyle.swift | 33 ++++++++++++-- .../ProgressViewWithLinearStyle.swift | 33 ++++++++++++-- Sources/ViewTypes/ScrollView.swift | 35 +++++++++++++-- Sources/ViewTypes/SearchField.swift | 30 +++++++++++-- Sources/ViewTypes/Slider.swift | 22 ++++++++-- Sources/ViewTypes/Stepper.swift | 22 ++++++++-- Sources/ViewTypes/TabView.swift | 37 ++++++++++++++-- Sources/ViewTypes/TabViewWithPageStyle.swift | 28 ++++++++++-- Sources/ViewTypes/Table.swift | 42 +++++++++++++++--- Sources/ViewTypes/TextEditor.swift | 21 +++++++-- Sources/ViewTypes/TextField.swift | 34 ++++++++++++-- .../ViewTypes/TextFieldWithVerticalAxis.swift | 31 +++++++++++-- Sources/ViewTypes/Toggle.swift | 24 ++++++++-- Sources/ViewTypes/ToggleWithButtonStyle.swift | 12 ++++- .../ViewTypes/ToggleWithCheckboxStyle.swift | 12 ++++- Sources/ViewTypes/ToggleWithSwitchStyle.swift | 22 ++++++++-- Sources/ViewTypes/View.swift | 27 ++++++++++-- 44 files changed, 904 insertions(+), 130 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be65f8211..d62a6b96f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Changelog ### SwiftUIIntrospect - Changed: disabled accessibility for introspection views (#261) +- Documentation: code samples are now split by OS (#262) - Infrastructure: use [`xcodes`](https://github.com/XcodesOrg/xcodes) via [`tea`](https://github.com/teaxyz/cli) on CI (#261) ## [0.6.2] diff --git a/Package.swift b/Package.swift index 1e3086163..e5f668283 100644 --- a/Package.swift +++ b/Package.swift @@ -6,8 +6,8 @@ let package = Package( name: "Introspect", platforms: [ .iOS(.v13), - .macOS(.v10_15), .tvOS(.v13), + .macOS(.v10_15), ], products: [ .library(name: "Introspect", targets: ["Introspect"]), diff --git a/Package@swift-5.7.swift b/Package@swift-5.7.swift index 4b1cf8b1d..25ea09e1f 100644 --- a/Package@swift-5.7.swift +++ b/Package@swift-5.7.swift @@ -6,8 +6,8 @@ let package = Package( name: "swiftui-introspect", platforms: [ .iOS(.v13), - .macOS(.v10_15), .tvOS(.v13), + .macOS(.v10_15), ], products: [ // legacy library diff --git a/Sources/ViewTypes/Button.swift b/Sources/ViewTypes/Button.swift index 108ba6f30..36793a094 100644 --- a/Sources/ViewTypes/Button.swift +++ b/Sources/ViewTypes/Button.swift @@ -2,15 +2,23 @@ import SwiftUI /// An abstract representation of the `Button` type in SwiftUI. /// +/// ### iOS +/// +/// Not available. +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// /// ```swift /// struct ContentView: View { /// var body: some View { /// Button("Action", action: {}) -/// #if os(macOS) /// .introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSButton /// } -/// #endif /// } /// } /// ``` diff --git a/Sources/ViewTypes/ColorPicker.swift b/Sources/ViewTypes/ColorPicker.swift index d1970d547..31f690240 100644 --- a/Sources/ViewTypes/ColorPicker.swift +++ b/Sources/ViewTypes/ColorPicker.swift @@ -2,21 +2,36 @@ import SwiftUI /// An abstract representation of the `ColorPicker` type in SwiftUI. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// @State var color = Color.red /// /// var body: some View { /// ColorPicker("Pick a color", selection: $color) -/// #if os(iOS) /// .introspect(.colorPicker, on: .iOS(.v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UIColorPicker /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var color = Color.red +/// +/// var body: some View { +/// ColorPicker("Pick a color", selection: $color) /// .introspect(.colorPicker, on: .macOS(.v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSColorPicker /// } -/// #endif /// } /// } /// ``` diff --git a/Sources/ViewTypes/DatePicker.swift b/Sources/ViewTypes/DatePicker.swift index e9a48f428..db199aea8 100644 --- a/Sources/ViewTypes/DatePicker.swift +++ b/Sources/ViewTypes/DatePicker.swift @@ -2,21 +2,34 @@ import SwiftUI /// An abstract representation of the `DatePicker` type in SwiftUI. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// @State var date = Date() /// /// var body: some View { /// DatePicker("Pick a date", selection: $date) -/// #if os(iOS) /// .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UIDatePicker /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// Not available. +/// +/// ```swift +/// struct ContentView: View { +/// @State var date = Date() +/// +/// var body: some View { +/// DatePicker("Pick a date", selection: $date) /// .introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSDatePicker /// } -/// #endif /// } /// } /// ``` diff --git a/Sources/ViewTypes/DatePickerWithCompactStyle.swift b/Sources/ViewTypes/DatePickerWithCompactStyle.swift index 972766057..6769cd2f1 100644 --- a/Sources/ViewTypes/DatePickerWithCompactStyle.swift +++ b/Sources/ViewTypes/DatePickerWithCompactStyle.swift @@ -2,6 +2,8 @@ import SwiftUI /// An abstract representation of the `DatePicker` type in SwiftUI, with `.compact` style. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// @State var date = Date() @@ -9,15 +11,29 @@ import SwiftUI /// var body: some View { /// DatePicker("Pick a date", selection: $date) /// .datePickerStyle(.compact) -/// #if os(iOS) /// .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UIDatePicker /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var date = Date() +/// +/// var body: some View { +/// DatePicker("Pick a date", selection: $date) +/// .datePickerStyle(.compact) /// .introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSDatePicker /// } -/// #endif /// } /// } /// ``` diff --git a/Sources/ViewTypes/DatePickerWithFieldStyle.swift b/Sources/ViewTypes/DatePickerWithFieldStyle.swift index b47f1930a..6aea42ec7 100644 --- a/Sources/ViewTypes/DatePickerWithFieldStyle.swift +++ b/Sources/ViewTypes/DatePickerWithFieldStyle.swift @@ -2,6 +2,16 @@ import SwiftUI /// An abstract representation of the `DatePicker` type in SwiftUI, with `.field` style. /// +/// ### iOS +/// +/// Not available. +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// /// ```swift /// struct ContentView: View { /// @State var date = Date() @@ -9,11 +19,9 @@ import SwiftUI /// var body: some View { /// DatePicker("Pick a date", selection: $date) /// .datePickerStyle(.field) -/// #if os(macOS) /// .introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSDatePicker /// } -/// #endif /// } /// } /// ``` diff --git a/Sources/ViewTypes/DatePickerWithGraphicalStyleType.swift b/Sources/ViewTypes/DatePickerWithGraphicalStyleType.swift index b292ce613..ddfae0c6b 100644 --- a/Sources/ViewTypes/DatePickerWithGraphicalStyleType.swift +++ b/Sources/ViewTypes/DatePickerWithGraphicalStyleType.swift @@ -2,6 +2,8 @@ import SwiftUI /// An abstract representation of the `DatePicker` type in SwiftUI, with `.graphical` style. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// @State var date = Date() @@ -9,15 +11,29 @@ import SwiftUI /// var body: some View { /// DatePicker("Pick a date", selection: $date) /// .datePickerStyle(.graphical) -/// #if os(iOS) /// .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UIDatePicker /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var date = Date() +/// +/// var body: some View { +/// DatePicker("Pick a date", selection: $date) +/// .datePickerStyle(.graphical) /// .introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSDatePicker /// } -/// #endif /// } /// } /// ``` diff --git a/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift b/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift index 911fc2c96..3cc3d9e89 100644 --- a/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift +++ b/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift @@ -2,6 +2,16 @@ import SwiftUI /// An abstract representation of the `DatePicker` type in SwiftUI, with `.stepperField` style. /// +/// ### iOS +/// +/// Not available. +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// /// ```swift /// struct ContentView: View { /// @State var date = Date() @@ -9,11 +19,9 @@ import SwiftUI /// var body: some View { /// DatePicker("Pick a date", selection: $date) /// .datePickerStyle(.stepperField) -/// #if os(macOS) /// .introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSDatePicker /// } -/// #endif /// } /// } /// ``` diff --git a/Sources/ViewTypes/DatePickerWithWheelStyle.swift b/Sources/ViewTypes/DatePickerWithWheelStyle.swift index 6f3ced386..d87d4aba6 100644 --- a/Sources/ViewTypes/DatePickerWithWheelStyle.swift +++ b/Sources/ViewTypes/DatePickerWithWheelStyle.swift @@ -2,6 +2,8 @@ import SwiftUI /// An abstract representation of the `DatePicker` type in SwiftUI, with `.wheel` style. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// @State var date = Date() @@ -9,14 +11,21 @@ import SwiftUI /// var body: some View { /// DatePicker("Pick a date", selection: $date) /// .datePickerStyle(.wheel) -/// #if os(iOS) /// .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UIDatePicker /// } -/// #endif /// } /// } /// ``` +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// +/// Not available. +/// public struct DatePickerWithWheelStyleType: IntrospectableViewType { public enum Style { case wheel diff --git a/Sources/ViewTypes/Form.swift b/Sources/ViewTypes/Form.swift index 16cbd2e7b..4ba05620c 100644 --- a/Sources/ViewTypes/Form.swift +++ b/Sources/ViewTypes/Form.swift @@ -2,6 +2,8 @@ import SwiftUI /// An abstract representation of the `Form` type in SwiftUI. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// var body: some View { @@ -10,17 +12,37 @@ import SwiftUI /// Text("Item 2") /// Text("Item 3") /// } -/// #if os(iOS) || os(tvOS) -/// .introspect(.form, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// .introspect(.form, on: .iOS(.v13, .v14, .v15)) { /// print(type(of: $0)) // UITableView /// } /// .introspect(.form, on: .iOS(.v16, .v17)) { /// print(type(of: $0)) // UICollectionView /// } -/// #endif /// } /// } /// ``` +/// +/// ### tvOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// Form { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// .introspect(.form, on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UITableView +/// } +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// Not available. +/// public struct FormType: IntrospectableViewType {} #if !os(macOS) diff --git a/Sources/ViewTypes/FormWithGroupedStyle.swift b/Sources/ViewTypes/FormWithGroupedStyle.swift index 3a35b76d2..6f38a31dc 100644 --- a/Sources/ViewTypes/FormWithGroupedStyle.swift +++ b/Sources/ViewTypes/FormWithGroupedStyle.swift @@ -2,6 +2,8 @@ import SwiftUI /// An abstract representation of the `Form` type in SwiftUI, with `.grouped` style. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// var body: some View { @@ -11,18 +13,45 @@ import SwiftUI /// Text("Item 3") /// } /// .formStyle(.grouped) -/// #if os(iOS) || os(tvOS) /// .introspect(.form(style: .grouped), on: .iOS(.v16, .v17)) { /// print(type(of: $0)) // UITableView /// } +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// Form { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// .formStyle(.grouped) /// .introspect(.form(style: .grouped), on: .tvOS(.v16, .v17)) { /// print(type(of: $0)) // UICollectionView /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// Form { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// .formStyle(.grouped) /// .introspect(.form(style: .grouped), on: .macOS(.v13, .v14)) { /// print(type(of: $0)) // NSScrollView /// } -/// #endif /// } /// } /// ``` diff --git a/Sources/ViewTypes/List.swift b/Sources/ViewTypes/List.swift index 8badfb0e1..e5d0faccc 100644 --- a/Sources/ViewTypes/List.swift +++ b/Sources/ViewTypes/List.swift @@ -2,6 +2,8 @@ import SwiftUI /// An abstract representation of the `List` type in SwiftUI. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// var body: some View { @@ -10,18 +12,46 @@ import SwiftUI /// Text("Item 2") /// Text("Item 3") /// } -/// #if os(iOS) || os(tvOS) -/// .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// .introspect(.list, on: .iOS(.v13, .v14, .v15)) { /// print(type(of: $0)) // UITableView /// } /// .introspect(.list, on: .iOS(.v16, .v17)) { /// print(type(of: $0)) // UICollectionView /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// List { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// .introspect(.list, on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UITableView +/// } +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// List { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } /// .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSTableView /// } -/// #endif /// } /// } /// ``` diff --git a/Sources/ViewTypes/ListCell.swift b/Sources/ViewTypes/ListCell.swift index f1f9e6625..fe7aa0883 100644 --- a/Sources/ViewTypes/ListCell.swift +++ b/Sources/ViewTypes/ListCell.swift @@ -2,24 +2,54 @@ import SwiftUI /// An abstract representation of a `List` cell type in SwiftUI. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// var body: some View { /// List { /// ForEach(1...3, id: \.self) { int in /// Text("Item \(int)") -/// #if os(iOS) || os(tvOS) -/// .introspect(.listCell, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// .introspect(.listCell, on: .iOS(.v13, .v14, .v15)) { /// print(type(of: $0)) // UITableViewCell /// } /// .introspect(.listCell, on: .iOS(.v16, .v17)) { /// print(type(of: $0)) // UICollectionViewCell /// } -/// #elseif os(macOS) +/// } +/// } +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// List { +/// ForEach(1...3, id: \.self) { int in +/// Text("Item \(int)") +/// .introspect(.listCell, on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UITableViewCell +/// } +/// } +/// } +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// List { +/// ForEach(1...3, id: \.self) { int in +/// Text("Item \(int)") /// .introspect(.listCell, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSTableCellView /// } -/// #endif /// } /// } /// } diff --git a/Sources/ViewTypes/ListWithBorderedStyle.swift b/Sources/ViewTypes/ListWithBorderedStyle.swift index 252e75492..2c196d23c 100644 --- a/Sources/ViewTypes/ListWithBorderedStyle.swift +++ b/Sources/ViewTypes/ListWithBorderedStyle.swift @@ -2,6 +2,16 @@ import SwiftUI /// An abstract representation of the `List` type in SwiftUI, with `.bordered` style. /// +/// ### iOS +/// +/// Not available. +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// /// ```swift /// struct ContentView: View { /// var body: some View { @@ -11,11 +21,9 @@ import SwiftUI /// Text("Item 3") /// } /// .listStyle(.bordered) -/// #if os(macOS) /// .introspect(.list(style: .bordered), on: .macOS(.v12, .v13, .v14)) { /// print(type(of: $0)) // NSTableView /// } -/// #endif /// } /// } /// ``` diff --git a/Sources/ViewTypes/ListWithGroupedStyle.swift b/Sources/ViewTypes/ListWithGroupedStyle.swift index 2ebdbeaaa..459e264c5 100644 --- a/Sources/ViewTypes/ListWithGroupedStyle.swift +++ b/Sources/ViewTypes/ListWithGroupedStyle.swift @@ -2,6 +2,8 @@ import SwiftUI /// An abstract representation of the `List` type in SwiftUI, with `.grouped` style. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// var body: some View { @@ -11,17 +13,38 @@ import SwiftUI /// Text("Item 3") /// } /// .listStyle(.grouped) -/// #if os(iOS) || os(tvOS) -/// .introspect(.list(style: .grouped), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// .introspect(.list(style: .grouped), on: .iOS(.v13, .v14, .v15)) { /// print(type(of: $0)) // UITableView /// } /// .introspect(.list(style: .grouped), on: .iOS(.v16, .v17)) { /// print(type(of: $0)) // UICollectionView /// } -/// #endif /// } /// } /// ``` +/// +/// ### tvOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// List { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// .listStyle(.grouped) +/// .introspect(.list(style: .grouped), on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UITableView +/// } +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// Not available. +/// public struct ListWithGroupedStyleType: IntrospectableViewType { public enum Style { case grouped diff --git a/Sources/ViewTypes/ListWithInsetGroupedStyle.swift b/Sources/ViewTypes/ListWithInsetGroupedStyle.swift index 5e26727e9..63e6a2898 100644 --- a/Sources/ViewTypes/ListWithInsetGroupedStyle.swift +++ b/Sources/ViewTypes/ListWithInsetGroupedStyle.swift @@ -2,6 +2,8 @@ import SwiftUI /// An abstract representation of the `List` type in SwiftUI, with `.insetGrouped` style. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// var body: some View { @@ -11,17 +13,24 @@ import SwiftUI /// Text("Item 3") /// } /// .listStyle(.insetGrouped) -/// #if os(iOS) /// .introspect(.list(style: .insetGrouped), on: .iOS(.v14, .v15)) { /// print(type(of: $0)) // UITableView /// } /// .introspect(.list(style: .insetGrouped), on: .iOS(.v16, .v17)) { /// print(type(of: $0)) // UICollectionView /// } -/// #endif /// } /// } /// ``` +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// +/// Not available. +/// public struct ListWithInsetGroupedStyleType: IntrospectableViewType { public enum Style { case insetGrouped diff --git a/Sources/ViewTypes/ListWithInsetStyle.swift b/Sources/ViewTypes/ListWithInsetStyle.swift index 62269d352..ee08aa034 100644 --- a/Sources/ViewTypes/ListWithInsetStyle.swift +++ b/Sources/ViewTypes/ListWithInsetStyle.swift @@ -2,6 +2,8 @@ import SwiftUI /// An abstract representation of the `List` type in SwiftUI, with `.inset` style. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// var body: some View { @@ -11,21 +13,38 @@ import SwiftUI /// Text("Item 3") /// } /// .listStyle(.inset) -/// #if os(iOS) /// .introspect(.list(style: .inset), on: .iOS(.v14, .v15)) { /// print(type(of: $0)) // UITableView /// } /// .introspect(.list(style: .inset), on: .iOS(.v16, .v17)) { /// print(type(of: $0)) // UICollectionView /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// List { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// .listStyle(.inset) /// .introspect(.list(style: .inset), on: .macOS(.v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSTableView /// } -/// #endif /// } /// } /// ``` +/// public struct ListWithInsetStyleType: IntrospectableViewType { public enum Style { case inset diff --git a/Sources/ViewTypes/ListWithSidebarStyle.swift b/Sources/ViewTypes/ListWithSidebarStyle.swift index fede61ff1..4d127dfce 100644 --- a/Sources/ViewTypes/ListWithSidebarStyle.swift +++ b/Sources/ViewTypes/ListWithSidebarStyle.swift @@ -2,6 +2,8 @@ import SwiftUI /// An abstract representation of the `List` type in SwiftUI, with `.sidebar` style. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// var body: some View { @@ -11,18 +13,34 @@ import SwiftUI /// Text("Item 3") /// } /// .listStyle(.sidebar) -/// #if os(iOS) /// .introspect(.list(style: .sidebar), on: .iOS(.v14, .v15)) { /// print(type(of: $0)) // UITableView /// } /// .introspect(.list(style: .sidebar), on: .iOS(.v16, .v17)) { /// print(type(of: $0)) // UICollectionView /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// List { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// .listStyle(.sidebar) /// .introspect(.list(style: .sidebar), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSTableView /// } -/// #endif /// } /// } /// ``` diff --git a/Sources/ViewTypes/NavigationSplitView.swift b/Sources/ViewTypes/NavigationSplitView.swift index 5673b09df..72b0d5647 100644 --- a/Sources/ViewTypes/NavigationSplitView.swift +++ b/Sources/ViewTypes/NavigationSplitView.swift @@ -2,6 +2,8 @@ import SwiftUI /// An abstract representation of the `NavigationSplitView` type in SwiftUI. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// var body: some View { @@ -10,22 +12,47 @@ import SwiftUI /// } detail: { /// Text("Detail") /// } -/// #if os(iOS) /// .introspect(.navigationSplitView, on: .iOS(.v16, .v17)) { /// print(type(of: $0)) // UISplitViewController /// } -/// #elseif os(tvOS) +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// NavigationSplitView { +/// Text("Root") +/// } detail: { +/// Text("Detail") +/// } /// .introspect(.navigationSplitView, on: .tvOS(.v16, .v17)) { /// print(type(of: $0)) // UINavigationController /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// NavigationSplitView { +/// Text("Root") +/// } detail: { +/// Text("Detail") +/// } /// .introspect(.navigationSplitView, on: .macOS(.v13, .v14)) { /// print(type(of: $0)) // NSSplitView /// } -/// #endif /// } /// } /// ``` +/// public struct NavigationSplitViewType: IntrospectableViewType {} extension IntrospectableViewType where Self == NavigationSplitViewType { diff --git a/Sources/ViewTypes/NavigationStack.swift b/Sources/ViewTypes/NavigationStack.swift index d4ecfad20..b1f330210 100644 --- a/Sources/ViewTypes/NavigationStack.swift +++ b/Sources/ViewTypes/NavigationStack.swift @@ -2,20 +2,40 @@ import SwiftUI /// An abstract representation of the `NavigationStack` type in SwiftUI. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// var body: some View { /// NavigationStack { /// Text("Root") /// } -/// #if os(iOS) || os(tvOS) -/// .introspect(.navigationStack, on: .iOS(.v16, .v17), .tvOS(.v16, .v17)) { +/// .introspect(.navigationStack, on: .iOS(.v16, .v17)) { /// print(type(of: $0)) // UINavigationController /// } -/// #endif /// } /// } /// ``` +/// +/// ### tvOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// NavigationStack { +/// Text("Root") +/// } +/// .introspect(.navigationStack, on: .tvOS(.v16, .v17)) { +/// print(type(of: $0)) // UINavigationController +/// } +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// Not available. +/// public struct NavigationStackType: IntrospectableViewType {} extension IntrospectableViewType where Self == NavigationStackType { diff --git a/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift b/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift index c21639cac..b1355277d 100644 --- a/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift +++ b/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift @@ -2,6 +2,8 @@ import SwiftUI /// An abstract representation of the `NavigationView` type in SwiftUI, with `.columns` style. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// var body: some View { @@ -9,19 +11,41 @@ import SwiftUI /// Text("Root") /// } /// .navigationViewStyle(DoubleColumnNavigationViewStyle()) -/// #if os(iOS) /// .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UISplitViewController /// } -/// #elseif os(tvOS) +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// NavigationView { +/// Text("Root") +/// } +/// .navigationViewStyle(DoubleColumnNavigationViewStyle()) /// .introspect(.navigationView(style: .columns), on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UINavigationController /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// NavigationView { +/// Text("Root") +/// } +/// .navigationViewStyle(DoubleColumnNavigationViewStyle()) /// .introspect(.navigationView(style: .columns), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSSplitView /// } -/// #endif /// } /// } /// ``` diff --git a/Sources/ViewTypes/NavigationViewWithStackStyle.swift b/Sources/ViewTypes/NavigationViewWithStackStyle.swift index c83d357ff..1fc7397a4 100644 --- a/Sources/ViewTypes/NavigationViewWithStackStyle.swift +++ b/Sources/ViewTypes/NavigationViewWithStackStyle.swift @@ -2,6 +2,24 @@ import SwiftUI /// An abstract representation of the `NavigationView` type in SwiftUI, with `.stack` style. /// +/// ### iOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// NavigationView { +/// Text("Root") +/// } +/// .navigationViewStyle(.stack) +/// .introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UINavigationController +/// } +/// } +/// } +/// ``` +/// +/// ### tvOS +/// /// ```swift /// struct ContentView: View { /// var body: some View { @@ -9,14 +27,17 @@ import SwiftUI /// Text("Root") /// } /// .navigationViewStyle(.stack) -/// #if os(iOS) || os(tvOS) -/// .introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// .introspect(.navigationView(style: .stack), on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UINavigationController /// } -/// #endif /// } /// } /// ``` +/// +/// ### macOS +/// +/// Not available. +/// public struct NavigationViewWithStackStyleType: IntrospectableViewType { public enum Style { case stack diff --git a/Sources/ViewTypes/PickerWithMenuStyle.swift b/Sources/ViewTypes/PickerWithMenuStyle.swift index dc804df75..f2c1f0529 100644 --- a/Sources/ViewTypes/PickerWithMenuStyle.swift +++ b/Sources/ViewTypes/PickerWithMenuStyle.swift @@ -2,6 +2,16 @@ import SwiftUI /// An abstract representation of the `Picker` type in SwiftUI, with `.menu` style. /// +/// ### iOS +/// +/// Not available. +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// /// ```swift /// struct ContentView: View { /// @State var selection = "1" @@ -13,11 +23,9 @@ import SwiftUI /// Text("3").tag("3") /// } /// .pickerStyle(.menu) -/// #if os(macOS) /// .introspect(.picker(style: .menu), on: .macOS(.v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSPopUpButton /// } -/// #endif /// } /// } /// ``` diff --git a/Sources/ViewTypes/PickerWithSegmentedStyle.swift b/Sources/ViewTypes/PickerWithSegmentedStyle.swift index 20ecfecd0..3eee45637 100644 --- a/Sources/ViewTypes/PickerWithSegmentedStyle.swift +++ b/Sources/ViewTypes/PickerWithSegmentedStyle.swift @@ -2,6 +2,8 @@ import SwiftUI /// An abstract representation of the `Picker` type in SwiftUI, with `.segmented` style. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// @State var selection = "1" @@ -13,15 +15,49 @@ import SwiftUI /// Text("3").tag("3") /// } /// .pickerStyle(.segmented) -/// #if os(iOS) || os(tvOS) -/// .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UISegmentedControl /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var selection = "1" +/// +/// var body: some View { +/// Picker("Pick a number", selection: $selection) { +/// Text("1").tag("1") +/// Text("2").tag("2") +/// Text("3").tag("3") +/// } +/// .pickerStyle(.segmented) +/// .introspect(.picker(style: .segmented), on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UISegmentedControl +/// } +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var selection = "1" +/// +/// var body: some View { +/// Picker("Pick a number", selection: $selection) { +/// Text("1").tag("1") +/// Text("2").tag("2") +/// Text("3").tag("3") +/// } +/// .pickerStyle(.segmented) /// .introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSSegmentedControl /// } -/// #endif /// } /// } /// ``` diff --git a/Sources/ViewTypes/PickerWithWheelStyle.swift b/Sources/ViewTypes/PickerWithWheelStyle.swift index 587f2d413..5b3c702bf 100644 --- a/Sources/ViewTypes/PickerWithWheelStyle.swift +++ b/Sources/ViewTypes/PickerWithWheelStyle.swift @@ -2,6 +2,8 @@ import SwiftUI /// An abstract representation of the `Picker` type in SwiftUI, with `.wheel` style. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// @State var selection = "1" @@ -13,14 +15,21 @@ import SwiftUI /// Text("3").tag("3") /// } /// .pickerStyle(.wheel) -/// #if os(iOS) /// .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UIPickerView /// } -/// #endif /// } /// } /// ``` +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// +/// Not available. +/// public struct PickerWithWheelStyleType: IntrospectableViewType { public enum Style { case wheel diff --git a/Sources/ViewTypes/ProgressViewWithCircularStyle.swift b/Sources/ViewTypes/ProgressViewWithCircularStyle.swift index 0f4bcae9f..d237008f8 100644 --- a/Sources/ViewTypes/ProgressViewWithCircularStyle.swift +++ b/Sources/ViewTypes/ProgressViewWithCircularStyle.swift @@ -2,23 +2,48 @@ import SwiftUI /// An abstract representation of the `ProgressView` type in SwiftUI, with `.circular` style. /// +/// ### iOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// ProgressView(value: 0.5) +/// .progressViewStyle(.circular) +/// .introspect(.progressView(style: .circular), on: .iOS(.v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIActivityIndicatorView +/// } +/// } +/// } +/// ``` +/// +/// ### tvOS +/// /// ```swift /// struct ContentView: View { /// var body: some View { /// ProgressView(value: 0.5) /// .progressViewStyle(.circular) -/// #if os(iOS) || os(tvOS) -/// .introspect(.progressView(style: .circular), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17)) { +/// .introspect(.progressView(style: .circular), on: .tvOS(.v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UIActivityIndicatorView /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// ProgressView(value: 0.5) +/// .progressViewStyle(.circular) /// .introspect(.progressView(style: .circular), on: .macOS(.v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSProgressIndicator /// } -/// #endif /// } /// } /// ``` +/// public struct ProgressViewWithCircularStyleType: IntrospectableViewType { public enum Style { case circular diff --git a/Sources/ViewTypes/ProgressViewWithLinearStyle.swift b/Sources/ViewTypes/ProgressViewWithLinearStyle.swift index d9c2dd365..5c667d065 100644 --- a/Sources/ViewTypes/ProgressViewWithLinearStyle.swift +++ b/Sources/ViewTypes/ProgressViewWithLinearStyle.swift @@ -2,23 +2,48 @@ import SwiftUI /// An abstract representation of the `ProgressView` type in SwiftUI, with `.linear` style. /// +/// ### iOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// ProgressView(value: 0.5) +/// .progressViewStyle(.linear) +/// .introspect(.progressView(style: .linear), on: .iOS(.v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIProgressView +/// } +/// } +/// } +/// ``` +/// +/// ### tvOS +/// /// ```swift /// struct ContentView: View { /// var body: some View { /// ProgressView(value: 0.5) /// .progressViewStyle(.linear) -/// #if os(iOS) || os(tvOS) -/// .introspect(.progressView(style: .linear), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17)) { +/// .introspect(.progressView(style: .linear), on: .tvOS(.v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UIProgressView /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// ProgressView(value: 0.5) +/// .progressViewStyle(.linear) /// .introspect(.progressView(style: .linear), on: .macOS(.v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSProgressIndicator /// } -/// #endif /// } /// } /// ``` +/// public struct ProgressViewWithLinearStyleType: IntrospectableViewType { public enum Style { case linear diff --git a/Sources/ViewTypes/ScrollView.swift b/Sources/ViewTypes/ScrollView.swift index b58e79c97..93ef0022f 100644 --- a/Sources/ViewTypes/ScrollView.swift +++ b/Sources/ViewTypes/ScrollView.swift @@ -2,24 +2,51 @@ import SwiftUI /// An abstract representation of the `ScrollView` type in SwiftUI. /// +/// ### iOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// ScrollView { +/// Text("Item") +/// } +/// .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIScrollView +/// } +/// } +/// } +/// ``` +/// +/// ### tvOS +/// /// ```swift /// struct ContentView: View { /// var body: some View { /// ScrollView { /// Text("Item") /// } -/// #if os(iOS) || os(tvOS) -/// .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// .introspect(.scrollView, on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UIScrollView /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// ScrollView { +/// Text("Item") +/// } /// .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSScrollView /// } -/// #endif /// } /// } /// ``` +/// public struct ScrollViewType: IntrospectableViewType {} extension IntrospectableViewType where Self == ScrollViewType { diff --git a/Sources/ViewTypes/SearchField.swift b/Sources/ViewTypes/SearchField.swift index 8e381648d..0bc89d013 100644 --- a/Sources/ViewTypes/SearchField.swift +++ b/Sources/ViewTypes/SearchField.swift @@ -2,6 +2,8 @@ import SwiftUI /// An abstract representation of the search field displayed via the `.searchable` modifier in SwiftUI. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// @State var searchTerm = "" @@ -12,14 +14,36 @@ import SwiftUI /// .searchable(text: $searchTerm) /// } /// .navigationViewStyle(.stack) -/// #if os(iOS) || os(tvOS) -/// .introspect(.searchField, on: .iOS(.v15, .v16, .v17), .tvOS(.v15, .v16, .v17)) { +/// .introspect(.searchField, on: .iOS(.v15, .v16, .v17)) { /// print(type(of: $0)) // UISearchBar /// } -/// #endif /// } /// } /// ``` +/// +/// ### tvOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var searchTerm = "" +/// +/// var body: some View { +/// NavigationView { +/// Text("Root") +/// .searchable(text: $searchTerm) +/// } +/// .navigationViewStyle(.stack) +/// .introspect(.searchField, on: .tvOS(.v15, .v16, .v17)) { +/// print(type(of: $0)) // UISearchBar +/// } +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// Not available. +/// public struct SearchFieldType: IntrospectableViewType {} extension IntrospectableViewType where Self == SearchFieldType { diff --git a/Sources/ViewTypes/Slider.swift b/Sources/ViewTypes/Slider.swift index 16747546b..b8fa3455c 100644 --- a/Sources/ViewTypes/Slider.swift +++ b/Sources/ViewTypes/Slider.swift @@ -2,24 +2,40 @@ import SwiftUI /// An abstract representation of the `Slider` type in SwiftUI. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// @State var selection = 0.5 /// /// var body: some View { /// Slider(value: $selection, in: 0...1) -/// #if os(iOS) /// .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UISlider /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var selection = 0.5 +/// +/// var body: some View { +/// Slider(value: $selection, in: 0...1) /// .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSSlider /// } -/// #endif /// } /// } /// ``` +/// public struct SliderType: IntrospectableViewType {} #if !os(tvOS) diff --git a/Sources/ViewTypes/Stepper.swift b/Sources/ViewTypes/Stepper.swift index 61b584e97..8b1e37205 100644 --- a/Sources/ViewTypes/Stepper.swift +++ b/Sources/ViewTypes/Stepper.swift @@ -2,24 +2,40 @@ import SwiftUI /// An abstract representation of the `Stepper` type in SwiftUI. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// @State var selection = 5 /// /// var body: some View { /// Stepper("Select a number", value: $selection, in: 0...10) -/// #if os(iOS) /// .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UIStepper /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var selection = 5 +/// +/// var body: some View { +/// Stepper("Select a number", value: $selection, in: 0...10) /// .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSStepper /// } -/// #endif /// } /// } /// ``` +/// public struct StepperType: IntrospectableViewType {} #if !os(tvOS) diff --git a/Sources/ViewTypes/TabView.swift b/Sources/ViewTypes/TabView.swift index 5966130e8..bb886b406 100644 --- a/Sources/ViewTypes/TabView.swift +++ b/Sources/ViewTypes/TabView.swift @@ -2,6 +2,24 @@ import SwiftUI /// An abstract representation of the `TabView` type in SwiftUI. /// +/// ### iOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// TabView { +/// Text("Tab 1").tabItem { Text("Tab 1") } +/// Text("Tab 2").tabItem { Text("Tab 2") } +/// } +/// .introspect(.tabView, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UITabBarController +/// } +/// } +/// } +/// ``` +/// +/// ### tvOS +/// /// ```swift /// struct ContentView: View { /// var body: some View { @@ -9,18 +27,29 @@ import SwiftUI /// Text("Tab 1").tabItem { Text("Tab 1") } /// Text("Tab 2").tabItem { Text("Tab 2") } /// } -/// #if os(iOS) || os(tvOS) -/// .introspect(.tabView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// .introspect(.tabView, on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UITabBarController /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// TabView { +/// Text("Tab 1").tabItem { Text("Tab 1") } +/// Text("Tab 2").tabItem { Text("Tab 2") } +/// } /// .introspect(.tabView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSTabView /// } -/// #endif /// } /// } /// ``` +/// public struct TabViewType: IntrospectableViewType {} extension IntrospectableViewType where Self == TabViewType { diff --git a/Sources/ViewTypes/TabViewWithPageStyle.swift b/Sources/ViewTypes/TabViewWithPageStyle.swift index 62cefffb5..30e028b75 100644 --- a/Sources/ViewTypes/TabViewWithPageStyle.swift +++ b/Sources/ViewTypes/TabViewWithPageStyle.swift @@ -2,6 +2,8 @@ import SwiftUI /// An abstract representation of the `TabView` type in SwiftUI, with `.page` style. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// var body: some View { @@ -10,14 +12,34 @@ import SwiftUI /// Text("Page 2").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.blue) /// } /// .tabViewStyle(.page(indexDisplayMode: .always)) -/// #if os(iOS) || os(tvOS) -/// .introspect(.tabView(style: .page), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17)) { +/// .introspect(.tabView(style: .page), on: .iOS(.v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UICollectionView /// } -/// #endif /// } /// } /// ``` +/// +/// ### tvOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// TabView { +/// Text("Page 1").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red) +/// Text("Page 2").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.blue) +/// } +/// .tabViewStyle(.page(indexDisplayMode: .always)) +/// .introspect(.tabView(style: .page), on: .tvOS(.v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UICollectionView +/// } +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// Not available. +/// public struct TabViewWithPageStyleType: IntrospectableViewType { public enum Style { case page diff --git a/Sources/ViewTypes/Table.swift b/Sources/ViewTypes/Table.swift index ea002aff0..134ebb228 100644 --- a/Sources/ViewTypes/Table.swift +++ b/Sources/ViewTypes/Table.swift @@ -2,6 +2,8 @@ import SwiftUI /// An abstract representation of the `Table` type in SwiftUI, with any style. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// struct Purchase: Identifiable { @@ -20,26 +22,54 @@ import SwiftUI /// 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)) /// } -/// #if os(iOS) /// .introspect(.table, on: .iOS(.v16, .v17)) { /// print(type(of: $0)) // UICollectionView /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// struct Purchase: Identifiable { +/// let id = UUID() +/// let price: Decimal +/// } +/// +/// 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")) +/// } +/// } rows: { +/// TableRow(Purchase(price: 20)) +/// TableRow(Purchase(price: 50)) +/// TableRow(Purchase(price: 75)) +/// } /// .introspect(.table, on: .macOS(.v12, .v13, .v14)) { /// print(type(of: $0)) // NSTableView /// } -/// #endif /// } /// } /// ``` +/// public struct TableType: IntrospectableViewType {} #if os(iOS) || os(macOS) diff --git a/Sources/ViewTypes/TextEditor.swift b/Sources/ViewTypes/TextEditor.swift index b7c81c067..0991f104b 100644 --- a/Sources/ViewTypes/TextEditor.swift +++ b/Sources/ViewTypes/TextEditor.swift @@ -2,21 +2,36 @@ import SwiftUI /// An abstract representation of the `TextEditor` type in SwiftUI. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// @State var text = "Lorem ipsum" /// /// var body: some View { /// TextEditor(text: $text) -/// #if os(iOS) /// .introspect(.textEditor, on: .iOS(.v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UITextView /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var text = "Lorem ipsum" +/// +/// var body: some View { +/// TextEditor(text: $text) /// .introspect(.textEditor, on: .macOS(.v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSTextView /// } -/// #endif /// } /// } /// ``` diff --git a/Sources/ViewTypes/TextField.swift b/Sources/ViewTypes/TextField.swift index ae6d0f11b..5214f2428 100644 --- a/Sources/ViewTypes/TextField.swift +++ b/Sources/ViewTypes/TextField.swift @@ -2,21 +2,47 @@ import SwiftUI /// An abstract representation of the `TextField` type in SwiftUI. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// @State var text = "Lorem ipsum" /// /// var body: some View { /// TextField("Text Field", text: $text) -/// #if os(iOS) || os(tvOS) -/// .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UITextField /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var text = "Lorem ipsum" +/// +/// var body: some View { +/// TextField("Text Field", text: $text) +/// .introspect(.textField, on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UITextField +/// } +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var text = "Lorem ipsum" +/// +/// var body: some View { +/// TextField("Text Field", text: $text) /// .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSTextField /// } -/// #endif /// } /// } /// ``` diff --git a/Sources/ViewTypes/TextFieldWithVerticalAxis.swift b/Sources/ViewTypes/TextFieldWithVerticalAxis.swift index 983a8dfcb..d1472bedc 100644 --- a/Sources/ViewTypes/TextFieldWithVerticalAxis.swift +++ b/Sources/ViewTypes/TextFieldWithVerticalAxis.swift @@ -2,28 +2,51 @@ import SwiftUI /// An abstract representation of the `TextField` type in SwiftUI, with `.vertical` axis. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// @State var text = "Lorem ipsum" /// /// var body: some View { /// TextField("Text Field", text: $text, axis: .vertical) -/// #if os(iOS) /// .introspect(.textField(axis: .vertical), on: .iOS(.v16, .v17)) { /// print(type(of: $0)) // UITextView /// } -/// #elseif os(tvOS) +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var text = "Lorem ipsum" +/// +/// var body: some View { +/// TextField("Text Field", text: $text, axis: .vertical) /// .introspect(.textField(axis: .vertical), on: .tvOS(.v16, .v17)) { /// print(type(of: $0)) // UITextField /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var text = "Lorem ipsum" +/// +/// var body: some View { +/// TextField("Text Field", text: $text, axis: .vertical) /// .introspect(.textField(axis: .vertical), on: .macOS(.v13, .v14)) { /// print(type(of: $0)) // NSTextField /// } -/// #endif /// } /// } /// ``` +/// public struct TextFieldWithVerticalAxisType: IntrospectableViewType { public enum Axis { case vertical diff --git a/Sources/ViewTypes/Toggle.swift b/Sources/ViewTypes/Toggle.swift index 335c3c5dd..d61d29a30 100644 --- a/Sources/ViewTypes/Toggle.swift +++ b/Sources/ViewTypes/Toggle.swift @@ -2,24 +2,40 @@ import SwiftUI /// An abstract representation of the `Toggle` type in SwiftUI. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// @State var isOn = false -/// +/// /// var body: some View { /// Toggle("Toggle", isOn: $isOn) -/// #if os(iOS) /// .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UISwitch /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var isOn = false +/// +/// var body: some View { +/// Toggle("Toggle", isOn: $isOn) /// .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSButton /// } -/// #endif /// } /// } /// ``` +/// public struct ToggleType: IntrospectableViewType {} #if !os(tvOS) diff --git a/Sources/ViewTypes/ToggleWithButtonStyle.swift b/Sources/ViewTypes/ToggleWithButtonStyle.swift index 0851d6084..18bc80d6d 100644 --- a/Sources/ViewTypes/ToggleWithButtonStyle.swift +++ b/Sources/ViewTypes/ToggleWithButtonStyle.swift @@ -2,6 +2,16 @@ import SwiftUI /// An abstract representation of the `Toggle` type in SwiftUI, with `.button` style. /// +/// ### iOS +/// +/// Not available. +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// /// ```swift /// struct ContentView: View { /// @State var isOn = false @@ -9,11 +19,9 @@ import SwiftUI /// var body: some View { /// Toggle("Toggle", isOn: $isOn) /// .toggleStyle(.button) -/// #if os(macOS) /// .introspect(.toggle(style: .button), on: .macOS(.v12, .v13, .v14)) { /// print(type(of: $0)) // NSButton /// } -/// #endif /// } /// } /// ``` diff --git a/Sources/ViewTypes/ToggleWithCheckboxStyle.swift b/Sources/ViewTypes/ToggleWithCheckboxStyle.swift index 11098bc46..e33281bc4 100644 --- a/Sources/ViewTypes/ToggleWithCheckboxStyle.swift +++ b/Sources/ViewTypes/ToggleWithCheckboxStyle.swift @@ -2,6 +2,16 @@ import SwiftUI /// An abstract representation of the `Toggle` type in SwiftUI, with `.checkbox` style. /// +/// ### iOS +/// +/// Not available. +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// /// ```swift /// struct ContentView: View { /// @State var isOn = false @@ -9,11 +19,9 @@ import SwiftUI /// var body: some View { /// Toggle("Checkbox", isOn: $isOn) /// .toggleStyle(.checkbox) -/// #if os(macOS) /// .introspect(.toggle(style: .checkbox), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSButton /// } -/// #endif /// } /// } /// ``` diff --git a/Sources/ViewTypes/ToggleWithSwitchStyle.swift b/Sources/ViewTypes/ToggleWithSwitchStyle.swift index b49780149..07d56e249 100644 --- a/Sources/ViewTypes/ToggleWithSwitchStyle.swift +++ b/Sources/ViewTypes/ToggleWithSwitchStyle.swift @@ -2,6 +2,8 @@ import SwiftUI /// An abstract representation of the `Toggle` type in SwiftUI, with `.switch` style. /// +/// ### iOS +/// /// ```swift /// struct ContentView: View { /// @State var isOn = false @@ -9,15 +11,29 @@ import SwiftUI /// var body: some View { /// Toggle("Switch", isOn: $isOn) /// .toggleStyle(.switch) -/// #if os(iOS) /// .introspect(.toggle(style: .switch), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UISwitch /// } -/// #elseif os(macOS) +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var isOn = false +/// +/// var body: some View { +/// Toggle("Switch", isOn: $isOn) +/// .toggleStyle(.switch) /// .introspect(.toggle(style: .switch), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { /// print(type(of: $0)) // NSSwitch /// } -/// #endif /// } /// } /// ``` diff --git a/Sources/ViewTypes/View.swift b/Sources/ViewTypes/View.swift index 19bc86fe7..e299dc370 100644 --- a/Sources/ViewTypes/View.swift +++ b/Sources/ViewTypes/View.swift @@ -2,21 +2,42 @@ import SwiftUI /// An abstract representation of a generic view type in SwiftUI. /// +/// ### iOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// NavigationView { +/// Text("Root") +/// .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIViewController +/// } +/// } +/// .navigationViewStyle(.stack) +/// } +/// } +/// ``` +/// +/// ### tvOS +/// /// ```swift /// struct ContentView: View { /// var body: some View { /// NavigationView { /// Text("Root") -/// #if os(iOS) || os(tvOS) -/// .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// .introspect(.view, on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { /// print(type(of: $0)) // UIViewController /// } -/// #endif /// } /// .navigationViewStyle(.stack) /// } /// } /// ``` +/// +/// ### macOS +/// +/// Not available. +/// public struct ViewType: IntrospectableViewType { public var scope: IntrospectionScope { [.receiver, .ancestor] } } From dafd88cf0cc09d906dcef9d042743ae13fcff0d4 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sun, 25 Jun 2023 23:40:57 +0100 Subject: [PATCH 061/116] Bump to 0.6.3 (#263) --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d62a6b96f..9918ef536 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Changelog ## master +## [0.6.3] + ### SwiftUIIntrospect - Changed: disabled accessibility for introspection views (#261) From 7ff754609ecfad63c48c10d4675943f4dbf23350 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 26 Jun 2023 16:08:15 +0100 Subject: [PATCH 062/116] Add `VideoPlayer` introspection (#264) --- Sources/ViewTypes/VideoPlayer.swift | 80 ++++++++++++++++++++ Tests/Tests.xcodeproj/project.pbxproj | 10 ++- Tests/Tests/ViewTypes/VideoPlayerTests.swift | 58 ++++++++++++++ docs/SwiftUIIntrospect.md | 1 + 4 files changed, 146 insertions(+), 3 deletions(-) create mode 100644 Sources/ViewTypes/VideoPlayer.swift create mode 100644 Tests/Tests/ViewTypes/VideoPlayerTests.swift diff --git a/Sources/ViewTypes/VideoPlayer.swift b/Sources/ViewTypes/VideoPlayer.swift new file mode 100644 index 000000000..8e2376485 --- /dev/null +++ b/Sources/ViewTypes/VideoPlayer.swift @@ -0,0 +1,80 @@ +import SwiftUI + +/// An abstract representation of the `VideoPlayer` type in SwiftUI. +/// +/// ### iOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// VideoPlayer(player: AVPlayer(url: URL(string: "https://bit.ly/swswift")!)) +/// .introspect(.videoPlayer, on: .iOS(.v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // AVPlayerViewController +/// } +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// VideoPlayer(player: AVPlayer(url: URL(string: "https://bit.ly/swswift")!)) +/// .introspect(.videoPlayer, on: .tvOS(.v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // AVPlayerViewController +/// } +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// VideoPlayer(player: AVPlayer(url: URL(string: "https://bit.ly/swswift")!)) +/// .introspect(.videoPlayer, on: .macOS(.v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // AVPlayerView +/// } +/// } +/// } +/// ``` +public struct VideoPlayerType: IntrospectableViewType {} + +#if canImport(AVKit) +import AVKit + +extension IntrospectableViewType where Self == VideoPlayerType { + public static var videoPlayer: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + @available(*, unavailable, message: "VideoPlayer 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) + public static let v17 = Self(for: .v17) +} + +extension tvOSViewVersion { + @available(*, unavailable, message: "VideoPlayer 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) + public static let v17 = Self(for: .v17) +} +#elseif canImport(AppKit) +extension macOSViewVersion { + @available(*, unavailable, message: "VideoPlayer 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) + public static let v14 = Self(for: .v14) +} +#endif +#endif diff --git a/Tests/Tests.xcodeproj/project.pbxproj b/Tests/Tests.xcodeproj/project.pbxproj index 5d95579c4..bd665b047 100644 --- a/Tests/Tests.xcodeproj/project.pbxproj +++ b/Tests/Tests.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + D503B2AC2A49BFE300027F5F /* VideoPlayerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D503B2AB2A49BFE300027F5F /* VideoPlayerTests.swift */; }; 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 */; }; @@ -126,6 +127,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + D503B2AB2A49BFE300027F5F /* VideoPlayerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerTests.swift; sourceTree = ""; }; 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; }; @@ -233,14 +235,14 @@ D57506912A27EE4700A628E4 /* DatePickerWithWheelStyleTests.swift */, D57506872A27CB9800A628E4 /* FormTests.swift */, D57506892A27CE7900A628E4 /* FormWithGroupedStyleTests.swift */, + D58119C32A211B8A0081F853 /* ListCellTests.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 */, + D57506832A27C8D400A628E4 /* ListWithSidebarStyleTests.swift */, D58547F92A1D12270068ADF4 /* NavigationSplitViewTests.swift */, D58547F72A1CDD740068ADF4 /* NavigationStackTests.swift */, D5F8D5EE2A1E87950054E9AB /* NavigationViewWithColumnsStyleTests.swift */, @@ -251,6 +253,7 @@ D575069B2A27F68700A628E4 /* ProgressViewWithCircularStyleTests.swift */, D575069D2A27F80E00A628E4 /* ProgressViewWithLinearStyleTests.swift */, D50FFE8D2A17E2A400C32641 /* ScrollViewTests.swift */, + D57506A12A281B9C00A628E4 /* SearchFieldTests.swift */, D58119CF2A23A62C0081F853 /* SliderTests.swift */, D58119D12A23A77C0081F853 /* StepperTests.swift */, D575069F2A27FC0400A628E4 /* TableTests.swift */, @@ -258,12 +261,12 @@ 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 */, + D503B2AB2A49BFE300027F5F /* VideoPlayerTests.swift */, D58119C52A227E930081F853 /* ViewTests.swift */, ); path = ViewTypes; @@ -577,6 +580,7 @@ D57506782A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift in Sources */, D58119CE2A23A4A70081F853 /* TabViewWithPageStyleTests.swift in Sources */, D575069A2A27F48D00A628E4 /* DatePickerWithFieldStyleTests.swift in Sources */, + D503B2AC2A49BFE300027F5F /* VideoPlayerTests.swift in Sources */, D57506A02A27FC0400A628E4 /* TableTests.swift in Sources */, D58119D42A23AC100081F853 /* DatePickerTests.swift in Sources */, D575068C2A27D40500A628E4 /* ToggleWithSwitchStyleTests.swift in Sources */, diff --git a/Tests/Tests/ViewTypes/VideoPlayerTests.swift b/Tests/Tests/ViewTypes/VideoPlayerTests.swift new file mode 100644 index 000000000..04c5df7fe --- /dev/null +++ b/Tests/Tests/ViewTypes/VideoPlayerTests.swift @@ -0,0 +1,58 @@ +#if canImport(AVKit) +import AVKit +import SwiftUI +import SwiftUIIntrospect +import XCTest + +@available(iOS 14, tvOS 14, macOS 11, *) +final class VideoPlayerTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformVideoPlayer = AVPlayerViewController + #elseif canImport(AppKit) + typealias PlatformVideoPlayer = AVPlayerView + #endif + + func testVideoPlayer() throws { + guard #available(iOS 14, tvOS 14, macOS 11, *) else { + throw XCTSkip() + } + + let videoURL0 = URL(string: "https://bit.ly/swswift#1")! + let videoURL1 = URL(string: "https://bit.ly/swswift#2")! + let videoURL2 = URL(string: "https://bit.ly/swswift#3")! + + XCTAssertViewIntrospection(of: PlatformVideoPlayer.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + VideoPlayer(player: AVPlayer(url: videoURL0)) + #if os(iOS) || os(tvOS) + .introspect(.videoPlayer, on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy0) + #elseif os(macOS) + .introspect(.videoPlayer, on: .macOS(.v11, .v12, .v13, .v14), customize: spy0) + #endif + + VideoPlayer(player: AVPlayer(url: videoURL1)) + #if os(iOS) || os(tvOS) + .introspect(.videoPlayer, on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy1) + #elseif os(macOS) + .introspect(.videoPlayer, on: .macOS(.v11, .v12, .v13, .v14), customize: spy1) + #endif + + VideoPlayer(player: AVPlayer(url: videoURL2)) + #if os(iOS) || os(tvOS) + .introspect(.videoPlayer, on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy2) + #elseif os(macOS) + .introspect(.videoPlayer, on: .macOS(.v11, .v12, .v13, .v14), customize: spy2) + #endif + } + } extraAssertions: { + XCTAssertEqual(($0[safe: 0]?.player?.currentItem?.asset as? AVURLAsset)?.url, videoURL0) + XCTAssertEqual(($0[safe: 1]?.player?.currentItem?.asset as? AVURLAsset)?.url, videoURL1) + XCTAssertEqual(($0[safe: 2]?.player?.currentItem?.asset as? AVURLAsset)?.url, videoURL2) + } + } +} +#endif diff --git a/docs/SwiftUIIntrospect.md b/docs/SwiftUIIntrospect.md index 451683e9f..d9775bbe8 100644 --- a/docs/SwiftUIIntrospect.md +++ b/docs/SwiftUIIntrospect.md @@ -125,6 +125,7 @@ Introspection - [`Toggle` with `button` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/togglewithbuttonstyletype) - [`Toggle` with `checkbox` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/togglewithcheckboxstyletype) - [`Toggle` with `switch` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/togglewithswitchstyletype) +- [`VideoPlayer`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/videoplayertype) - [`View`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/viewtype) **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). From 3cfdd8baf035615f83be358c4786fdec0c55d977 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 26 Jun 2023 18:16:19 +0100 Subject: [PATCH 063/116] Add `SignInWithAppleButton` introspection (#265) --- Sources/ViewTypes/SignInWithAppleButton.swift | 90 +++++++++++++++++++ Tests/Tests.xcodeproj/project.pbxproj | 4 + .../SignInWithAppleButtonTests.swift | 50 +++++++++++ 3 files changed, 144 insertions(+) create mode 100644 Sources/ViewTypes/SignInWithAppleButton.swift create mode 100644 Tests/Tests/ViewTypes/SignInWithAppleButtonTests.swift diff --git a/Sources/ViewTypes/SignInWithAppleButton.swift b/Sources/ViewTypes/SignInWithAppleButton.swift new file mode 100644 index 000000000..4cfcbdd04 --- /dev/null +++ b/Sources/ViewTypes/SignInWithAppleButton.swift @@ -0,0 +1,90 @@ +import SwiftUI + +/// An abstract representation of the `SignInWithAppleButton` type in SwiftUI. +/// +/// ### iOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// SignInWithAppleButton(.signIn) { request in +/// request.requestedScopes = [.fullName, .email] +/// } onCompletion: { result in +/// // do something with result +/// } +/// .introspect(.signInWithAppleButton, on: .iOS(.v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // ASAuthorizationAppleIDButton +/// } +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// SignInWithAppleButton(.signIn) { request in +/// request.requestedScopes = [.fullName, .email] +/// } onCompletion: { result in +/// // do something with result +/// } +/// .introspect(.signInWithAppleButton, on: .tvOS(.v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // ASAuthorizationAppleIDButton +/// } +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// SignInWithAppleButton(.signIn) { request in +/// request.requestedScopes = [.fullName, .email] +/// } onCompletion: { result in +/// // do something with result +/// } +/// .introspect(.signInWithAppleButton, on: .macOS(.v11, .v12, .v13, .v14),) { +/// print(type(of: $0)) // ASAuthorizationAppleIDButton +/// } +/// } +/// } +/// ``` +public struct SignInWithAppleButtonType: IntrospectableViewType {} + +#if canImport(AuthenticationServices) +import AuthenticationServices + +extension IntrospectableViewType where Self == SignInWithAppleButtonType { + public static var signInWithAppleButton: Self { .init() } +} + +extension iOSViewVersion { + @available(*, unavailable, message: "SignInWithAppleButton 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) + public static let v17 = Self(for: .v17) +} + +extension tvOSViewVersion { + @available(*, unavailable, message: "SignInWithAppleButton 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) + public static let v17 = Self(for: .v17) +} + +extension macOSViewVersion { + @available(*, unavailable, message: "SignInWithAppleButton 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) + public static let v14 = Self(for: .v14) +} +#endif diff --git a/Tests/Tests.xcodeproj/project.pbxproj b/Tests/Tests.xcodeproj/project.pbxproj index bd665b047..b595b865a 100644 --- a/Tests/Tests.xcodeproj/project.pbxproj +++ b/Tests/Tests.xcodeproj/project.pbxproj @@ -56,6 +56,7 @@ 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 */; }; + D568532C2A49DBB10039A99F /* SignInWithAppleButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D568532B2A49DBB10039A99F /* SignInWithAppleButtonTests.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 */; }; @@ -133,6 +134,7 @@ 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 = ""; }; + D568532B2A49DBB10039A99F /* SignInWithAppleButtonTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInWithAppleButtonTests.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 = ""; }; @@ -261,6 +263,7 @@ D58119CD2A23A4A70081F853 /* TabViewWithPageStyleTests.swift */, D58119C92A239BAC0081F853 /* TextEditorTests.swift */, D5B67B832A0D318F007D5D9B /* TextFieldTests.swift */, + D568532B2A49DBB10039A99F /* SignInWithAppleButtonTests.swift */, D5AD0D902A114B98003D8DEC /* TextFieldWithVerticalAxisTests.swift */, D58119C72A22AC130081F853 /* ToggleTests.swift */, D575068D2A27D4DC00A628E4 /* ToggleWithButtonStyleTests.swift */, @@ -556,6 +559,7 @@ D575067A2A27BF6C00A628E4 /* PickerWithMenuStyleTests.swift in Sources */, D57506922A27EE4700A628E4 /* DatePickerWithWheelStyleTests.swift in Sources */, D57506822A27C74600A628E4 /* ListWithInsetGroupedStyleTests.swift in Sources */, + D568532C2A49DBB10039A99F /* SignInWithAppleButtonTests.swift in Sources */, D575068A2A27CE7900A628E4 /* FormWithGroupedStyleTests.swift in Sources */, D575067C2A27C24600A628E4 /* ListWithPlainStyleTests.swift in Sources */, D58119CA2A239BAC0081F853 /* TextEditorTests.swift in Sources */, diff --git a/Tests/Tests/ViewTypes/SignInWithAppleButtonTests.swift b/Tests/Tests/ViewTypes/SignInWithAppleButtonTests.swift new file mode 100644 index 000000000..3d4ece250 --- /dev/null +++ b/Tests/Tests/ViewTypes/SignInWithAppleButtonTests.swift @@ -0,0 +1,50 @@ +#if canImport(AuthenticationServices) +import AuthenticationServices +import SwiftUI +import SwiftUIIntrospect +import XCTest + +@available(iOS 14, tvOS 14, macOS 11, *) +final class SignInWithAppleButtonTests: XCTestCase { + typealias PlatformSignInWithAppleButton = ASAuthorizationAppleIDButton + + func testSignInWithAppleButton() throws { + guard #available(iOS 14, tvOS 14, macOS 11, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformSignInWithAppleButton.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + SignInWithAppleButton(.continue, onRequest: { _ in }, onCompletion: { _ in }) + .introspect( + .signInWithAppleButton, + on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .macOS(.v11, .v12, .v13, .v14), + customize: spy0 + ) + + SignInWithAppleButton(.signIn, onRequest: { _ in }, onCompletion: { _ in }) + .introspect( + .signInWithAppleButton, + on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .macOS(.v11, .v12, .v13, .v14), + customize: spy1 + ) + + SignInWithAppleButton(.signUp, onRequest: { _ in }, onCompletion: { _ in }) + .introspect( + .signInWithAppleButton, + on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .macOS(.v11, .v12, .v13, .v14), + customize: spy2 + ) + } + } extraAssertions: { + XCTAssertNotIdentical($0[safe: 0], $0[safe: 1]) + XCTAssertNotIdentical($0[safe: 0], $0[safe: 2]) + XCTAssertNotIdentical($0[safe: 1], $0[safe: 2]) + } + } +} +#endif From c12f3e625f4b4927991c3ac011c805a620a29c1a Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 26 Jun 2023 19:02:47 +0100 Subject: [PATCH 064/116] Add missing `SignInWithAppleButton` documentation link (#267) --- docs/SwiftUIIntrospect.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/SwiftUIIntrospect.md b/docs/SwiftUIIntrospect.md index d9775bbe8..d031f67c0 100644 --- a/docs/SwiftUIIntrospect.md +++ b/docs/SwiftUIIntrospect.md @@ -113,6 +113,7 @@ Introspection - [`ProgressView` with `.linear` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/progressviewwithlinearstyletype) - [`ScrollView`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/scrollviewtype) - [`.searchable`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/searchfieldtype) +- [`SignInWithAppleButton`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/SignInWithAppleButtonType) - [`Slider`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/slidertype) - [`Stepper`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/steppertype) - [`Table`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/tabletype) From 5299d2c2c86fd38869f63e56a38770e933f893b3 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 26 Jun 2023 20:25:50 +0100 Subject: [PATCH 065/116] Improve accuracy of `View` introspection (#266) --- CHANGELOG.md | 5 ++ Examples/Showcase/Showcase/ContentView.swift | 67 +++++++++++++++----- Sources/Introspect.swift | 8 +++ Sources/IntrospectionView.swift | 25 +++++++- Sources/ViewTypes/View.swift | 56 ++++++++++------ Tests/Tests/ViewTypes/ViewTests.swift | 43 +++++++------ 6 files changed, 148 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9918ef536..9d8ec54ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ Changelog ## master +- Added: `VideoPlayer` introspection (#264) +- Added: `SignInWithAppleButton` introspection (#265) +- Added: `View` introspection on macOS (#266) +- Improved: `View` introspection accuracy (#266) + ## [0.6.3] ### SwiftUIIntrospect diff --git a/Examples/Showcase/Showcase/ContentView.swift b/Examples/Showcase/Showcase/ContentView.swift index 6d00095b7..2451325fa 100644 --- a/Examples/Showcase/Showcase/ContentView.swift +++ b/Examples/Showcase/Showcase/ContentView.swift @@ -16,10 +16,10 @@ struct ContentView: View { NavigationShowcase() .tabItem { Text("Navigation") } .tag(2) - ViewControllerShowcase() - .tabItem { Text("ViewController") } - .tag(3) #endif + GenericViewShowcase() + .tabItem { Text("Generic View") } + .tag(3) SimpleElementsShowcase() .tabItem { Text("Simple elements") } .tag(4) @@ -196,25 +196,58 @@ struct NavigationShowcase: View { } } -#if os(iOS) || os(tvOS) -struct ViewControllerShowcase: View { +struct GenericViewShowcase: View { var body: some View { - NavigationView { - VStack { - Text(".introspect(.view, ...)") - .lineLimit(1) - .minimumScaleFactor(0.5) - .padding(.horizontal, 12) - .font(.system(.subheadline, design: .monospaced)) - } + VStack(spacing: 10) { + Text(".introspect(.view, ...)") + .lineLimit(1) + .minimumScaleFactor(0.5) + .font(.system(.subheadline, design: .monospaced)) + #if os(iOS) || os(tvOS) + .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { view in + view.backgroundColor = .cyan + } + #elseif os(macOS) + .introspect(.view, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { view in + view.layer?.backgroundColor = NSColor.cyan.cgColor + } + #endif + + Button("A button", action: {}) + .padding(5) + #if os(iOS) || os(tvOS) + .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { view in + view.backgroundColor = .lightGray + } + #elseif os(macOS) + .introspect(.view, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { view in + view.layer?.backgroundColor = NSColor.lightGray.cgColor + } + #endif + + Image(systemName: "scribble") + #if os(iOS) || os(tvOS) + .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { view in + view.backgroundColor = .blue + } + #elseif os(macOS) + .introspect(.view, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { view in + view.layer?.backgroundColor = NSColor.blue.cgColor + } + #endif } - .navigationViewStyle(.stack) - .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { viewController in - viewController.children.first?.view.backgroundColor = .cyan + .padding() + #if os(iOS) || os(tvOS) + .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { view in + view.backgroundColor = .red } + #elseif os(macOS) + .introspect(.view, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { view in + view.layer?.backgroundColor = NSColor.red.cgColor + } + #endif } } -#endif struct SimpleElementsShowcase: View { diff --git a/Sources/Introspect.swift b/Sources/Introspect.swift index 02c255364..cabb10b3a 100644 --- a/Sources/Introspect.swift +++ b/Sources/Introspect.swift @@ -46,6 +46,12 @@ struct IntrospectModifier some View { if let selector { content + .background( + // boxes up content without affecting appearance or behavior, for more accurate `.view` introspection + Color.white + .opacity(0) + .accessibility(hidden: true) + ) .background( IntrospectionAnchorView(id: id) .frame(width: 0, height: 0) @@ -116,6 +122,7 @@ extension PlatformEntity { return commonAncestor .allDescendants(between: backEntity~, and: frontEntity~) + .filter { !$0.isIntrospectionPlatformEntity } .compactMap { $0 as? PlatformSpecificEntity } .first } @@ -125,6 +132,7 @@ extension PlatformEntity { ) -> PlatformSpecificEntity? { self.ancestors .lazy + .filter { !$0.isIntrospectionPlatformEntity } .compactMap { $0 as? PlatformSpecificEntity } .first } diff --git a/Sources/IntrospectionView.swift b/Sources/IntrospectionView.swift index 681666c79..6842003c0 100644 --- a/Sources/IntrospectionView.swift +++ b/Sources/IntrospectionView.swift @@ -56,6 +56,7 @@ struct IntrospectionAnchorView: PlatformViewControllerRepresentable { final class IntrospectionAnchorPlatformViewController: PlatformViewController { init(id: IntrospectionViewID) { super.init(nibName: nil, bundle: nil) + self.isIntrospectionPlatformEntity = true IntrospectionStore.shared[id, default: .init()].anchor = self } @@ -64,9 +65,15 @@ final class IntrospectionAnchorPlatformViewController: PlatformViewController { fatalError("init(coder:) has not been implemented") } - #if canImport(AppKit) && !targetEnvironment(macCatalyst) + #if canImport(UIKit) + override func viewDidLoad() { + super.viewDidLoad() + view.isIntrospectionPlatformEntity = true + } + #elseif canImport(AppKit) override func loadView() { view = NSView() + view.isIntrospectionPlatformEntity = true } #endif } @@ -152,6 +159,7 @@ final class IntrospectionPlatformViewController: PlatformViewController { } handler?(self) } + self.isIntrospectionPlatformEntity = true IntrospectionStore.shared[id, default: .init()].controller = self } @@ -164,6 +172,7 @@ final class IntrospectionPlatformViewController: PlatformViewController { override func viewDidLoad() { super.viewDidLoad() view.introspectionController = self + view.isIntrospectionPlatformEntity = true handler?() } @@ -185,6 +194,7 @@ final class IntrospectionPlatformViewController: PlatformViewController { override func loadView() { view = NSView() view.introspectionController = self + view.isIntrospectionPlatformEntity = true } override func viewDidLoad() { @@ -213,3 +223,16 @@ extension PlatformView { } } } + +extension PlatformEntity { + var isIntrospectionPlatformEntity: Bool { + get { + let key = unsafeBitCast(Selector(#function), to: UnsafeRawPointer.self) + return objc_getAssociatedObject(self, key) as? Bool ?? false + } + set { + let key = unsafeBitCast(Selector(#function), to: UnsafeRawPointer.self) + objc_setAssociatedObject(self, key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } +} diff --git a/Sources/ViewTypes/View.swift b/Sources/ViewTypes/View.swift index e299dc370..1a2c8e770 100644 --- a/Sources/ViewTypes/View.swift +++ b/Sources/ViewTypes/View.swift @@ -1,19 +1,19 @@ import SwiftUI -/// An abstract representation of a generic view type in SwiftUI. +/// An abstract representation of a generic SwiftUI view type. /// /// ### iOS /// /// ```swift /// struct ContentView: View { /// var body: some View { -/// NavigationView { -/// Text("Root") -/// .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { -/// print(type(of: $0)) // UIViewController -/// } +/// HStack { +/// Image(systemName: "scribble") +/// Text("Some text") +/// } +/// .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // some subclass of UIView /// } -/// .navigationViewStyle(.stack) /// } /// } /// ``` @@ -23,31 +23,41 @@ import SwiftUI /// ```swift /// struct ContentView: View { /// var body: some View { -/// NavigationView { -/// Text("Root") -/// .introspect(.view, on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { -/// print(type(of: $0)) // UIViewController -/// } +/// HStack { +/// Image(systemName: "scribble") +/// Text("Some text") +/// } +/// .introspect(.view, on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // some subclass of UIView /// } -/// .navigationViewStyle(.stack) /// } /// } /// ``` /// /// ### macOS /// -/// Not available. +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// HStack { +/// Image(systemName: "scribble") +/// Text("Some text") +/// } +/// .introspect(.view, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // some subclass of NSView +/// } +/// } +/// } +/// ``` /// -public struct ViewType: IntrospectableViewType { - public var scope: IntrospectionScope { [.receiver, .ancestor] } -} +public struct ViewType: IntrospectableViewType {} extension IntrospectableViewType where Self == ViewType { public static var view: Self { .init() } } #if canImport(UIKit) -extension iOSViewVersion { +extension iOSViewVersion { public static let v13 = Self(for: .v13) public static let v14 = Self(for: .v14) public static let v15 = Self(for: .v15) @@ -55,11 +65,19 @@ extension iOSViewVersion { public static let v17 = Self(for: .v17) } -extension tvOSViewVersion { +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 v17 = Self(for: .v17) } +#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) + public static let v14 = Self(for: .v14) +} #endif diff --git a/Tests/Tests/ViewTypes/ViewTests.swift b/Tests/Tests/ViewTypes/ViewTests.swift index 133029b42..55fd1e8f8 100644 --- a/Tests/Tests/ViewTypes/ViewTests.swift +++ b/Tests/Tests/ViewTypes/ViewTests.swift @@ -1,34 +1,39 @@ -#if !os(macOS) import SwiftUI import SwiftUIIntrospect import XCTest final class ViewTests: XCTestCase { func testView() { - XCTAssertViewIntrospection(of: PlatformViewController.self) { spies in + XCTAssertViewIntrospection(of: PlatformView.self) { spies in let spy0 = spies[0] let spy1 = spies[1] + let spy2 = spies[2] - VStack { - NavigationView { - Text("Item 0") - #if os(iOS) || os(tvOS) - .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) - #endif - } - .navigationViewStyle(.stack) + VStack(spacing: 10) { + Image(systemName: "scribble").resizable().frame(height: 30) + #if os(iOS) || os(tvOS) + .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) + #elseif os(macOS) + .introspect(.view, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) + #endif - NavigationView { - Text("Item 1") - #if os(iOS) || os(tvOS) - .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) - #endif - } - .navigationViewStyle(.stack) + Text("Text").frame(height: 40) + #if os(iOS) || os(tvOS) + .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) + #elseif os(macOS) + .introspect(.view, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) + #endif } + .padding(10) + #if os(iOS) || os(tvOS) + .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) + #elseif os(macOS) + .introspect(.view, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) + #endif } extraAssertions: { - XCTAssert($0[safe: 0] !== $0[safe: 1]) + XCTAssertEqual($0[safe: 0]?.frame.height, 30) + XCTAssertEqual($0[safe: 1]?.frame.height, 40) + XCTAssertEqual($0[safe: 2]?.frame.height, 100) } } } -#endif From f1c20d68310f0bb199fb4310f95778701c9de9d4 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 27 Jun 2023 00:32:55 +0100 Subject: [PATCH 066/116] Add modal introspection (#268) --- CHANGELOG.md | 3 + Examples/Showcase/Showcase/ContentView.swift | 61 +++++++++++- Sources/Introspect.swift | 13 +++ Sources/ViewTypes/FullScreenCover.swift | 81 ++++++++++++++++ Sources/ViewTypes/Popover.swift | 53 +++++++++++ Sources/ViewTypes/Sheet.swift | 95 +++++++++++++++++++ Tests/Tests.xcodeproj/project.pbxproj | 26 +++++ .../ViewTypes/FullScreenCoverTests.swift | 31 ++++++ Tests/Tests/ViewTypes/PopoverTests.swift | 27 ++++++ Tests/Tests/ViewTypes/SheetTests.swift | 63 ++++++++++++ docs/SwiftUIIntrospect.md | 3 + 11 files changed, 454 insertions(+), 2 deletions(-) create mode 100644 Sources/ViewTypes/FullScreenCover.swift create mode 100644 Sources/ViewTypes/Popover.swift create mode 100644 Sources/ViewTypes/Sheet.swift create mode 100644 Tests/Tests/ViewTypes/FullScreenCoverTests.swift create mode 100644 Tests/Tests/ViewTypes/PopoverTests.swift create mode 100644 Tests/Tests/ViewTypes/SheetTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d8ec54ed..b9785c96a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ Changelog ## master +- Added: `.sheet` introspection (#268) +- Added: `.fullScreenCover` introspection (#268) +- Added: `.popover` introspection (#268) - Added: `VideoPlayer` introspection (#264) - Added: `SignInWithAppleButton` introspection (#265) - Added: `View` introspection on macOS (#266) diff --git a/Examples/Showcase/Showcase/ContentView.swift b/Examples/Showcase/Showcase/ContentView.swift index 2451325fa..01b375b54 100644 --- a/Examples/Showcase/Showcase/ContentView.swift +++ b/Examples/Showcase/Showcase/ContentView.swift @@ -16,13 +16,16 @@ struct ContentView: View { NavigationShowcase() .tabItem { Text("Navigation") } .tag(2) + PresentationShowcase() + .tabItem { Text("Presentation") } + .tag(3) #endif GenericViewShowcase() .tabItem { Text("Generic View") } - .tag(3) + .tag(4) SimpleElementsShowcase() .tabItem { Text("Simple elements") } - .tag(4) + .tag(5) } #if os(iOS) || os(tvOS) .introspect(.tabView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { tabBarController in @@ -196,6 +199,60 @@ struct NavigationShowcase: View { } } +#if !os(macOS) +struct PresentationShowcase: View { + @State var isSheetPresented = false + @State var isFullScreenPresented = false + @State var isPopoverPresented = false + + var body: some View { + VStack(spacing: 20) { + Button("Sheet", action: { isSheetPresented = true }) + .sheet(isPresented: $isSheetPresented) { + Button("Dismiss", action: { isSheetPresented = false }) + #if os(iOS) || os(tvOS) + .introspect( + .sheet, + on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17) + ) { presentationController in + presentationController.containerView?.backgroundColor = .red.withAlphaComponent(0.75) + } + #endif + } + + if #available(iOS 14, tvOS 14, *) { + Button("Full Screen Cover", action: { isFullScreenPresented = true }) + .fullScreenCover(isPresented: $isFullScreenPresented) { + Button("Dismiss", action: { isFullScreenPresented = false }) + #if os(iOS) || os(tvOS) + .introspect( + .fullScreenCover, + on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17) + ) { presentationController in + presentationController.containerView?.backgroundColor = .red.withAlphaComponent(0.75) + } + #endif + } + } + + #if os(iOS) + Button("Popover", action: { isPopoverPresented = true }) + .popover(isPresented: $isPopoverPresented) { + Button("Dismiss", action: { isPopoverPresented = false }) + .padding() + .introspect( + .popover, + on: .iOS(.v13, .v14, .v15, .v16, .v17) + ) { presentationController in + presentationController.containerView?.backgroundColor = .red.withAlphaComponent(0.75) + } + } + #endif + } + } +} +#endif + struct GenericViewShowcase: View { var body: some View { VStack(spacing: 10) { diff --git a/Sources/Introspect.swift b/Sources/Introspect.swift index cabb10b3a..47f8a7124 100644 --- a/Sources/Introspect.swift +++ b/Sources/Introspect.swift @@ -166,3 +166,16 @@ extension PlatformViewController: PlatformEntity { self.ancestors.contains(other) } } + +#if canImport(UIKit) +extension UIPresentationController: PlatformEntity { + @_spi(Internals) + public var ancestor: UIPresentationController? { nil } + + @_spi(Internals) + public var descendants: [UIPresentationController] { [] } + + @_spi(Internals) + public func isDescendant(of other: UIPresentationController) -> Bool { false } +} +#endif diff --git a/Sources/ViewTypes/FullScreenCover.swift b/Sources/ViewTypes/FullScreenCover.swift new file mode 100644 index 000000000..0243b41c2 --- /dev/null +++ b/Sources/ViewTypes/FullScreenCover.swift @@ -0,0 +1,81 @@ +import SwiftUI + +/// An abstract representation of `.fullScreenCover` in SwiftUI. +/// +/// ### iOS +/// +/// ```swift +/// public struct ContentView: View { +/// @State var isPresented = false +/// +/// public var body: some View { +/// Button("Present", action: { isPresented = true }) +/// .fullScreenCover(isPresented: $isPresented) { +/// Button("Dismiss", action: { isPresented = false }) +/// .introspect(.fullScreenCover, on: .iOS(.v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIPresentationController +/// } +/// } +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// ```swift +/// public struct ContentView: View { +/// @State var isPresented = false +/// +/// public var body: some View { +/// Button("Present", action: { isPresented = true }) +/// .fullScreenCover(isPresented: $isPresented) { +/// Button("Dismiss", action: { isPresented = false }) +/// .introspect(.fullScreenCover, on: .tvOS(.v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIPresentationController +/// } +/// } +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// Not available. +/// +public struct FullScreenCoverType: IntrospectableViewType { + public var scope: IntrospectionScope { .ancestor } +} + +#if os(iOS) || os(tvOS) +extension IntrospectableViewType where Self == FullScreenCoverType { + public static var fullScreenCover: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + @available(*, unavailable, message: ".fullScreenCover isn't available on iOS 13") + public static let v13 = Self.unavailable() + 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 { + .from(UIViewController.self, selector: \.presentationController) + } +} + +extension tvOSViewVersion { + @available(*, unavailable, message: ".fullScreenCover isn't available on tvOS 13") + public static let v13 = Self.unavailable() + 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 { + .from(UIViewController.self, selector: \.presentationController) + } +} +#endif +#endif diff --git a/Sources/ViewTypes/Popover.swift b/Sources/ViewTypes/Popover.swift new file mode 100644 index 000000000..cf8e19c23 --- /dev/null +++ b/Sources/ViewTypes/Popover.swift @@ -0,0 +1,53 @@ +import SwiftUI + +/// An abstract representation of `.popover` in SwiftUI. +/// +/// ### iOS +/// +/// ```swift +/// public struct ContentView: View { +/// @State var isPresented = false +/// +/// public var body: some View { +/// Button("Present", action: { isPresented = true }) +/// .popover(isPresented: $isPresented) { +/// Button("Dismiss", action: { isPresented = false }) +/// .introspect(.popover, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIPopoverPresentationController +/// } +/// } +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// Not available. +/// +/// ### macOS +/// +/// Not available. +/// +public struct PopoverType: IntrospectableViewType { + public var scope: IntrospectionScope { .ancestor } +} + +#if os(iOS) +extension IntrospectableViewType where Self == PopoverType { + public static var popover: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + 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) + public static let v17 = Self(for: .v17, selector: selector) + + private static var selector: IntrospectionSelector { + .from(UIViewController.self, selector: \.popoverPresentationController) + } +} +#endif +#endif diff --git a/Sources/ViewTypes/Sheet.swift b/Sources/ViewTypes/Sheet.swift new file mode 100644 index 000000000..af769e522 --- /dev/null +++ b/Sources/ViewTypes/Sheet.swift @@ -0,0 +1,95 @@ +import SwiftUI + +/// An abstract representation of `.sheet` in SwiftUI. +/// +/// ### iOS +/// +/// ```swift +/// public struct ContentView: View { +/// @State var isPresented = false +/// +/// public var body: some View { +/// Button("Present", action: { isPresented = true }) +/// .sheet(isPresented: $isPresented) { +/// Button("Dismiss", action: { isPresented = false }) +/// .introspect(.sheet, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIPresentationController +/// } +/// } +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// ```swift +/// public struct ContentView: View { +/// @State var isPresented = false +/// +/// public var body: some View { +/// Button("Present", action: { isPresented = true }) +/// .sheet(isPresented: $isPresented) { +/// Button("Dismiss", action: { isPresented = false }) +/// .introspect(.sheet, on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIPresentationController +/// } +/// } +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// Not available. +/// +public struct SheetType: IntrospectableViewType { + public var scope: IntrospectionScope { .ancestor } +} + +#if os(iOS) || os(tvOS) +extension IntrospectableViewType where Self == SheetType { + public static var sheet: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + 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) + public static let v17 = Self(for: .v17, selector: selector) + + private static var selector: IntrospectionSelector { + .from(UIViewController.self, selector: \.presentationController) + } +} + +#if !os(tvOS) +@available(iOS 15, *) +extension iOSViewVersion { + @_disfavoredOverload + public static let v15 = Self(for: .v15, selector: selector) + @_disfavoredOverload + public static let v16 = Self(for: .v16, selector: selector) + @_disfavoredOverload + public static let v17 = Self(for: .v17, selector: selector) + + private static var selector: IntrospectionSelector { + .from(UIViewController.self, selector: \.sheetPresentationController) + } +} +#endif + +extension tvOSViewVersion { + 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) + public static let v17 = Self(for: .v17, selector: selector) + + private static var selector: IntrospectionSelector { + .from(UIViewController.self, selector: \.presentationController) + } +} +#endif +#endif diff --git a/Tests/Tests.xcodeproj/project.pbxproj b/Tests/Tests.xcodeproj/project.pbxproj index b595b865a..eeb4ce5eb 100644 --- a/Tests/Tests.xcodeproj/project.pbxproj +++ b/Tests/Tests.xcodeproj/project.pbxproj @@ -96,6 +96,15 @@ 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 */; }; + D5ADFACC2A4A22AE009494FD /* SheetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ADFACB2A4A22AE009494FD /* SheetTests.swift */; }; + D5ADFACE2A4A3482009494FD /* FullScreenCoverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ADFACD2A4A3482009494FD /* FullScreenCoverTests.swift */; }; + D5ADFAD02A4A3E54009494FD /* PopoverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ADFACF2A4A3E54009494FD /* PopoverTests.swift */; }; + D5ADFAD22A4A41CB009494FD /* SignInWithAppleButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ADFAD12A4A41CB009494FD /* SignInWithAppleButtonTests.swift */; }; + D5ADFAD32A4A4649009494FD /* SignInWithAppleButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ADFAD12A4A41CB009494FD /* SignInWithAppleButtonTests.swift */; }; + D5ADFAD42A4A4653009494FD /* FullScreenCoverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ADFACD2A4A3482009494FD /* FullScreenCoverTests.swift */; }; + D5ADFAD52A4A4653009494FD /* SheetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ADFACB2A4A22AE009494FD /* SheetTests.swift */; }; + D5ADFAD62A4A4653009494FD /* VideoPlayerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D503B2AB2A49BFE300027F5F /* VideoPlayerTests.swift */; }; + D5ADFAD72A4A4653009494FD /* PopoverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ADFACF2A4A3E54009494FD /* PopoverTests.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 */; }; @@ -173,6 +182,10 @@ 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 = ""; }; + D5ADFACB2A4A22AE009494FD /* SheetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SheetTests.swift; sourceTree = ""; }; + D5ADFACD2A4A3482009494FD /* FullScreenCoverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenCoverTests.swift; sourceTree = ""; }; + D5ADFACF2A4A3E54009494FD /* PopoverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoverTests.swift; sourceTree = ""; }; + D5ADFAD12A4A41CB009494FD /* SignInWithAppleButtonTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignInWithAppleButtonTests.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 = ""; }; @@ -237,6 +250,7 @@ D57506912A27EE4700A628E4 /* DatePickerWithWheelStyleTests.swift */, D57506872A27CB9800A628E4 /* FormTests.swift */, D57506892A27CE7900A628E4 /* FormWithGroupedStyleTests.swift */, + D5ADFACD2A4A3482009494FD /* FullScreenCoverTests.swift */, D58119C32A211B8A0081F853 /* ListCellTests.swift */, D55F448C2A1FF209003381E4 /* ListTests.swift */, D57506852A27CA4100A628E4 /* ListWithBorderedStyleTests.swift */, @@ -252,10 +266,13 @@ D57506792A27BF6C00A628E4 /* PickerWithMenuStyleTests.swift */, D57506772A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift */, D58119D52A23AED70081F853 /* PickerWithWheelStyleTests.swift */, + D5ADFACF2A4A3E54009494FD /* PopoverTests.swift */, D575069B2A27F68700A628E4 /* ProgressViewWithCircularStyleTests.swift */, D575069D2A27F80E00A628E4 /* ProgressViewWithLinearStyleTests.swift */, D50FFE8D2A17E2A400C32641 /* ScrollViewTests.swift */, D57506A12A281B9C00A628E4 /* SearchFieldTests.swift */, + D5ADFACB2A4A22AE009494FD /* SheetTests.swift */, + D5ADFAD12A4A41CB009494FD /* SignInWithAppleButtonTests.swift */, D58119CF2A23A62C0081F853 /* SliderTests.swift */, D58119D12A23A77C0081F853 /* StepperTests.swift */, D575069F2A27FC0400A628E4 /* TableTests.swift */, @@ -490,6 +507,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D5ADFAD42A4A4653009494FD /* FullScreenCoverTests.swift in Sources */, D50E2F5E2A2B9F6600BAFB03 /* ScrollViewTests.swift in Sources */, D50E2F5F2A2B9F6600BAFB03 /* NavigationStackTests.swift in Sources */, D50E2F602A2B9F6600BAFB03 /* DatePickerWithGraphicalStyleTests.swift in Sources */, @@ -500,7 +518,9 @@ D50E2F652A2B9F6600BAFB03 /* PickerWithMenuStyleTests.swift in Sources */, D50E2F662A2B9F6600BAFB03 /* DatePickerWithWheelStyleTests.swift in Sources */, D50E2F672A2B9F6600BAFB03 /* ListWithInsetGroupedStyleTests.swift in Sources */, + D5ADFAD32A4A4649009494FD /* SignInWithAppleButtonTests.swift in Sources */, D50E2F682A2B9F6600BAFB03 /* FormWithGroupedStyleTests.swift in Sources */, + D5ADFAD52A4A4653009494FD /* SheetTests.swift in Sources */, D50E2F692A2B9F6600BAFB03 /* ListWithPlainStyleTests.swift in Sources */, D50E2F6A2A2B9F6600BAFB03 /* TextEditorTests.swift in Sources */, D50E2F6B2A2B9F6600BAFB03 /* ListWithSidebarStyleTests.swift in Sources */, @@ -509,6 +529,7 @@ D50E2F6E2A2B9F6600BAFB03 /* NavigationViewWithStackStyleTests.swift in Sources */, D50E2F6F2A2B9F6600BAFB03 /* DatePickerWithStepperFieldStyleTests.swift in Sources */, D50E2F702A2B9F6600BAFB03 /* TextFieldWithVerticalAxisTests.swift in Sources */, + D5ADFAD72A4A4653009494FD /* PopoverTests.swift in Sources */, D50E2F712A2B9F6600BAFB03 /* SliderTests.swift in Sources */, D50E2F722A2B9F6600BAFB03 /* ButtonTests.swift in Sources */, D50E2F732A2B9F6600BAFB03 /* ListTests.swift in Sources */, @@ -527,6 +548,7 @@ D50E2F802A2B9F6600BAFB03 /* TableTests.swift in Sources */, D50E2F812A2B9F6600BAFB03 /* DatePickerTests.swift in Sources */, D50E2F822A2B9F6600BAFB03 /* ToggleWithSwitchStyleTests.swift in Sources */, + D5ADFAD62A4A4653009494FD /* VideoPlayerTests.swift in Sources */, D50E2F832A2B9F6600BAFB03 /* ListCellTests.swift in Sources */, D50E2F842A2B9F6600BAFB03 /* SearchFieldTests.swift in Sources */, D50E2F852A2B9F6600BAFB03 /* ViewTests.swift in Sources */, @@ -559,6 +581,7 @@ D575067A2A27BF6C00A628E4 /* PickerWithMenuStyleTests.swift in Sources */, D57506922A27EE4700A628E4 /* DatePickerWithWheelStyleTests.swift in Sources */, D57506822A27C74600A628E4 /* ListWithInsetGroupedStyleTests.swift in Sources */, + D5ADFACE2A4A3482009494FD /* FullScreenCoverTests.swift in Sources */, D568532C2A49DBB10039A99F /* SignInWithAppleButtonTests.swift in Sources */, D575068A2A27CE7900A628E4 /* FormWithGroupedStyleTests.swift in Sources */, D575067C2A27C24600A628E4 /* ListWithPlainStyleTests.swift in Sources */, @@ -570,6 +593,7 @@ D57506942A27EED200A628E4 /* DatePickerWithStepperFieldStyleTests.swift in Sources */, D5AD0D912A114B98003D8DEC /* TextFieldWithVerticalAxisTests.swift in Sources */, D58119D02A23A62C0081F853 /* SliderTests.swift in Sources */, + D5ADFAD02A4A3E54009494FD /* PopoverTests.swift in Sources */, D58119D82A23B3B00081F853 /* ButtonTests.swift in Sources */, D55F448D2A1FF209003381E4 /* ListTests.swift in Sources */, D58547FA2A1D12270068ADF4 /* NavigationSplitViewTests.swift in Sources */, @@ -577,12 +601,14 @@ D57506882A27CB9800A628E4 /* FormTests.swift in Sources */, D58119C82A22AC130081F853 /* ToggleTests.swift in Sources */, D58119D22A23A77C0081F853 /* StepperTests.swift in Sources */, + D5ADFAD22A4A41CB009494FD /* SignInWithAppleButtonTests.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 */, + D5ADFACC2A4A22AE009494FD /* SheetTests.swift in Sources */, D575069A2A27F48D00A628E4 /* DatePickerWithFieldStyleTests.swift in Sources */, D503B2AC2A49BFE300027F5F /* VideoPlayerTests.swift in Sources */, D57506A02A27FC0400A628E4 /* TableTests.swift in Sources */, diff --git a/Tests/Tests/ViewTypes/FullScreenCoverTests.swift b/Tests/Tests/ViewTypes/FullScreenCoverTests.swift new file mode 100644 index 000000000..3528ad5e5 --- /dev/null +++ b/Tests/Tests/ViewTypes/FullScreenCoverTests.swift @@ -0,0 +1,31 @@ +#if os(iOS) || os(tvOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class FullScreenCoverTests: XCTestCase { + func testPresentationAsFullScreenCover() throws { + throw XCTSkip("FIXME: this doesn't pass, even though introspection works in the Showcase app") + + guard #available(iOS 14, tvOS 14, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: UIPresentationController.self) { spies in + let spy0 = spies[0] + + Text("Root") + .fullScreenCover(isPresented: .constant(true)) { + Text("Content") + #if os(iOS) || os(tvOS) + .introspect( + .fullScreenCover, + on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), + customize: spy0 + ) + #endif + } + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/PopoverTests.swift b/Tests/Tests/ViewTypes/PopoverTests.swift new file mode 100644 index 000000000..71f667abe --- /dev/null +++ b/Tests/Tests/ViewTypes/PopoverTests.swift @@ -0,0 +1,27 @@ +#if os(iOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class PopoverTests: XCTestCase { + func testPopover() throws { + if (UIDevice.current.userInterfaceIdiom == .pad) { + throw XCTSkip("FIXME: does not pass on iPad, even though it works in Showcase app") + } + + XCTAssertViewIntrospection(of: UIPopoverPresentationController.self) { spies in + let spy0 = spies[0] + + Text("Root") + .popover(isPresented: .constant(true)) { + Text("Popover") + .introspect( + .popover, + on: .iOS(.v13, .v14, .v15, .v16, .v17), + customize: spy0 + ) + } + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/SheetTests.swift b/Tests/Tests/ViewTypes/SheetTests.swift new file mode 100644 index 000000000..857a8f751 --- /dev/null +++ b/Tests/Tests/ViewTypes/SheetTests.swift @@ -0,0 +1,63 @@ +#if os(iOS) || os(tvOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class SheetTests: XCTestCase { + #if os(iOS) + func testSheet() throws { + XCTAssertViewIntrospection(of: UIPresentationController.self) { spies in + let spy0 = spies[0] + + Text("Root") + .sheet(isPresented: .constant(true)) { + Text("Sheet") + .introspect( + .sheet, + on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), + customize: spy0 + ) + } + } + } + + func testSheetAsSheetPresentationController() throws { + guard #available(iOS 15, tvOS 15, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: UISheetPresentationController.self) { spies in + let spy0 = spies[0] + + Text("Root") + .sheet(isPresented: .constant(true)) { + Text("Sheet") + .introspect( + .sheet, + on: .iOS(.v15, .v16, .v17), + customize: spy0 + ) + } + } + } + #elseif os(tvOS) + func testSheet() throws { + throw XCTSkip("FIXME: this test doesn't pass for some reason, even though introspection works in the Showcase app") + + XCTAssertViewIntrospection(of: UIPresentationController.self) { spies in + let spy0 = spies[0] + + Text("Root") + .sheet(isPresented: .constant(true)) { + Text("Content") + .introspect( + .sheet, + on: .tvOS(.v13, .v14, .v15, .v16, .v17), + customize: spy0 + ) + } + } + } + #endif +} +#endif diff --git a/docs/SwiftUIIntrospect.md b/docs/SwiftUIIntrospect.md index d031f67c0..ddd2b7421 100644 --- a/docs/SwiftUIIntrospect.md +++ b/docs/SwiftUIIntrospect.md @@ -95,6 +95,7 @@ Introspection - [`DatePicker` with `.wheel` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithwheelstyletype) - [`Form`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/formtype) - [`Form` with `.grouped` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/formwithgroupedstyletype) +- [`.fullScreenCover`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/fullScreenCovertype) - [`List`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listtype) - [`List` with `.bordered` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithborderedstyletype) - [`List` with `.grouped` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithgroupedstyletype) @@ -109,10 +110,12 @@ Introspection - [`Picker` with `.menu` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithmenustyletype) - [`Picker` with `.segmented` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithsegmentedstyletype) - [`Picker` with `.wheel` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithwheelstyletype) +- [`.popover`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/popovertype) - [`ProgressView` with `.circular` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/progressviewwithcircularstyletype) - [`ProgressView` with `.linear` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/progressviewwithlinearstyletype) - [`ScrollView`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/scrollviewtype) - [`.searchable`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/searchfieldtype) +- [`.sheet`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/sheettype) - [`SignInWithAppleButton`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/SignInWithAppleButtonType) - [`Slider`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/slidertype) - [`Stepper`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/steppertype) From 99f747fd4602fcbb27937f594e09342b13b7a540 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 27 Jun 2023 09:15:33 +0100 Subject: [PATCH 067/116] Add window introspection (#269) --- CHANGELOG.md | 1 + .../Showcase.xcodeproj/project.pbxproj | 8 +- Examples/Showcase/Showcase/App.swift | 4 +- .../{ContentView.swift => AppView.swift} | 15 ++++ Sources/Introspect.swift | 11 +++ Sources/ViewTypes/Window.swift | 86 +++++++++++++++++++ Tests/Tests.xcodeproj/project.pbxproj | 6 ++ Tests/Tests/ViewTypes/WindowTests.swift | 44 ++++++++++ docs/SwiftUIIntrospect.md | 1 + 9 files changed, 170 insertions(+), 6 deletions(-) rename Examples/Showcase/Showcase/{ContentView.swift => AppView.swift} (97%) create mode 100644 Sources/ViewTypes/Window.swift create mode 100644 Tests/Tests/ViewTypes/WindowTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index b9785c96a..37b533af5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Changelog ## master +- Added: window introspection (#269) - Added: `.sheet` introspection (#268) - Added: `.fullScreenCover` introspection (#268) - Added: `.popover` introspection (#268) diff --git a/Examples/Showcase/Showcase.xcodeproj/project.pbxproj b/Examples/Showcase/Showcase.xcodeproj/project.pbxproj index 08f01b9d5..171bccb93 100644 --- a/Examples/Showcase/Showcase.xcodeproj/project.pbxproj +++ b/Examples/Showcase/Showcase.xcodeproj/project.pbxproj @@ -8,7 +8,7 @@ /* 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 */; }; + D53071F929983CEF00F1936C /* AppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53071F829983CEF00F1936C /* AppView.swift */; }; D5B829752999738200920EBD /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B829742999738200920EBD /* Helpers.swift */; }; D5E3180329C132B6005847DC /* SwiftUIIntrospect in Frameworks */ = {isa = PBXBuildFile; productRef = D5E3180229C132B6005847DC /* SwiftUIIntrospect */; }; /* End PBXBuildFile section */ @@ -16,7 +16,7 @@ /* 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 = ""; }; + D53071F829983CEF00F1936C /* AppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppView.swift; 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 */ @@ -55,7 +55,7 @@ children = ( D530720429983D9300F1936C /* Showcase.entitlements */, D53071F629983CEF00F1936C /* App.swift */, - D53071F829983CEF00F1936C /* ContentView.swift */, + D53071F829983CEF00F1936C /* AppView.swift */, D5B829742999738200920EBD /* Helpers.swift */, ); path = Showcase; @@ -139,7 +139,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D53071F929983CEF00F1936C /* ContentView.swift in Sources */, + D53071F929983CEF00F1936C /* AppView.swift in Sources */, D5B829752999738200920EBD /* Helpers.swift in Sources */, D53071F729983CEF00F1936C /* App.swift in Sources */, ); diff --git a/Examples/Showcase/Showcase/App.swift b/Examples/Showcase/Showcase/App.swift index f9cecf9f7..803adc616 100644 --- a/Examples/Showcase/Showcase/App.swift +++ b/Examples/Showcase/Showcase/App.swift @@ -8,7 +8,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { window = UIWindow(frame: UIScreen.main.bounds) - window?.rootViewController = UIHostingController(rootView: ContentView()) + window?.rootViewController = UIHostingController(rootView: AppView()) window?.makeKeyAndVisible() return true } @@ -18,7 +18,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { struct App: SwiftUI.App { var body: some Scene { WindowGroup { - ContentView() + AppView() } } } diff --git a/Examples/Showcase/Showcase/ContentView.swift b/Examples/Showcase/Showcase/AppView.swift similarity index 97% rename from Examples/Showcase/Showcase/ContentView.swift rename to Examples/Showcase/Showcase/AppView.swift index 01b375b54..75d370336 100644 --- a/Examples/Showcase/Showcase/ContentView.swift +++ b/Examples/Showcase/Showcase/AppView.swift @@ -1,6 +1,21 @@ import SwiftUI import SwiftUIIntrospect +struct AppView: View { + var body: some View { + ContentView() + #if os(iOS) || os(tvOS) + .introspect(.window, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { window in + window.backgroundColor = .brown + } + #elseif os(macOS) + .introspect(.window, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { window in + window.backgroundColor = .lightGray + } + #endif + } +} + struct ContentView: View { @State var selection = 0 diff --git a/Sources/Introspect.swift b/Sources/Introspect.swift index 47f8a7124..e928c643c 100644 --- a/Sources/Introspect.swift +++ b/Sources/Introspect.swift @@ -178,4 +178,15 @@ extension UIPresentationController: PlatformEntity { @_spi(Internals) public func isDescendant(of other: UIPresentationController) -> Bool { false } } +#elseif canImport(AppKit) +extension NSWindow: PlatformEntity { + @_spi(Internals) + public var ancestor: NSWindow? { nil } + + @_spi(Internals) + public var descendants: [NSWindow] { [] } + + @_spi(Internals) + public func isDescendant(of other: NSWindow) -> Bool { false } +} #endif diff --git a/Sources/ViewTypes/Window.swift b/Sources/ViewTypes/Window.swift new file mode 100644 index 000000000..bff2767c5 --- /dev/null +++ b/Sources/ViewTypes/Window.swift @@ -0,0 +1,86 @@ +import SwiftUI + +/// An abstract representation of a view's window in SwiftUI. +/// +/// ### iOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// Text("Content") +/// .introspect(.window, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIWindow +/// } +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// Text("Content") +/// .introspect(.window, on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIWindow +/// } +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// Text("Content") +/// .introspect(.window, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSWindow +/// } +/// } +/// } +/// ``` +/// +public struct WindowType: IntrospectableViewType {} + +extension IntrospectableViewType where Self == WindowType { + public static var window: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + 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) + public static let v17 = Self(for: .v17, selector: selector) + + private static var selector: IntrospectionSelector { + .from(UIView.self, selector: \.window) + } +} + +extension tvOSViewVersion { + 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) + public static let v17 = Self(for: .v17, selector: selector) + + private static var selector: IntrospectionSelector { + .from(UIView.self, selector: \.window) + } +} +#elseif canImport(AppKit) +extension macOSViewVersion { + public static let v10_15 = Self(for: .v10_15, selector: selector) + public static let v11 = Self(for: .v11, selector: selector) + public static let v12 = Self(for: .v12, selector: selector) + public static let v13 = Self(for: .v13, selector: selector) + public static let v14 = Self(for: .v14, selector: selector) + + private static var selector: IntrospectionSelector { + .from(NSView.self, selector: \.window) + } +} +#endif diff --git a/Tests/Tests.xcodeproj/project.pbxproj b/Tests/Tests.xcodeproj/project.pbxproj index eeb4ce5eb..0c191f36c 100644 --- a/Tests/Tests.xcodeproj/project.pbxproj +++ b/Tests/Tests.xcodeproj/project.pbxproj @@ -55,6 +55,8 @@ 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 */; }; + D534D4DC2A4A596200218BFB /* WindowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D534D4DB2A4A596200218BFB /* WindowTests.swift */; }; + D534D4DD2A4A596200218BFB /* WindowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D534D4DB2A4A596200218BFB /* WindowTests.swift */; }; D55F448D2A1FF209003381E4 /* ListTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55F448C2A1FF209003381E4 /* ListTests.swift */; }; D568532C2A49DBB10039A99F /* SignInWithAppleButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D568532B2A49DBB10039A99F /* SignInWithAppleButtonTests.swift */; }; D57506782A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506772A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift */; }; @@ -142,6 +144,7 @@ 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 = ""; }; + D534D4DB2A4A596200218BFB /* WindowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowTests.swift; sourceTree = ""; }; D55F448C2A1FF209003381E4 /* ListTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTests.swift; sourceTree = ""; }; D568532B2A49DBB10039A99F /* SignInWithAppleButtonTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInWithAppleButtonTests.swift; sourceTree = ""; }; D57506772A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickerWithSegmentedStyleTests.swift; sourceTree = ""; }; @@ -288,6 +291,7 @@ D575068B2A27D40500A628E4 /* ToggleWithSwitchStyleTests.swift */, D503B2AB2A49BFE300027F5F /* VideoPlayerTests.swift */, D58119C52A227E930081F853 /* ViewTests.swift */, + D534D4DB2A4A596200218BFB /* WindowTests.swift */, ); path = ViewTypes; sourceTree = ""; @@ -516,6 +520,7 @@ D50E2F632A2B9F6600BAFB03 /* TabViewTests.swift in Sources */, D50E2F642A2B9F6600BAFB03 /* ListWithInsetStyleTests.swift in Sources */, D50E2F652A2B9F6600BAFB03 /* PickerWithMenuStyleTests.swift in Sources */, + D534D4DD2A4A596200218BFB /* WindowTests.swift in Sources */, D50E2F662A2B9F6600BAFB03 /* DatePickerWithWheelStyleTests.swift in Sources */, D50E2F672A2B9F6600BAFB03 /* ListWithInsetGroupedStyleTests.swift in Sources */, D5ADFAD32A4A4649009494FD /* SignInWithAppleButtonTests.swift in Sources */, @@ -585,6 +590,7 @@ D568532C2A49DBB10039A99F /* SignInWithAppleButtonTests.swift in Sources */, D575068A2A27CE7900A628E4 /* FormWithGroupedStyleTests.swift in Sources */, D575067C2A27C24600A628E4 /* ListWithPlainStyleTests.swift in Sources */, + D534D4DC2A4A596200218BFB /* WindowTests.swift in Sources */, D58119CA2A239BAC0081F853 /* TextEditorTests.swift in Sources */, D57506842A27C8D400A628E4 /* ListWithSidebarStyleTests.swift in Sources */, D575069E2A27F80E00A628E4 /* ProgressViewWithLinearStyleTests.swift in Sources */, diff --git a/Tests/Tests/ViewTypes/WindowTests.swift b/Tests/Tests/ViewTypes/WindowTests.swift new file mode 100644 index 000000000..63cc32438 --- /dev/null +++ b/Tests/Tests/ViewTypes/WindowTests.swift @@ -0,0 +1,44 @@ +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class WindowTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformWindow = UIWindow + #elseif canImport(AppKit) + typealias PlatformWindow = NSWindow + #endif + + func testWindow() { + XCTAssertViewIntrospection(of: PlatformWindow.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + Image(systemName: "scribble") + #if os(iOS) || os(tvOS) + .introspect(.window, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) + #elseif os(macOS) + .introspect(.window, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) + #endif + + Text("Text") + #if os(iOS) || os(tvOS) + .introspect(.window, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) + #elseif os(macOS) + .introspect(.window, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) + #endif + } + #if os(iOS) || os(tvOS) + .introspect(.window, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) + #elseif os(macOS) + .introspect(.window, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) + #endif + } extraAssertions: { + XCTAssertIdentical($0[safe: 0], $0[safe: 1]) + XCTAssertIdentical($0[safe: 0], $0[safe: 2]) + XCTAssertIdentical($0[safe: 1], $0[safe: 2]) + } + } +} diff --git a/docs/SwiftUIIntrospect.md b/docs/SwiftUIIntrospect.md index ddd2b7421..5773dde96 100644 --- a/docs/SwiftUIIntrospect.md +++ b/docs/SwiftUIIntrospect.md @@ -131,6 +131,7 @@ Introspection - [`Toggle` with `switch` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/togglewithswitchstyletype) - [`VideoPlayer`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/videoplayertype) - [`View`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/viewtype) +- [`Window`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/windowtype) **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). From d8c1e5343d874c0cd084ba949129f427a2fac9f9 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 27 Jun 2023 09:56:25 +0100 Subject: [PATCH 068/116] Swap READMEs (#270) --- README.md | 253 +++++++++++++++++++++++--------------- docs/LEGACY.md | 193 +++++++++++++++++++++++++++++ docs/SwiftUIIntrospect.md | 247 ------------------------------------- 3 files changed, 346 insertions(+), 347 deletions(-) create mode 100644 docs/LEGACY.md delete mode 100644 docs/SwiftUIIntrospect.md diff --git a/README.md b/README.md index 93d804f07..a46f6ea10 100644 --- a/README.md +++ b/README.md @@ -1,53 +1,84 @@ -> **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. +SwiftUI Introspect +================= -Introspect for SwiftUI -====================== +[![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) -[![GithubCI_Status]][GithubCI_URL] [![Siteline_Badge]](https://siteline.com) [![Quintschaf_Badge]](https://quintschaf.com) +> **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. +> +> 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. -> Introspect allows you to get the underlying UIKit or AppKit element of a SwiftUI view. +SwiftUIIntrospect allows you to get the underlying UIKit or AppKit element of a SwiftUI view. -For instance, with Introspect you can access `UITableView` to modify separators, or `UINavigationController` to customize the tab bar. +For instance, with SwiftUIIntrospect you can access `UITableView` to modify separators, or `UINavigationController` to customize the tab bar. How it works ------------ -Introspect works by adding a custom `IntrospectionView` to the view hierarchy, then looking into the UIKit hierarchy to find the relevant view. +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, .v17)) { scrollView in + // do something with UIScrollView +} +``` + +... it will: -![](./docs/diagram.png) +- 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. -For instance, when introspecting a `TextField`, it will: +> **Warning** +> +> Although this introspection method itself is very solid and unlikely to break in itself, 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. - - Add `IntrospectionView` as an overlay of `TextField` - - Get the view host of the introspection view (which is alongside the view host of the `UITextField`) - - Get the previous sibling containing `UITextField` +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`: -**Please note that this introspection method might break in future SwiftUI releases.** Future implementations might not use the same hierarchy, or might not use UIKit elements that are being looked for. Though the library is unlikely to crash, the `.introspect()` method will not be called in those cases. +```swift +ScrollView { + Text("Item 1") + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { scrollView in + // do something with UIScrollView + } +} +``` ### Usage in production -`Introspect` 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 classes, and the `introspect()` methods are simply ignored if UIKit views cannot be found. - +`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 ------- -### SwiftPM +### Swift Package Manager -``` -https://github.com/siteline/SwiftUI-Introspect.git +```swift +let package = Package( + dependencies: [ + .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.6.1"), + ], + targets: [ + .target(name: <#Target Name#>, dependencies: [ + .product(name: "SwiftUIIntrospect", package: "swiftui-introspect"), + ]), + ] +) ``` -### Cocoapods +### CocoaPods -``` -pod 'Introspect' +```ruby +pod 'SwiftUIIntrospect' ``` Introspection @@ -55,30 +86,55 @@ Introspection ### Implemented -SwiftUI | UIKit | AppKit | Introspect ---- | --- | --- | --- -NavigationView (StackNavigationViewStyle) | UINavigationController | _N/A_ | `.introspectNavigationController()` -NavigationView (DoubleColumnNavigationViewStyle) | UISplitViewController | _N/A_ | `.introspectSplitViewController()` -NavigationView (DoubleColumnNavigationViewStyle) | _N/A_ | NSSplitView | `.introspectSplitView()` -_Any embedded view_ | UIViewController | _N/A_ | `.introspectViewController()` -ScrollView | UIScrollView | NSScrollView | `.introspectScrollView()` -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) -Slider | UISlider | NSSlider | `.introspectSlider()` -Stepper | UIStepper | NSStepper | `.introspectStepper()` -DatePicker | UIDatePicker | NSDatePicker | `.introspectDatePicker()` -Picker (SegmentedPickerStyle) | UISegmentedControl | NSSegmentedControl | `.introspectSegmentedControl()` -Button | _N/A_ | NSButton | `.introspectButton()` -ColorPicker | UIColorWell | NSColorWell | `.introspectColorWell()` -TextEditor | UITextView | NSTextView | `.introspectTextView()` - - -**Missing an element?** Please [create an issue](https://github.com/timbersoftware/SwiftUI-Introspect/issues). As a temporary solution, you can [implement your own selector](#implement-your-own-selector). +- [`Button`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/buttontype) +- [`ColorPicker`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/colorpickertype) +- [`DatePicker`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickertype) +- [`DatePicker` with `.compact` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithcompactstyletype) +- [`DatePicker` with `.field` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithfieldstyletype) +- [`DatePicker` with `.graphical` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithgraphicalstyletype) +- [`DatePicker` with `.stepperField` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithstepperfieldstyletype) +- [`DatePicker` with `.wheel` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithwheelstyletype) +- [`Form`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/formtype) +- [`Form` with `.grouped` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/formwithgroupedstyletype) +- [`.fullScreenCover`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/fullScreenCovertype) +- [`List`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listtype) +- [`List` with `.bordered` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithborderedstyletype) +- [`List` with `.grouped` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithgroupedstyletype) +- [`List` with `.insetGrouped` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithinsetgroupedstyletype) +- [`List` with `.inset` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithinsetstyletype) +- [`List` with `.sidebar` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithsidebarstyletype) +- [`ListCell`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listcelltype) +- [`NavigationSplitView`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/navigationsplitviewtype) +- [`NavigationStack`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/navigationstacktype) +- [`NavigationView` with `.columns` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/NavigationViewWithColumnsStyleType) +- [`NavigationView` with `.stack` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/NavigationViewWithStackStyleType) +- [`Picker` with `.menu` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithmenustyletype) +- [`Picker` with `.segmented` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithsegmentedstyletype) +- [`Picker` with `.wheel` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithwheelstyletype) +- [`.popover`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/popovertype) +- [`ProgressView` with `.circular` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/progressviewwithcircularstyletype) +- [`ProgressView` with `.linear` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/progressviewwithlinearstyletype) +- [`ScrollView`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/scrollviewtype) +- [`.searchable`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/searchfieldtype) +- [`.sheet`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/sheettype) +- [`SignInWithAppleButton`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/SignInWithAppleButtonType) +- [`Slider`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/slidertype) +- [`Stepper`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/steppertype) +- [`Table`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/tabletype) +- [`TabView`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/tabviewtype) +- [`TabView` with `.page` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/TabViewWithPageStyleType) +- [`TextEditor`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/texteditortype) +- [`TextField`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/textfieldtype) +- [`TextField` with `.vertical` axis](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/TextFieldWithVerticalAxisType) +- [`Toggle`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/toggletype) +- [`Toggle` with `button` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/togglewithbuttonstyletype) +- [`Toggle` with `checkbox` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/togglewithcheckboxstyletype) +- [`Toggle` with `switch` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/togglewithswitchstyletype) +- [`VideoPlayer`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/videoplayertype) +- [`View`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/viewtype) +- [`Window`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/windowtype) + +**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 @@ -95,16 +151,15 @@ Examples ```swift List { - Text("Item 1") - Text("Item 2") + Text("Item") } -.introspectTableView { tableView in - tableView.separatorStyle = .none +.introspect(.list, on: .iOS(.v13, .v14, .v15)) { tableView in + tableView.backgroundView = UIView() + tableView.backgroundColor = .cyan } -.introspectTableViewCell { cell in - let backgroundView = UIView() - backgroundView.backgroundColor = .clear - cell.selectedBackgroundView = backgroundView +.introspect(.list, on: .iOS(.v16, .v17)) { collectionView in + collectionView.backgroundView = UIView() + collectionView.subviews.dropFirst(1).first?.backgroundColor = .cyan } ``` @@ -112,9 +167,9 @@ List { ```swift ScrollView { - Text("Item 2") + Text("Item") } -.introspectScrollView { scrollView in +.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { scrollView in scrollView.refreshControl = UIRefreshControl() } ``` @@ -123,19 +178,20 @@ ScrollView { ```swift NavigationView { - Text("Item 2") - .introspectNavigationController { navigationController in - navigationController.navigationBar.backgroundColor = .red - } + Text("Item") +} +.navigationViewStyle(.stack) +.introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { navigationController in + navigationController.navigationBar.backgroundColor = .cyan } ``` ### TextField ```swift -TextField("Text Field", text: $textFieldValue) - .introspectTextField { textField in - textField.layer.backgroundColor = UIColor.red.cgColor +TextField("Text Field", text: <#Binding#>) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { textField in + textField.backgroundColor = .red } ``` @@ -144,33 +200,40 @@ Implement your own selector **Missing an element?** Please [create an issue](https://github.com/timbersoftware/SwiftUI-Introspect/issues). -In case Introspect doesn't support the SwiftUI element that you're looking for, you can implement your own selector. For example, to look for a `UITextField`: +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 -extension View { - public func introspectTextField(customize: @escaping (UITextField) -> ()) -> some View { - return inject(UIKitIntrospectionView( - selector: { introspectionView in - guard let viewHost = Introspect.findViewHost(from: introspectionView) else { - return nil - } - return Introspect.previousSibling(containing: UITextField.self, from: viewHost) - }, - customize: customize - )) - } +@_spi(Internals) import SwiftUIIntrospect + +public struct TextFieldType: IntrospectableViewType {} + +extension IntrospectableViewType where Self == TextFieldType { + public static var textField: Self { .init() } } -``` -You can use any of the following [methods](https://github.com/timbersoftware/SwiftUI-Introspect/blob/master/Introspect/Introspect.swift#L3-L71) to inspect the hierarchy: +#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) +} - - `Introspect.findChild(ofType:in:)` - - `Introspect.findChildUsingFrame(ofType:in:from:)` - - `Introspect.previousSibling(containing:from:)` - - `Introspect.nextSibling(containing:from:)` - - `Introspect.findAncestor(ofType:from:)` - - `Introspect.findHostingView(from:)` - - `Introspect.findViewHost(from:)` +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 --------- @@ -183,13 +246,3 @@ Releasing $ git tag X.Y.Z $ git push origin --tags ``` - - - -[GithubCI_Status]: https://github.com/siteline/swiftui-introspect/actions/workflows/ci.yml/badge.svg?branch=master - -[GithubCI_URL]: https://github.com/siteline/SwiftUI-Introspect/actions/workflows/ci.yml - -[Siteline_Badge]: https://badgen.net/badge/Built%20by/Siteline/blue?icon=https://uploads-ssl.webflow.com/5f4513afbbfc64c4777fcccf/5f525b122370d681879e170e_siteline-icon.svg - -[Quintschaf_Badge]: https://badgen.net/badge/Maintained%20by/Quintschaf/cyan?icon=https://quintschaf.com/assets/logo.svg diff --git a/docs/LEGACY.md b/docs/LEGACY.md new file mode 100644 index 000000000..37cf99734 --- /dev/null +++ b/docs/LEGACY.md @@ -0,0 +1,193 @@ +> **Note** +> +> [`Introspect`](../Package.swift#L13) is a legacy module being replaced by [`SwiftUIIntrospect`](../Package@swift-5.7.swift#L19) later this year. +> +> Please refer to this repo's [README](../README.md) to learn more. + +Introspect for SwiftUI +====================== + +[![GithubCI_Status]][GithubCI_URL] [![Siteline_Badge]](https://siteline.com) [![Quintschaf_Badge]](https://quintschaf.com) + +> Introspect allows you to get the underlying UIKit or AppKit element of a SwiftUI view. + +For instance, with Introspect you can access `UITableView` to modify separators, or `UINavigationController` to customize the tab bar. + +How it works +------------ + +Introspect works by adding a custom `IntrospectionView` to the view hierarchy, then looking into the UIKit hierarchy to find the relevant view. + +![](./docs/diagram.png) + +For instance, when introspecting a `TextField`, it will: + + - Add `IntrospectionView` as an overlay of `TextField` + - Get the view host of the introspection view (which is alongside the view host of the `UITextField`) + - Get the previous sibling containing `UITextField` + +**Please note that this introspection method might break in future SwiftUI releases.** Future implementations might not use the same hierarchy, or might not use UIKit elements that are being looked for. Though the library is unlikely to crash, the `.introspect()` method will not be called in those cases. + +### Usage in production + +`Introspect` 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 classes, and the `introspect()` methods are simply ignored if UIKit views cannot be found. + + +Install +------- + +### SwiftPM + +``` +https://github.com/siteline/SwiftUI-Introspect.git +``` + +### Cocoapods + +``` +pod 'Introspect' +``` + +Introspection +------------- + +### Implemented + +SwiftUI | UIKit | AppKit | Introspect +--- | --- | --- | --- +NavigationView (StackNavigationViewStyle) | UINavigationController | _N/A_ | `.introspectNavigationController()` +NavigationView (DoubleColumnNavigationViewStyle) | UISplitViewController | _N/A_ | `.introspectSplitViewController()` +NavigationView (DoubleColumnNavigationViewStyle) | _N/A_ | NSSplitView | `.introspectSplitView()` +_Any embedded view_ | UIViewController | _N/A_ | `.introspectViewController()` +ScrollView | UIScrollView | NSScrollView | `.introspectScrollView()` +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) +Slider | UISlider | NSSlider | `.introspectSlider()` +Stepper | UIStepper | NSStepper | `.introspectStepper()` +DatePicker | UIDatePicker | NSDatePicker | `.introspectDatePicker()` +Picker (SegmentedPickerStyle) | UISegmentedControl | NSSegmentedControl | `.introspectSegmentedControl()` +Button | _N/A_ | NSButton | `.introspectButton()` +ColorPicker | UIColorWell | NSColorWell | `.introspectColorWell()` +TextEditor | UITextView | NSTextView | `.introspectTextView()` + + +**Missing an element?** Please [create an issue](https://github.com/timbersoftware/SwiftUI-Introspect/issues). As a temporary solution, you can [implement your own selector](#implement-your-own-selector). + +### 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 1") + Text("Item 2") +} +.introspectTableView { tableView in + tableView.separatorStyle = .none +} +.introspectTableViewCell { cell in + let backgroundView = UIView() + backgroundView.backgroundColor = .clear + cell.selectedBackgroundView = backgroundView +} +``` + +### ScrollView + +```swift +ScrollView { + Text("Item 2") +} +.introspectScrollView { scrollView in + scrollView.refreshControl = UIRefreshControl() +} +``` + +### NavigationView + +```swift +NavigationView { + Text("Item 2") + .introspectNavigationController { navigationController in + navigationController.navigationBar.backgroundColor = .red + } +} +``` + +### TextField + +```swift +TextField("Text Field", text: $textFieldValue) + .introspectTextField { textField in + textField.layer.backgroundColor = UIColor.red.cgColor + } +``` + +Implement your own selector +--------------------------- + +**Missing an element?** Please [create an issue](https://github.com/timbersoftware/SwiftUI-Introspect/issues). + +In case Introspect doesn't support the SwiftUI element that you're looking for, you can implement your own selector. For example, to look for a `UITextField`: + +```swift +extension View { + public func introspectTextField(customize: @escaping (UITextField) -> ()) -> some View { + return inject(UIKitIntrospectionView( + selector: { introspectionView in + guard let viewHost = Introspect.findViewHost(from: introspectionView) else { + return nil + } + return Introspect.previousSibling(containing: UITextField.self, from: viewHost) + }, + customize: customize + )) + } +} +``` + +You can use any of the following [methods](https://github.com/timbersoftware/SwiftUI-Introspect/blob/master/Introspect/Introspect.swift#L3-L71) to inspect the hierarchy: + + - `Introspect.findChild(ofType:in:)` + - `Introspect.findChildUsingFrame(ofType:in:from:)` + - `Introspect.previousSibling(containing:from:)` + - `Introspect.nextSibling(containing:from:)` + - `Introspect.findAncestor(ofType:from:)` + - `Introspect.findHostingView(from:)` + - `Introspect.findViewHost(from:)` + +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 + ``` + + + +[GithubCI_Status]: https://github.com/siteline/swiftui-introspect/actions/workflows/ci.yml/badge.svg?branch=master + +[GithubCI_URL]: https://github.com/siteline/SwiftUI-Introspect/actions/workflows/ci.yml + +[Siteline_Badge]: https://badgen.net/badge/Built%20by/Siteline/blue?icon=https://uploads-ssl.webflow.com/5f4513afbbfc64c4777fcccf/5f525b122370d681879e170e_siteline-icon.svg + +[Quintschaf_Badge]: https://badgen.net/badge/Maintained%20by/Quintschaf/cyan?icon=https://quintschaf.com/assets/logo.svg diff --git a/docs/SwiftUIIntrospect.md b/docs/SwiftUIIntrospect.md deleted file mode 100644 index 5773dde96..000000000 --- a/docs/SwiftUIIntrospect.md +++ /dev/null @@ -1,247 +0,0 @@ -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, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { 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 itself, 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, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), 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.6.1"), - ], - targets: [ - .target(name: <#Target Name#>, dependencies: [ - .product(name: "SwiftUIIntrospect", package: "swiftui-introspect"), - ]), - ] -) -``` - -### CocoaPods - -```ruby -pod 'SwiftUIIntrospect' -``` - -Introspection -------------- - -### Implemented - -- [`Button`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/buttontype) -- [`ColorPicker`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/colorpickertype) -- [`DatePicker`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickertype) -- [`DatePicker` with `.compact` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithcompactstyletype) -- [`DatePicker` with `.field` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithfieldstyletype) -- [`DatePicker` with `.graphical` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithgraphicalstyletype) -- [`DatePicker` with `.stepperField` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithstepperfieldstyletype) -- [`DatePicker` with `.wheel` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/datepickerwithwheelstyletype) -- [`Form`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/formtype) -- [`Form` with `.grouped` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/formwithgroupedstyletype) -- [`.fullScreenCover`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/fullScreenCovertype) -- [`List`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listtype) -- [`List` with `.bordered` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithborderedstyletype) -- [`List` with `.grouped` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithgroupedstyletype) -- [`List` with `.insetGrouped` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithinsetgroupedstyletype) -- [`List` with `.inset` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithinsetstyletype) -- [`List` with `.sidebar` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithsidebarstyletype) -- [`ListCell`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listcelltype) -- [`NavigationSplitView`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/navigationsplitviewtype) -- [`NavigationStack`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/navigationstacktype) -- [`NavigationView` with `.columns` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/NavigationViewWithColumnsStyleType) -- [`NavigationView` with `.stack` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/NavigationViewWithStackStyleType) -- [`Picker` with `.menu` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithmenustyletype) -- [`Picker` with `.segmented` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithsegmentedstyletype) -- [`Picker` with `.wheel` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithwheelstyletype) -- [`.popover`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/popovertype) -- [`ProgressView` with `.circular` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/progressviewwithcircularstyletype) -- [`ProgressView` with `.linear` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/progressviewwithlinearstyletype) -- [`ScrollView`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/scrollviewtype) -- [`.searchable`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/searchfieldtype) -- [`.sheet`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/sheettype) -- [`SignInWithAppleButton`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/SignInWithAppleButtonType) -- [`Slider`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/slidertype) -- [`Stepper`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/steppertype) -- [`Table`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/tabletype) -- [`TabView`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/tabviewtype) -- [`TabView` with `.page` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/TabViewWithPageStyleType) -- [`TextEditor`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/texteditortype) -- [`TextField`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/textfieldtype) -- [`TextField` with `.vertical` axis](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/TextFieldWithVerticalAxisType) -- [`Toggle`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/toggletype) -- [`Toggle` with `button` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/togglewithbuttonstyletype) -- [`Toggle` with `checkbox` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/togglewithcheckboxstyletype) -- [`Toggle` with `switch` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/togglewithswitchstyletype) -- [`VideoPlayer`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/videoplayertype) -- [`View`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/viewtype) -- [`Window`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/windowtype) - -**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, .v17)) { tableView in - tableView.backgroundView = UIView() - tableView.backgroundColor = .cyan -} -.introspect(.list, on: .iOS(.v16, .v17)) { 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, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { scrollView in - scrollView.refreshControl = UIRefreshControl() -} -``` - -### NavigationView - -```swift -NavigationView { - Text("Item") -} -.navigationViewStyle(.stack) -.introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { navigationController in - navigationController.navigationBar.backgroundColor = .cyan -} -``` - -### TextField - -```swift -TextField("Text Field", text: <#Binding#>) - .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { 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 -@_spi(Internals) import SwiftUIIntrospect - -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 - ``` From 5da8f123eb1d8299e2e626c9fa7371bf81470dcb Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 27 Jun 2023 11:54:42 +0100 Subject: [PATCH 069/116] Rename xcworkspace (#271) --- .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/xcschemes/Introspect.xcscheme | 0 .../xcshareddata/xcschemes/SwiftUIIntrospect.xcscheme | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename {Introspect.xcworkspace => SwiftUIIntrospect.xcworkspace}/contents.xcworkspacedata (100%) rename {Introspect.xcworkspace => SwiftUIIntrospect.xcworkspace}/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {Introspect.xcworkspace => SwiftUIIntrospect.xcworkspace}/xcshareddata/xcschemes/Introspect.xcscheme (100%) rename {Introspect.xcworkspace => SwiftUIIntrospect.xcworkspace}/xcshareddata/xcschemes/SwiftUIIntrospect.xcscheme (100%) diff --git a/Introspect.xcworkspace/contents.xcworkspacedata b/SwiftUIIntrospect.xcworkspace/contents.xcworkspacedata similarity index 100% rename from Introspect.xcworkspace/contents.xcworkspacedata rename to SwiftUIIntrospect.xcworkspace/contents.xcworkspacedata diff --git a/Introspect.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SwiftUIIntrospect.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from Introspect.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to SwiftUIIntrospect.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/Introspect.xcworkspace/xcshareddata/xcschemes/Introspect.xcscheme b/SwiftUIIntrospect.xcworkspace/xcshareddata/xcschemes/Introspect.xcscheme similarity index 100% rename from Introspect.xcworkspace/xcshareddata/xcschemes/Introspect.xcscheme rename to SwiftUIIntrospect.xcworkspace/xcshareddata/xcschemes/Introspect.xcscheme diff --git a/Introspect.xcworkspace/xcshareddata/xcschemes/SwiftUIIntrospect.xcscheme b/SwiftUIIntrospect.xcworkspace/xcshareddata/xcschemes/SwiftUIIntrospect.xcscheme similarity index 100% rename from Introspect.xcworkspace/xcshareddata/xcschemes/SwiftUIIntrospect.xcscheme rename to SwiftUIIntrospect.xcworkspace/xcshareddata/xcschemes/SwiftUIIntrospect.xcscheme From 67b41fa5bede2a08ed296a3fb2a9f96d165b3435 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 27 Jun 2023 13:18:44 +0100 Subject: [PATCH 070/116] Deprecate Introspect module (#272) --- CHANGELOG.md | 6 +++++ Introspect/ViewExtensions.swift | 39 +++++++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37b533af5..b2415aa3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Changelog ## master +### SwiftUIIntrospect + - Added: window introspection (#269) - Added: `.sheet` introspection (#268) - Added: `.fullScreenCover` introspection (#268) @@ -12,6 +14,10 @@ Changelog - Added: `View` introspection on macOS (#266) - Improved: `View` introspection accuracy (#266) +### Introspect + +This module is now deprecated (#272) and will be removed later this year (whenever iOS/tvOS 17 come out). + ## [0.6.3] ### SwiftUIIntrospect diff --git a/Introspect/ViewExtensions.swift b/Introspect/ViewExtensions.swift index ca8ea4a97..6ff3627a2 100644 --- a/Introspect/ViewExtensions.swift +++ b/Introspect/ViewExtensions.swift @@ -14,8 +14,9 @@ extension View { #if canImport(UIKit) extension View { - + /// Finds a `TargetView` from a `SwiftUI.View` + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspect( selector: @escaping (IntrospectionUIView) -> TargetView?, customize: @escaping (TargetView) -> () @@ -25,8 +26,9 @@ extension View { customize: customize )) } - + /// Finds a `UINavigationController` from any view embedded in a `SwiftUI.NavigationView`. + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectNavigationController(customize: @escaping (UINavigationController) -> ()) -> some View { inject(UIKitIntrospectionViewController( selector: { introspectionViewController in @@ -44,6 +46,7 @@ extension View { } /// Finds a `UISplitViewController` from a `SwiftUI.NavigationView` with style `DoubleColumnNavigationViewStyle`. + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectSplitViewController(customize: @escaping (UISplitViewController) -> ()) -> some View { inject(UIKitIntrospectionViewController( selector: { introspectionViewController in @@ -61,6 +64,7 @@ extension View { } /// Finds the containing `UIViewController` of a SwiftUI view. + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectViewController(customize: @escaping (UIViewController) -> ()) -> some View { inject(UIKitIntrospectionViewController( selector: { $0.parent }, @@ -69,6 +73,7 @@ extension View { } /// Finds a `UITabBarController` from any SwiftUI view embedded in a `SwiftUI.TabView` + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectTabBarController(customize: @escaping (UITabBarController) -> ()) -> some View { inject(UIKitIntrospectionViewController( selector: { introspectionViewController in @@ -88,6 +93,7 @@ extension View { /// Finds a `UISearchController` from a `SwiftUI.View` with a `.searchable` modifier @available(iOS 15, *) @available(tvOS, unavailable) + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectSearchController(customize: @escaping (UISearchController) -> ()) -> some View { introspectNavigationController { navigationController in let navigationBar = navigationController.navigationBar @@ -98,26 +104,31 @@ extension View { } /// Finds a `UITableView` from a `SwiftUI.List`, or `SwiftUI.List` child. + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectTableView(customize: @escaping (UITableView) -> ()) -> some View { introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, customize: customize) } /// Finds a `UITableViewCell` from a `SwiftUI.List`, or `SwiftUI.List` child. You can attach this directly to the element inside the list. + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectTableViewCell(customize: @escaping (UITableViewCell) -> ()) -> some View { introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, customize: customize) } /// Finds a `UICollectionView` from a `SwiftUI.List`, or `SwiftUI.List` child. + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectCollectionView(customize: @escaping (UICollectionView) -> ()) -> some View { introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, customize: customize) } /// Finds a `UICollectionView` from a `SwiftUI.List`, or `SwiftUI.List` child. You can attach this directly to the element inside the list. + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectCollectionViewCell(customize: @escaping (UICollectionViewCell) -> ()) -> some View { introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, customize: customize) } /// Finds a `UIScrollView` from a `SwiftUI.ScrollView`, or `SwiftUI.ScrollView` child. + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectScrollView(customize: @escaping (UIScrollView) -> ()) -> some View { if #available(iOS 14, tvOS 14, *) { return introspect(selector: TargetViewSelector.siblingOrAncestorOrSiblingContainingOrAncestorChild, customize: customize) @@ -130,6 +141,7 @@ extension View { /// /// Customize is called with a `UICollectionView` wrapper, and the horizontal `UIScrollView`. @available(iOS 14, tvOS 14, *) + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectPagedTabView(customize: @escaping (UICollectionView, UIScrollView) -> ()) -> some View { if #available(iOS 16, *) { return introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, customize: { (collectionView: UICollectionView) in @@ -148,40 +160,47 @@ extension View { } /// Finds a `UITextField` from a `SwiftUI.TextField` + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectTextField(customize: @escaping (UITextField) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContainingOrAncestorOrAncestorChild, customize: customize) } /// Finds a `UITextView` from a `SwiftUI.TextEditor` + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectTextView(customize: @escaping (UITextView) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) } /// Finds a `UISwitch` from a `SwiftUI.Toggle` @available(tvOS, unavailable) + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectSwitch(customize: @escaping (UISwitch) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) } /// Finds a `UISlider` from a `SwiftUI.Slider` @available(tvOS, unavailable) + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectSlider(customize: @escaping (UISlider) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) } /// Finds a `UIStepper` from a `SwiftUI.Stepper` @available(tvOS, unavailable) + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectStepper(customize: @escaping (UIStepper) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) } /// Finds a `UIDatePicker` from a `SwiftUI.DatePicker` @available(tvOS, unavailable) + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectDatePicker(customize: @escaping (UIDatePicker) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) } /// Finds a `UISegmentedControl` from a `SwiftUI.Picker` with style `SegmentedPickerStyle` + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectSegmentedControl(customize: @escaping (UISegmentedControl) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) } @@ -190,6 +209,7 @@ extension View { #if os(iOS) @available(iOS 14, *) @available(tvOS, unavailable) + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectColorWell(customize: @escaping (UIColorWell) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) } @@ -201,6 +221,7 @@ extension View { extension View { /// Finds a `TargetView` from a `SwiftUI.View` + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspect( selector: @escaping (IntrospectionNSView) -> TargetView?, customize: @escaping (TargetView) -> () @@ -212,21 +233,25 @@ extension View { } /// Finds a `NSSplitViewController` from a `SwiftUI.NavigationView` with style `DoubleColumnNavigationViewStyle`. + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectSplitView(customize: @escaping (NSSplitView) -> ()) -> some View { return introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, customize: customize) } /// Finds a `NSTableView` from a `SwiftUI.List`, or `SwiftUI.List` child. + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectTableView(customize: @escaping (NSTableView) -> ()) -> some View { introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, customize: customize) } /// Finds a `NSTableCellView` from a `SwiftUI.List`, or `SwiftUI.List` child. You can attach this directly to the element inside the list. + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectTableViewCell(customize: @escaping (NSTableCellView) -> ()) -> some View { introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, customize: customize) } /// Finds a `NSScrollView` from a `SwiftUI.ScrollView`, or `SwiftUI.ScrollView` child. + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectScrollView(customize: @escaping (NSScrollView) -> ()) -> some View { if #available(macOS 11, *) { return introspect(selector: TargetViewSelector.siblingOrAncestorOrSiblingContainingOrAncestorChild, customize: customize) @@ -236,47 +261,56 @@ extension View { } /// Finds a `NSTextField` from a `SwiftUI.TextField` + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectTextField(customize: @escaping (NSTextField) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) } /// Finds a `NSTextView` from a `SwiftUI.TextView` + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectTextView(customize: @escaping (NSTextView) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) } /// Finds a `NSSlider` from a `SwiftUI.Slider` + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectSlider(customize: @escaping (NSSlider) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) } /// Finds a `NSStepper` from a `SwiftUI.Stepper` + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectStepper(customize: @escaping (NSStepper) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) } /// Finds a `NSDatePicker` from a `SwiftUI.DatePicker` + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectDatePicker(customize: @escaping (NSDatePicker) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) } /// Finds a `NSSegmentedControl` from a `SwiftUI.Picker` with style `SegmentedPickerStyle` + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectSegmentedControl(customize: @escaping (NSSegmentedControl) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) } /// Finds a `NSTabView` from a `SwiftUI.TabView` + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectTabView(customize: @escaping (NSTabView) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) } /// Finds a `NSButton` from a `SwiftUI.Button` + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectButton(customize: @escaping (NSButton) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) } /// Finds a `NSColorWell` from a `SwiftUI.ColorPicker` @available(macOS 11, *) + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectColorWell(customize: @escaping (NSColorWell) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) } @@ -289,6 +323,7 @@ import MapKit extension View { /// Finds an `MKMapView` from a `SwiftUI.Map` @available(iOS 14, tvOS 14, macOS 11, *) + @available(*, deprecated, message: "The Introspect module is deprecated and will be obsoleted later this year. Please switch over to the new and improved SwiftUIIntrospect module. More info: https://github.com/siteline/swiftui-introspect#readme") public func introspectMapView(customize: @escaping (MKMapView) -> ()) -> some View { introspect(selector: TargetViewSelector.siblingContaining, customize: customize) } From f917d1c5277a16d28f90429921fac601320ddfde Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 27 Jun 2023 14:15:45 +0100 Subject: [PATCH 071/116] Document some more public symbols (#273) --- CHANGELOG.md | 1 + Sources/Introspect.swift | 27 +++++++++++++++++++++++++++ Sources/IntrospectableViewType.swift | 10 ++++++++++ 3 files changed, 38 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2415aa3b..f8413e476 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Changelog - Added: `SignInWithAppleButton` introspection (#265) - Added: `View` introspection on macOS (#266) - Improved: `View` introspection accuracy (#266) +- Documentation: added some more docs for public symbols (#273) ### Introspect diff --git a/Sources/Introspect.swift b/Sources/Introspect.swift index e928c643c..19008a0e1 100644 --- a/Sources/Introspect.swift +++ b/Sources/Introspect.swift @@ -1,7 +1,12 @@ import SwiftUI +/// The scope of introspection i.e. where introspect should look to find +/// the desired target view relative to the applied `.introspect(...)` +/// modifier. public struct IntrospectionScope: OptionSet { + /// Look within the `receiver` of the `.introspect(...)` modifier. public static let receiver = Self(rawValue: 1 << 0) + /// Look for an `ancestor` relative to the `.introspect(...)` modifier. public static let ancestor = Self(rawValue: 1 << 1) @_spi(Private) public let rawValue: UInt @@ -12,6 +17,28 @@ public struct IntrospectionScope: OptionSet { } extension View { + /// Introspects a SwiftUI view to find its underlying UIKit/AppKit instance. + /// + /// - Parameters: + /// - viewType: The type of view to be introspected. + /// - platforms: A list of `PlatformViewVersions` that specify platform-specific entities associated with the view, with one or more corresponding version numbers. + /// - scope: An optional `IntrospectionScope` that specifies the scope of introspection. + /// - customize: A closure that hands over the underlying UIKit/AppKit instance ready for customization. + /// + /// Here's an example usage: + /// + /// ```swift + /// struct ContentView: View { + /// @State var date = Date() + /// + /// var body: some View { + /// DatePicker("Pick a date", selection: $date) + /// .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { + /// print(type(of: $0)) // UIDatePicker + /// } + /// } + /// } + /// ``` public func introspect( _ viewType: SwiftUIViewType, on platforms: (PlatformViewVersions)..., diff --git a/Sources/IntrospectableViewType.swift b/Sources/IntrospectableViewType.swift index 4f4139843..5424c602e 100644 --- a/Sources/IntrospectableViewType.swift +++ b/Sources/IntrospectableViewType.swift @@ -1,4 +1,14 @@ public protocol IntrospectableViewType { + /// The scope of introspection for this particular view type, i.e. where introspect + /// should look to find the desired target view relative to the applied + /// `.introspect(...)` modifier. + /// + /// While the scope can be overridden by the user in their `.introspect(...)` call, + /// most of the time it's preferable to defer to the view type's own scope, + /// as it guarantees introspection is working as intended by the vendor. + /// + /// Defaults to `.receiver` if left unimplemented, which is a sensible one in + /// most cases if you're looking to implement your own view type. var scope: IntrospectionScope { get } } From 6dce3c8f5bfa8bc20120c7497da27e984a8813aa Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 27 Jun 2023 15:29:20 +0100 Subject: [PATCH 072/116] Bump to 0.7.0 (#274) --- CHANGELOG.md | 2 ++ README.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8413e476..03a1488a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Changelog ## master +## [0.7.0] + ### SwiftUIIntrospect - Added: window introspection (#269) diff --git a/README.md b/README.md index a46f6ea10..b6b0627a8 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Install ```swift let package = Package( dependencies: [ - .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.6.1"), + .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.7.0"), ], targets: [ .target(name: <#Target Name#>, dependencies: [ From 8d4962cbdb84baff3da33602f56293a99bad7773 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Wed, 28 Jun 2023 13:03:29 +0100 Subject: [PATCH 073/116] `tea` it up (#276) --- .github/workflows/ci.yml | 16 +++++++--------- tea.yml | 3 +++ 2 files changed, 10 insertions(+), 9 deletions(-) create mode 100644 tea.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 521efe83b..b3b9f47ee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -101,16 +101,14 @@ jobs: - name: Git Checkout uses: actions/checkout@v3 - - name: Select Xcode version - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: ${{ matrix.xcode || '14.3' }} - - - name: Install tea + - name: Setup tea environment uses: teaxyz/setup@v0 + with: + +: | + github.com/RobotsAndPencils/xcodes - - name: Install xcbeautify - run: brew install xcbeautify + - name: Select Xcode version + run: sudo xcodes select ${{ matrix.xcode || '14.3' }} - if: ${{ matrix.install }} name: Install Required Runtime (${{ matrix.runtime }}) @@ -118,7 +116,7 @@ jobs: with: timeout_minutes: 15 max_attempts: 3 - command: sudo tea xcodes runtimes install '${{ matrix.runtime }}' + command: sudo xcodes runtimes install '${{ matrix.runtime }}' - name: List Available Runtimes and Simulators run: xcrun simctl list diff --git a/tea.yml b/tea.yml new file mode 100644 index 000000000..419f146e3 --- /dev/null +++ b/tea.yml @@ -0,0 +1,3 @@ +dependencies: + fastlane.tools: '*' # latest + tuist.io/xcbeautify: '*' # latest From d95a286cf51b1714e5e718355f607a929f2ffb7e Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:05:06 +0100 Subject: [PATCH 074/116] Schedule weekly CI runs (#277) --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b3b9f47ee..b492e0f1d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,8 @@ on: pull_request: branches: - "**" + schedule: + - cron: '3 3 * * 2' # 3:03 AM, every Tuesday concurrency: group: ci-${{ github.ref }} From afccdb505441a4b8391a798c2f346721764b3c4c Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Wed, 28 Jun 2023 18:58:55 +0100 Subject: [PATCH 075/116] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b6b0627a8..8d40e0784 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,7 @@ ScrollView { Text("Item") } .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { scrollView in - scrollView.refreshControl = UIRefreshControl() + scrollView.backgroundColor = .red } ``` From f21e4b41c4af4d72bfc36c87f5701783c36f3c9b Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Wed, 28 Jun 2023 23:29:06 +0100 Subject: [PATCH 076/116] Fix CI (#281) --- .github/workflows/ci.yml | 2 +- Tests/Tests/ViewTypes/NavigationSplitViewTests.swift | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b492e0f1d..fccaa0da4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,7 +107,7 @@ jobs: uses: teaxyz/setup@v0 with: +: | - github.com/RobotsAndPencils/xcodes + github.com/XcodesOrg/xcodes - name: Select Xcode version run: sudo xcodes select ${{ matrix.xcode || '14.3' }} diff --git a/Tests/Tests/ViewTypes/NavigationSplitViewTests.swift b/Tests/Tests/ViewTypes/NavigationSplitViewTests.swift index ddeff1d5a..39caa25e8 100644 --- a/Tests/Tests/ViewTypes/NavigationSplitViewTests.swift +++ b/Tests/Tests/ViewTypes/NavigationSplitViewTests.swift @@ -24,11 +24,11 @@ final class NavigationSplitViewTests: XCTestCase { NavigationSplitView { ZStack { Color.red - Text("Something") + Text("Root") } } detail: { ZStack { - Color.red + Color.blue Text("Detail") } } @@ -64,7 +64,10 @@ final class NavigationSplitViewTests: XCTestCase { #endif } } detail: { - Text("Detail") + ZStack { + Color.blue + Text("Detail") + } } } } From d1c7cdf2d9d001da0d434c04be1dcef76372f8bf Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Thu, 29 Jun 2023 00:45:00 +0100 Subject: [PATCH 077/116] Generate docs for extensions (#282) --- .spi.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.spi.yml b/.spi.yml index 030c08949..c139fb9cd 100644 --- a/.spi.yml +++ b/.spi.yml @@ -2,3 +2,4 @@ version: 1 builder: configs: - documentation_targets: [SwiftUIIntrospect] + custom_documentation_parameters: [--include-extended-types] From 5ab4a2216a607f48f27bb6f70dd4f916cf031456 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sat, 1 Jul 2023 10:50:45 +0100 Subject: [PATCH 078/116] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 8d40e0784..0f2265bee 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,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 { @@ -224,6 +225,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 { @@ -231,6 +233,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 ``` From fb2f27dd74df3f2cfbda37871ddc57e72a6ad502 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sat, 1 Jul 2023 11:04:03 +0100 Subject: [PATCH 079/116] Add advanced range-based platform version predicates (#285) --- CHANGELOG.md | 4 + Sources/Introspect.swift | 22 +-- Sources/PlatformVersion.swift | 196 +++++++++++++------ Sources/PlatformViewVersion.swift | 81 ++++++-- Tests/Tests.xcodeproj/project.pbxproj | 12 +- Tests/Tests/PlatformTests.swift | 130 ------------- Tests/Tests/PlatformVersionTests.swift | 256 +++++++++++++++++++++++++ 7 files changed, 477 insertions(+), 224 deletions(-) delete mode 100644 Tests/Tests/PlatformTests.swift create mode 100644 Tests/Tests/PlatformVersionTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 03a1488a0..5a829a57f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ Changelog ## master +- Added: advanced range-based platform version predicates (#285) +- Documentation: generate docs for extensions (#282) +- Infrastructure: set up `tea` for CI and local environments (#276) + ## [0.7.0] ### SwiftUIIntrospect diff --git a/Sources/Introspect.swift b/Sources/Introspect.swift index 19008a0e1..4c1be9a6c 100644 --- a/Sources/Introspect.swift +++ b/Sources/Introspect.swift @@ -21,27 +21,27 @@ extension View { /// /// - Parameters: /// - viewType: The type of view to be introspected. - /// - platforms: A list of `PlatformViewVersions` that specify platform-specific entities associated with the view, with one or more corresponding version numbers. - /// - scope: An optional `IntrospectionScope` that specifies the scope of introspection. + /// - platforms: A list of version predicates that specify platform-specific entities associated with the view. + /// - scope: Optionally overrides the view's default scope of introspection. /// - customize: A closure that hands over the underlying UIKit/AppKit instance ready for customization. /// /// Here's an example usage: /// /// ```swift /// struct ContentView: View { - /// @State var date = Date() + /// @State var text = "" /// /// var body: some View { - /// DatePicker("Pick a date", selection: $date) - /// .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { - /// print(type(of: $0)) // UIDatePicker + /// TextField("Placeholder", text: $text) + /// .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { + /// print(type(of: $0)) // UITextField /// } /// } /// } /// ``` public func introspect( _ viewType: SwiftUIViewType, - on platforms: (PlatformViewVersions)..., + on platforms: (PlatformViewVersionPredicate)..., scope: IntrospectionScope? = nil, customize: @escaping (PlatformSpecificEntity) -> Void ) -> some View { @@ -57,16 +57,12 @@ struct IntrospectModifier], + platforms: [PlatformViewVersionPredicate], scope: IntrospectionScope?, customize: @escaping (PlatformSpecificEntity) -> Void ) { self.scope = scope ?? viewType.scope - if let platform = platforms.first(where: \.isCurrent) { - self.selector = platform.selector ?? .default - } else { - self.selector = nil - } + self.selector = platforms.lazy.compactMap(\.selector).first self.customize = customize } diff --git a/Sources/PlatformVersion.swift b/Sources/PlatformVersion.swift index 194b59745..ad085d1cd 100644 --- a/Sources/PlatformVersion.swift +++ b/Sources/PlatformVersion.swift @@ -1,195 +1,275 @@ import Foundation +public enum PlatformVersionCondition { + case past + case current + case future +} + public protocol PlatformVersion { - var isCurrent: Bool { get } + var condition: PlatformVersionCondition? { get } +} + +extension PlatformVersion { + public var isCurrent: Bool { + condition == .current + } + + public var isCurrentOrPast: Bool { + condition == .current || condition == .past + } } public struct iOSVersion: PlatformVersion { - public let isCurrent: Bool + public let condition: PlatformVersionCondition? - public init(isCurrent: () -> Bool) { - self.isCurrent = isCurrent() + public init(condition: () -> PlatformVersionCondition?) { + self.condition = condition() } } extension iOSVersion { public static let v13 = iOSVersion { + #if os(iOS) if #available(iOS 14, *) { - return false + return .past } if #available(iOS 13, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v14 = iOSVersion { + #if os(iOS) if #available(iOS 15, *) { - return false + return .past } if #available(iOS 14, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v15 = iOSVersion { + #if os(iOS) if #available(iOS 16, *) { - return false + return .past } if #available(iOS 15, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v16 = iOSVersion { + #if os(iOS) if #available(iOS 17, *) { - return false + return .past } if #available(iOS 16, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v17 = iOSVersion { + #if os(iOS) if #available(iOS 18, *) { - return false + return .past } if #available(iOS 17, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } } public struct tvOSVersion: PlatformVersion { - public let isCurrent: Bool + public let condition: PlatformVersionCondition? - public init(isCurrent: () -> Bool) { - self.isCurrent = isCurrent() + public init(condition: () -> PlatformVersionCondition?) { + self.condition = condition() } } extension tvOSVersion { public static let v13 = tvOSVersion { + #if os(tvOS) if #available(tvOS 14, *) { - return false + return .past } if #available(tvOS 13, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v14 = tvOSVersion { + #if os(tvOS) if #available(tvOS 15, *) { - return false + return .past } if #available(tvOS 14, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v15 = tvOSVersion { + #if os(tvOS) if #available(tvOS 16, *) { - return false + return .past } if #available(tvOS 15, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v16 = tvOSVersion { + #if os(tvOS) if #available(tvOS 17, *) { - return false + return .past } if #available(tvOS 16, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v17 = tvOSVersion { + #if os(tvOS) if #available(tvOS 18, *) { - return false + return .past } if #available(tvOS 17, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } } public struct macOSVersion: PlatformVersion { - public let isCurrent: Bool + public let condition: PlatformVersionCondition? - public init(isCurrent: () -> Bool) { - self.isCurrent = isCurrent() + public init(condition: () -> PlatformVersionCondition?) { + self.condition = condition() } } extension macOSVersion { public static let v10_15 = macOSVersion { + #if os(macOS) if #available(macOS 11, *) { - return false + return .past } if #available(macOS 10.15, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v10_15_4 = macOSVersion { + #if os(macOS) if #available(macOS 11, *) { - return false + return .past } if #available(macOS 10.15.4, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v11 = macOSVersion { + #if os(macOS) if #available(macOS 12, *) { - return false + return .past } if #available(macOS 11, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v12 = macOSVersion { + #if os(macOS) if #available(macOS 13, *) { - return false + return .past } if #available(macOS 12, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v13 = macOSVersion { + #if os(macOS) if #available(macOS 14, *) { - return false + return .past } if #available(macOS 13, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } public static let v14 = macOSVersion { + #if os(macOS) if #available(macOS 15, *) { - return false + return .past } if #available(macOS 14, *) { - return true + return .current } - return false + return .future + #else + return nil + #endif } } diff --git a/Sources/PlatformViewVersion.swift b/Sources/PlatformViewVersion.swift index 9684a5527..5190340a0 100644 --- a/Sources/PlatformViewVersion.swift +++ b/Sources/PlatformViewVersion.swift @@ -1,31 +1,44 @@ import SwiftUI -public struct PlatformViewVersions { - let isCurrent: Bool +public struct PlatformViewVersionPredicate { let selector: IntrospectionSelector? private init( - _ versions: [PlatformViewVersion] + _ versions: [PlatformViewVersion], + matches: (PlatformViewVersion) -> Bool ) { - if let currentVersion = versions.first(where: \.isCurrent) { - self.isCurrent = true - self.selector = currentVersion.selector + if let matchingVersion = versions.first(where: matches) { + self.selector = matchingVersion.selector ?? .default } else { - self.isCurrent = false self.selector = nil } } public static func iOS(_ versions: (iOSViewVersion)...) -> Self { - Self(versions) + Self(versions, matches: \.isCurrent) + } + + @_spi(Advanced) + public static func iOS(_ versions: PartialRangeFrom>) -> Self { + Self([versions.lowerBound], matches: \.isCurrentOrPast) } public static func tvOS(_ versions: (tvOSViewVersion)...) -> Self { - Self(versions) + Self(versions, matches: \.isCurrent) + } + + @_spi(Advanced) + public static func tvOS(_ versions: PartialRangeFrom>) -> Self { + Self([versions.lowerBound], matches: \.isCurrentOrPast) } public static func macOS(_ versions: (macOSViewVersion)...) -> Self { - Self(versions) + Self(versions, matches: \.isCurrent) + } + + @_spi(Advanced) + public static func macOS(_ versions: PartialRangeFrom>) -> Self { + Self([versions.lowerBound], matches: \.isCurrentOrPast) } } @@ -36,14 +49,12 @@ public typealias tvOSViewVersion = PlatformViewVersion -public struct PlatformViewVersion { - let isCurrent: Bool - let selector: IntrospectionSelector? -} +public enum PlatformViewVersion { + @_spi(Private) case available(Version, IntrospectionSelector?) + @_spi(Private) case unavailable -extension PlatformViewVersion { @_spi(Internals) public init(for version: Version, selector: IntrospectionSelector? = nil) { - self.init(isCurrent: version.isCurrent, selector: selector) + self = .available(version, selector) } @_spi(Internals) public static func unavailable(file: StaticString = #file, line: UInt = #line) -> Self { @@ -60,6 +71,42 @@ extension PlatformViewVersion { https://github.com/siteline/swiftui-introspect/issues/new?title=`\(fileName):\(line)`+should+be+marked+unavailable """ ) - return Self(isCurrent: false, selector: nil) + return .unavailable + } + + private var version: Version? { + if case .available(let version, _) = self { + return version + } else { + return nil + } + } + + fileprivate var selector: IntrospectionSelector? { + if case .available(_, let selector) = self { + return selector + } else { + return nil + } + } + + fileprivate var isCurrent: Bool { + version?.isCurrent ?? false + } + + fileprivate var isCurrentOrPast: Bool { + version?.isCurrentOrPast ?? false + } +} + +// This conformance isn't meant to be used directly by the user, +// it's only to satisfy requirements for forming ranges (e.g. `.v15...`). +extension PlatformViewVersion: Comparable { + public static func == (lhs: Self, rhs: Self) -> Bool { + true + } + + public static func < (lhs: Self, rhs: Self) -> Bool { + true } } diff --git a/Tests/Tests.xcodeproj/project.pbxproj b/Tests/Tests.xcodeproj/project.pbxproj index 0c191f36c..1c1de178c 100644 --- a/Tests/Tests.xcodeproj/project.pbxproj +++ b/Tests/Tests.xcodeproj/project.pbxproj @@ -38,7 +38,7 @@ 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 */; }; + D50E2F7B2A2B9F6600BAFB03 /* PlatformVersionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F0BE6729C0DC4900AD95AB /* PlatformVersionTests.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 */; }; @@ -109,7 +109,7 @@ D5ADFAD72A4A4653009494FD /* PopoverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ADFACF2A4A3E54009494FD /* PopoverTests.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 */; }; + D5F0BE6A29C0DC4900AD95AB /* PlatformVersionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F0BE6729C0DC4900AD95AB /* PlatformVersionTests.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 */ @@ -193,7 +193,7 @@ 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 = ""; }; + D5F0BE6729C0DC4900AD95AB /* PlatformVersionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlatformVersionTests.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 */ @@ -330,7 +330,7 @@ isa = PBXGroup; children = ( D5B67B852A0D3193007D5D9B /* ViewTypes */, - D5F0BE6729C0DC4900AD95AB /* PlatformTests.swift */, + D5F0BE6729C0DC4900AD95AB /* PlatformVersionTests.swift */, D58CE15729C621DD0081BFB0 /* TestUtils.swift */, ); path = Tests; @@ -545,7 +545,7 @@ D50E2F782A2B9F6600BAFB03 /* StepperTests.swift in Sources */, D50E2F792A2B9F6600BAFB03 /* ColorPickerTests.swift in Sources */, D50E2F7A2A2B9F6600BAFB03 /* ToggleWithButtonStyleTests.swift in Sources */, - D50E2F7B2A2B9F6600BAFB03 /* PlatformTests.swift in Sources */, + D50E2F7B2A2B9F6600BAFB03 /* PlatformVersionTests.swift in Sources */, D50E2F7C2A2B9F6600BAFB03 /* TestUtils.swift in Sources */, D50E2F7D2A2B9F6600BAFB03 /* PickerWithSegmentedStyleTests.swift in Sources */, D50E2F7E2A2B9F6600BAFB03 /* TabViewWithPageStyleTests.swift in Sources */, @@ -610,7 +610,7 @@ D5ADFAD22A4A41CB009494FD /* SignInWithAppleButtonTests.swift in Sources */, D58119DA2A23B7700081F853 /* ColorPickerTests.swift in Sources */, D575068E2A27D4DC00A628E4 /* ToggleWithButtonStyleTests.swift in Sources */, - D5F0BE6A29C0DC4900AD95AB /* PlatformTests.swift in Sources */, + D5F0BE6A29C0DC4900AD95AB /* PlatformVersionTests.swift in Sources */, D58CE15829C621DD0081BFB0 /* TestUtils.swift in Sources */, D57506782A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift in Sources */, D58119CE2A23A4A70081F853 /* TabViewWithPageStyleTests.swift in Sources */, diff --git a/Tests/Tests/PlatformTests.swift b/Tests/Tests/PlatformTests.swift deleted file mode 100644 index 0c736fa33..000000000 --- a/Tests/Tests/PlatformTests.swift +++ /dev/null @@ -1,130 +0,0 @@ -import SwiftUIIntrospect -import XCTest - -final class PlatformTests: XCTestCase { - func test_iOS() { - #if os(iOS) - 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) - XCTAssertEqual(iOSVersion.v13.isCurrent, false) - #endif - } - - func test_macOS() { - #if os(macOS) - 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) - XCTAssertEqual(macOSVersion.v10_15.isCurrent, false) - #endif - } - - func test_tvOS() { - #if os(tvOS) - 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) - XCTAssertEqual(tvOSVersion.v13.isCurrent, false) - #endif - } -} diff --git a/Tests/Tests/PlatformVersionTests.swift b/Tests/Tests/PlatformVersionTests.swift new file mode 100644 index 000000000..7a0bb953e --- /dev/null +++ b/Tests/Tests/PlatformVersionTests.swift @@ -0,0 +1,256 @@ +import SwiftUIIntrospect +import XCTest + +final class PlatformVersionTests: XCTestCase { + func test_iOS_isCurrent() { + #if os(iOS) + 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) + XCTAssertEqual(iOSVersion.v13.isCurrent, false) + #endif + } + + func test_iOS_isCurrentOrPast() { + #if os(iOS) + if #available(iOS 17, *) { + XCTAssertEqual(iOSVersion.v17.isCurrentOrPast, true) + XCTAssertEqual(iOSVersion.v16.isCurrentOrPast, true) + XCTAssertEqual(iOSVersion.v15.isCurrentOrPast, true) + XCTAssertEqual(iOSVersion.v14.isCurrentOrPast, true) + XCTAssertEqual(iOSVersion.v13.isCurrentOrPast, true) + } else if #available(iOS 16, *) { + XCTAssertEqual(iOSVersion.v17.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v16.isCurrentOrPast, true) + XCTAssertEqual(iOSVersion.v15.isCurrentOrPast, true) + XCTAssertEqual(iOSVersion.v14.isCurrentOrPast, true) + XCTAssertEqual(iOSVersion.v13.isCurrentOrPast, true) + } else if #available(iOS 15, *) { + XCTAssertEqual(iOSVersion.v17.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v16.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v15.isCurrentOrPast, true) + XCTAssertEqual(iOSVersion.v14.isCurrentOrPast, true) + XCTAssertEqual(iOSVersion.v13.isCurrentOrPast, true) + } else if #available(iOS 14, *) { + XCTAssertEqual(iOSVersion.v17.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v16.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v15.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v14.isCurrentOrPast, true) + XCTAssertEqual(iOSVersion.v13.isCurrentOrPast, true) + } else if #available(iOS 13, *) { + XCTAssertEqual(iOSVersion.v17.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v16.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v15.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v14.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v13.isCurrentOrPast, true) + } + #else + XCTAssertEqual(iOSVersion.v17.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v16.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v15.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v14.isCurrentOrPast, false) + XCTAssertEqual(iOSVersion.v13.isCurrentOrPast, false) + #endif + } + + func test_macOS_isCurrent() { + #if os(macOS) + 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) + XCTAssertEqual(macOSVersion.v10_15.isCurrent, false) + #endif + } + + func test_macOS_isCurrentOrPast() { + #if os(macOS) + if #available(macOS 14, *) { + XCTAssertEqual(macOSVersion.v14.isCurrentOrPast, true) + XCTAssertEqual(macOSVersion.v13.isCurrentOrPast, true) + XCTAssertEqual(macOSVersion.v12.isCurrentOrPast, true) + XCTAssertEqual(macOSVersion.v11.isCurrentOrPast, true) + XCTAssertEqual(macOSVersion.v10_15.isCurrentOrPast, true) + } else if #available(macOS 13, *) { + XCTAssertEqual(macOSVersion.v14.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v13.isCurrentOrPast, true) + XCTAssertEqual(macOSVersion.v12.isCurrentOrPast, true) + XCTAssertEqual(macOSVersion.v11.isCurrentOrPast, true) + XCTAssertEqual(macOSVersion.v10_15.isCurrentOrPast, true) + } else if #available(macOS 12, *) { + XCTAssertEqual(macOSVersion.v14.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v13.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v12.isCurrentOrPast, true) + XCTAssertEqual(macOSVersion.v11.isCurrentOrPast, true) + XCTAssertEqual(macOSVersion.v10_15.isCurrentOrPast, true) + } else if #available(macOS 11, *) { + XCTAssertEqual(macOSVersion.v14.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v13.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v12.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v11.isCurrentOrPast, true) + XCTAssertEqual(macOSVersion.v10_15.isCurrentOrPast, true) + } else if #available(macOS 10.15, *) { + XCTAssertEqual(macOSVersion.v14.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v13.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v12.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v11.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v10_15.isCurrentOrPast, true) + } + #else + XCTAssertEqual(macOSVersion.v14.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v13.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v12.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v11.isCurrentOrPast, false) + XCTAssertEqual(macOSVersion.v10_15.isCurrentOrPast, false) + #endif + } + + func test_tvOS_isCurrent() { + #if os(tvOS) + 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) + XCTAssertEqual(tvOSVersion.v13.isCurrent, false) + #endif + } + + func test_tvOS_isCurrentOrPast() { + #if os(tvOS) + if #available(tvOS 17, *) { + XCTAssertEqual(tvOSVersion.v17.isCurrentOrPast, true) + XCTAssertEqual(tvOSVersion.v16.isCurrentOrPast, true) + XCTAssertEqual(tvOSVersion.v15.isCurrentOrPast, true) + XCTAssertEqual(tvOSVersion.v14.isCurrentOrPast, true) + XCTAssertEqual(tvOSVersion.v13.isCurrentOrPast, true) + } else if #available(tvOS 16, *) { + XCTAssertEqual(tvOSVersion.v17.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v16.isCurrentOrPast, true) + XCTAssertEqual(tvOSVersion.v15.isCurrentOrPast, true) + XCTAssertEqual(tvOSVersion.v14.isCurrentOrPast, true) + XCTAssertEqual(tvOSVersion.v13.isCurrentOrPast, true) + } else if #available(tvOS 15, *) { + XCTAssertEqual(tvOSVersion.v17.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v16.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v15.isCurrentOrPast, true) + XCTAssertEqual(tvOSVersion.v14.isCurrentOrPast, true) + XCTAssertEqual(tvOSVersion.v13.isCurrentOrPast, true) + } else if #available(tvOS 14, *) { + XCTAssertEqual(tvOSVersion.v17.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v16.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v15.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v14.isCurrentOrPast, true) + XCTAssertEqual(tvOSVersion.v13.isCurrentOrPast, true) + } else if #available(tvOS 13, *) { + XCTAssertEqual(tvOSVersion.v17.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v16.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v15.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v14.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v13.isCurrentOrPast, true) + } + #else + XCTAssertEqual(tvOSVersion.v17.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v16.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v15.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v14.isCurrentOrPast, false) + XCTAssertEqual(tvOSVersion.v13.isCurrentOrPast, false) + #endif + } +} From 9843e3f5cb449b92c7f2ed5b62e74cdc89a6b1ff Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sat, 1 Jul 2023 12:05:59 +0100 Subject: [PATCH 080/116] Add `Map` introspection (#288) --- CHANGELOG.md | 1 + Sources/ViewTypes/Map.swift | 84 +++++++++++++++++++++++++++ Tests/Tests.xcodeproj/project.pbxproj | 4 ++ Tests/Tests/ViewTypes/MapTests.swift | 52 +++++++++++++++++ 4 files changed, 141 insertions(+) create mode 100644 Sources/ViewTypes/Map.swift create mode 100644 Tests/Tests/ViewTypes/MapTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a829a57f..e4bef3ed7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Changelog ## master +- Added: `Map` introspection (#288) - Added: advanced range-based platform version predicates (#285) - Documentation: generate docs for extensions (#282) - Infrastructure: set up `tea` for CI and local environments (#276) diff --git a/Sources/ViewTypes/Map.swift b/Sources/ViewTypes/Map.swift new file mode 100644 index 000000000..891d3b38e --- /dev/null +++ b/Sources/ViewTypes/Map.swift @@ -0,0 +1,84 @@ +import SwiftUI + +/// An abstract representation of the `Map` type in SwiftUI. +/// +/// ### iOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 51.507222, longitude: -0.1275), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)) +/// +/// var body: some View { +/// Map(coordinateRegion: $region) +/// .introspect(.map, on: .iOS(.v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // MKMapView +/// } +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 51.507222, longitude: -0.1275), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)) +/// +/// var body: some View { +/// Map(coordinateRegion: $region) +/// .introspect(.map, on: .tvOS(.v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // MKMapView +/// } +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 51.507222, longitude: -0.1275), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)) +/// +/// var body: some View { +/// Map(coordinateRegion: $region) +/// .introspect(.map, on: .macOS(.v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // MKMapView +/// } +/// } +/// } +/// ``` +public struct MapType: IntrospectableViewType {} + +#if canImport(MapKit) +import MapKit + +extension IntrospectableViewType where Self == MapType { + public static var map: Self { .init() } +} + +extension iOSViewVersion { + @available(*, unavailable, message: "Map 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) + public static let v17 = Self(for: .v17) +} + +extension tvOSViewVersion { + @available(*, unavailable, message: "Map 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) + public static let v17 = Self(for: .v17) +} + +extension macOSViewVersion { + @available(*, unavailable, message: "Map 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) + public static let v14 = Self(for: .v14) +} +#endif diff --git a/Tests/Tests.xcodeproj/project.pbxproj b/Tests/Tests.xcodeproj/project.pbxproj index 1c1de178c..193d1c514 100644 --- a/Tests/Tests.xcodeproj/project.pbxproj +++ b/Tests/Tests.xcodeproj/project.pbxproj @@ -97,6 +97,7 @@ 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 */; }; + D5AAF56F2A502EF000CAFFB6 /* MapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AAF56E2A502EF000CAFFB6 /* MapTests.swift */; }; D5AD0D912A114B98003D8DEC /* TextFieldWithVerticalAxisTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AD0D902A114B98003D8DEC /* TextFieldWithVerticalAxisTests.swift */; }; D5ADFACC2A4A22AE009494FD /* SheetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ADFACB2A4A22AE009494FD /* SheetTests.swift */; }; D5ADFACE2A4A3482009494FD /* FullScreenCoverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ADFACD2A4A3482009494FD /* FullScreenCoverTests.swift */; }; @@ -184,6 +185,7 @@ 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 = ""; }; + D5AAF56E2A502EF000CAFFB6 /* MapTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTests.swift; sourceTree = ""; }; D5AD0D902A114B98003D8DEC /* TextFieldWithVerticalAxisTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldWithVerticalAxisTests.swift; sourceTree = ""; }; D5ADFACB2A4A22AE009494FD /* SheetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SheetTests.swift; sourceTree = ""; }; D5ADFACD2A4A3482009494FD /* FullScreenCoverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenCoverTests.swift; sourceTree = ""; }; @@ -262,6 +264,7 @@ D575067F2A27C55600A628E4 /* ListWithInsetStyleTests.swift */, D575067B2A27C24600A628E4 /* ListWithPlainStyleTests.swift */, D57506832A27C8D400A628E4 /* ListWithSidebarStyleTests.swift */, + D5AAF56E2A502EF000CAFFB6 /* MapTests.swift */, D58547F92A1D12270068ADF4 /* NavigationSplitViewTests.swift */, D58547F72A1CDD740068ADF4 /* NavigationStackTests.swift */, D5F8D5EE2A1E87950054E9AB /* NavigationViewWithColumnsStyleTests.swift */, @@ -602,6 +605,7 @@ D5ADFAD02A4A3E54009494FD /* PopoverTests.swift in Sources */, D58119D82A23B3B00081F853 /* ButtonTests.swift in Sources */, D55F448D2A1FF209003381E4 /* ListTests.swift in Sources */, + D5AAF56F2A502EF000CAFFB6 /* MapTests.swift in Sources */, D58547FA2A1D12270068ADF4 /* NavigationSplitViewTests.swift in Sources */, D5F8D5EF2A1E87950054E9AB /* NavigationViewWithColumnsStyleTests.swift in Sources */, D57506882A27CB9800A628E4 /* FormTests.swift in Sources */, diff --git a/Tests/Tests/ViewTypes/MapTests.swift b/Tests/Tests/ViewTypes/MapTests.swift new file mode 100644 index 000000000..ac05532be --- /dev/null +++ b/Tests/Tests/ViewTypes/MapTests.swift @@ -0,0 +1,52 @@ +#if canImport(MapKit) +import MapKit +import SwiftUI +import SwiftUIIntrospect +import XCTest + +@available(iOS 14, tvOS 14, macOS 11, *) +final class MapTests: XCTestCase { + typealias PlatformMap = MKMapView + + func testMap() throws { + guard #available(iOS 14, tvOS 14, macOS 11, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformMap.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + let region = Binding.constant(MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 51.507222, longitude: -0.1275), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))) + + VStack { + Map(coordinateRegion: region) + .introspect( + .map, + on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .macOS(.v11, .v12, .v13, .v14), + customize: spy0 + ) + + Map(coordinateRegion: region) + .introspect( + .map, + on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .macOS(.v11, .v12, .v13, .v14), + customize: spy1 + ) + + Map(coordinateRegion: region) + .introspect( + .map, + on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .macOS(.v11, .v12, .v13, .v14), + customize: spy2 + ) + } + } extraAssertions: { + XCTAssertNotIdentical($0[safe: 0], $0[safe: 1]) + XCTAssertNotIdentical($0[safe: 0], $0[safe: 2]) + XCTAssertNotIdentical($0[safe: 1], $0[safe: 2]) + } + } +} +#endif From 005d009de398c29a6444758616cc71ac615145df Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sat, 1 Jul 2023 12:49:46 +0100 Subject: [PATCH 081/116] Remove ACLs from docs (#289) --- Sources/ViewTypes/FullScreenCover.swift | 8 ++++---- Sources/ViewTypes/Popover.swift | 4 ++-- Sources/ViewTypes/Sheet.swift | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Sources/ViewTypes/FullScreenCover.swift b/Sources/ViewTypes/FullScreenCover.swift index 0243b41c2..143295d7a 100644 --- a/Sources/ViewTypes/FullScreenCover.swift +++ b/Sources/ViewTypes/FullScreenCover.swift @@ -5,10 +5,10 @@ import SwiftUI /// ### iOS /// /// ```swift -/// public struct ContentView: View { +/// struct ContentView: View { /// @State var isPresented = false /// -/// public var body: some View { +/// var body: some View { /// Button("Present", action: { isPresented = true }) /// .fullScreenCover(isPresented: $isPresented) { /// Button("Dismiss", action: { isPresented = false }) @@ -23,10 +23,10 @@ import SwiftUI /// ### tvOS /// /// ```swift -/// public struct ContentView: View { +/// struct ContentView: View { /// @State var isPresented = false /// -/// public var body: some View { +/// var body: some View { /// Button("Present", action: { isPresented = true }) /// .fullScreenCover(isPresented: $isPresented) { /// Button("Dismiss", action: { isPresented = false }) diff --git a/Sources/ViewTypes/Popover.swift b/Sources/ViewTypes/Popover.swift index cf8e19c23..b4b9562ee 100644 --- a/Sources/ViewTypes/Popover.swift +++ b/Sources/ViewTypes/Popover.swift @@ -5,10 +5,10 @@ import SwiftUI /// ### iOS /// /// ```swift -/// public struct ContentView: View { +/// struct ContentView: View { /// @State var isPresented = false /// -/// public var body: some View { +/// var body: some View { /// Button("Present", action: { isPresented = true }) /// .popover(isPresented: $isPresented) { /// Button("Dismiss", action: { isPresented = false }) diff --git a/Sources/ViewTypes/Sheet.swift b/Sources/ViewTypes/Sheet.swift index af769e522..695e1da4a 100644 --- a/Sources/ViewTypes/Sheet.swift +++ b/Sources/ViewTypes/Sheet.swift @@ -5,10 +5,10 @@ import SwiftUI /// ### iOS /// /// ```swift -/// public struct ContentView: View { +/// struct ContentView: View { /// @State var isPresented = false /// -/// public var body: some View { +/// var body: some View { /// Button("Present", action: { isPresented = true }) /// .sheet(isPresented: $isPresented) { /// Button("Dismiss", action: { isPresented = false }) @@ -23,10 +23,10 @@ import SwiftUI /// ### tvOS /// /// ```swift -/// public struct ContentView: View { +/// struct ContentView: View { /// @State var isPresented = false /// -/// public var body: some View { +/// var body: some View { /// Button("Present", action: { isPresented = true }) /// .sheet(isPresented: $isPresented) { /// Button("Dismiss", action: { isPresented = false }) From d49a5788a59bb6f84b499d1fd8d05a0e2cbeb9ae Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sat, 1 Jul 2023 13:56:17 +0100 Subject: [PATCH 082/116] Rename `@_spi(Internals)` to `@_spi(Advanced)` (#290) --- CHANGELOG.md | 1 + README.md | 2 +- Sources/Introspect.swift | 4 ++-- Sources/IntrospectionSelector.swift | 10 +++++----- Sources/PlatformViewVersion.swift | 8 ++++---- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4bef3ed7..04134c693 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Changelog - Added: `Map` introspection (#288) - Added: advanced range-based platform version predicates (#285) +- Changed: renamed `@_spi(Internals)` to `@_spi(Advanced)` (#290) - Documentation: generate docs for extensions (#282) - Infrastructure: set up `tea` for CI and local environments (#276) diff --git a/README.md b/README.md index 0f2265bee..be22b8ee9 100644 --- a/README.md +++ b/README.md @@ -203,7 +203,7 @@ 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 +@_spi(Advanced) import SwiftUIIntrospect public struct TextFieldType: IntrospectableViewType {} diff --git a/Sources/Introspect.swift b/Sources/Introspect.swift index 4c1be9a6c..0379f59f9 100644 --- a/Sources/Introspect.swift +++ b/Sources/Introspect.swift @@ -9,9 +9,9 @@ public struct IntrospectionScope: OptionSet { /// Look for an `ancestor` relative to the `.introspect(...)` modifier. public static let ancestor = Self(rawValue: 1 << 1) - @_spi(Private) public let rawValue: UInt + @_spi(Internals) public let rawValue: UInt - @_spi(Private) public init(rawValue: UInt) { + @_spi(Internals) public init(rawValue: UInt) { self.rawValue = rawValue } } diff --git a/Sources/IntrospectionSelector.swift b/Sources/IntrospectionSelector.swift index 3b93efa12..6574054ff 100644 --- a/Sources/IntrospectionSelector.swift +++ b/Sources/IntrospectionSelector.swift @@ -1,9 +1,9 @@ -@_spi(Internals) +@_spi(Advanced) public struct IntrospectionSelector { - @_spi(Internals) + @_spi(Advanced) public static var `default`: Self { .from(Target.self, selector: { $0 }) } - @_spi(Internals) + @_spi(Advanced) public static func from(_ entryType: Entry.Type, selector: @escaping (Entry) -> Target?) -> Self { .init( receiverSelector: { controller in @@ -26,14 +26,14 @@ public struct IntrospectionSelector { self.ancestorSelector = ancestorSelector } - @_spi(Internals) + @_spi(Advanced) public func withReceiverSelector(_ selector: @escaping (PlatformViewController) -> Target?) -> Self { var copy = self copy.receiverSelector = selector return copy } - @_spi(Internals) + @_spi(Advanced) public func withAncestorSelector(_ selector: @escaping (PlatformViewController) -> Target?) -> Self { var copy = self copy.ancestorSelector = selector diff --git a/Sources/PlatformViewVersion.swift b/Sources/PlatformViewVersion.swift index 5190340a0..fa3aeeda8 100644 --- a/Sources/PlatformViewVersion.swift +++ b/Sources/PlatformViewVersion.swift @@ -50,14 +50,14 @@ public typealias macOSViewVersion public enum PlatformViewVersion { - @_spi(Private) case available(Version, IntrospectionSelector?) - @_spi(Private) case unavailable + @_spi(Internals) case available(Version, IntrospectionSelector?) + @_spi(Internals) case unavailable - @_spi(Internals) public init(for version: Version, selector: IntrospectionSelector? = nil) { + @_spi(Advanced) public init(for version: Version, selector: IntrospectionSelector? = nil) { self = .available(version, selector) } - @_spi(Internals) public static func unavailable(file: StaticString = #file, line: UInt = #line) -> Self { + @_spi(Advanced) 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( From 8bf15ad33a529359200bd419a72ca2dda841089b Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sat, 1 Jul 2023 13:58:18 +0100 Subject: [PATCH 083/116] Bump to 0.8.0 (#291) --- CHANGELOG.md | 2 ++ README.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04134c693..8c79a30aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Changelog ## master +## [0.8.0] + - Added: `Map` introspection (#288) - Added: advanced range-based platform version predicates (#285) - Changed: renamed `@_spi(Internals)` to `@_spi(Advanced)` (#290) diff --git a/README.md b/README.md index be22b8ee9..2fdc50e2c 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Install ```swift let package = Package( dependencies: [ - .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.7.0"), + .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.8.0"), ], targets: [ .target(name: <#Target Name#>, dependencies: [ From caba206fca2d3a067cd4537ef167eda6af8d9319 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sun, 2 Jul 2023 13:54:12 +0100 Subject: [PATCH 084/116] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2fdc50e2c..afa908fad 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ Introspection - [`List` with `.inset` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithinsetstyletype) - [`List` with `.sidebar` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listwithsidebarstyletype) - [`ListCell`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/listcelltype) +- [`Map`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/maptype) - [`NavigationSplitView`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/navigationsplitviewtype) - [`NavigationStack`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/navigationstacktype) - [`NavigationView` with `.columns` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/NavigationViewWithColumnsStyleType) From c150d11f09eae819bd44d161124655512c1f66b4 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 3 Jul 2023 15:31:50 +0100 Subject: [PATCH 085/116] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index afa908fad..95ead20c0 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ ScrollView { > **Warning** > -> Although this introspection method itself is very solid and unlikely to break in itself, 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. +> Although this introspection method itself is very solid and unlikely to break in itself, future OS releases require explicit opt-in for introspection (`.iOS(.vXYZ)`), given potential differences in underlying UIKit/AppKit view types between major 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`: From f91114c0f41ce7636752130c87d2632260996d57 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 3 Jul 2023 15:32:41 +0100 Subject: [PATCH 086/116] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 95ead20c0..6fce3a766 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ ScrollView { > **Warning** > -> Although this introspection method itself is very solid and unlikely to break in itself, future OS releases require explicit opt-in for introspection (`.iOS(.vXYZ)`), given potential differences in underlying UIKit/AppKit view types between major OS versions. +> Although this introspection method is very solid and unlikely to break in itself, future OS releases require explicit opt-in for introspection (`.iOS(.vXYZ)`), given potential differences in underlying UIKit/AppKit view types between major 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`: From b8678708e97e079e8b9fa8319918ea51d7ab1632 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 4 Jul 2023 01:53:42 +0100 Subject: [PATCH 087/116] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6fce3a766..8736decc8 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ ScrollView { ### 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. +`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` modifier is simply ignored if UIKit/AppKit views cannot be found. Install ------- From 8c1140f18319786fcff53cb9fe2fc3fb42a724ed Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Thu, 6 Jul 2023 13:07:48 +0100 Subject: [PATCH 088/116] Add page control introspection (#297) --- CHANGELOG.md | 2 + README.md | 1 + Sources/ViewTypes/PageControl.swift | 67 +++++++++++++++++++ Sources/ViewTypes/TabViewWithPageStyle.swift | 4 +- Tests/Tests.xcodeproj/project.pbxproj | 4 ++ Tests/Tests/ViewTypes/PageControlTests.swift | 31 +++++++++ .../ViewTypes/TabViewWithPageStyleTests.swift | 10 ++- 7 files changed, 111 insertions(+), 8 deletions(-) create mode 100644 Sources/ViewTypes/PageControl.swift create mode 100644 Tests/Tests/ViewTypes/PageControlTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c79a30aa..3684082a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Changelog ## master +- Added: page control introspection (#297) + ## [0.8.0] - Added: `Map` introspection (#288) diff --git a/README.md b/README.md index 8736decc8..fece6509c 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,7 @@ Introspection - [`NavigationStack`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/navigationstacktype) - [`NavigationView` with `.columns` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/NavigationViewWithColumnsStyleType) - [`NavigationView` with `.stack` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/NavigationViewWithStackStyleType) +- [`PageControl`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pagecontroltype) - [`Picker` with `.menu` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithmenustyletype) - [`Picker` with `.segmented` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithsegmentedstyletype) - [`Picker` with `.wheel` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/pickerwithwheelstyletype) diff --git a/Sources/ViewTypes/PageControl.swift b/Sources/ViewTypes/PageControl.swift new file mode 100644 index 000000000..aa5f4b6ba --- /dev/null +++ b/Sources/ViewTypes/PageControl.swift @@ -0,0 +1,67 @@ +import SwiftUI + +/// An abstract representation of the page control type in SwiftUI. +/// +/// ### iOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// TabView { +/// Text("Page 1").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red) +/// Text("Page 2").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.blue) +/// } +/// .tabViewStyle(.page(indexDisplayMode: .always)) +/// .introspect(.pageControl, on: .iOS(.v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIPageControl +/// } +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// TabView { +/// Text("Page 1").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red) +/// Text("Page 2").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.blue) +/// } +/// .tabViewStyle(.page(indexDisplayMode: .always)) +/// .introspect(.pageControl, on: .tvOS(.v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UIPageControl +/// } +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// Not available. +/// +public struct PageControlType: IntrospectableViewType {} + +extension IntrospectableViewType where Self == PageControlType { + public static var pageControl: Self { .init() } +} + +#if canImport(UIKit) +extension iOSViewVersion { + @available(*, unavailable, message: ".tabViewStyle(.page) 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) + public static let v17 = Self(for: .v17) +} + +extension tvOSViewVersion { + @available(*, unavailable, message: ".tabViewStyle(.page) 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) + public static let v17 = Self(for: .v17) +} +#endif diff --git a/Sources/ViewTypes/TabViewWithPageStyle.swift b/Sources/ViewTypes/TabViewWithPageStyle.swift index 30e028b75..a300a5e89 100644 --- a/Sources/ViewTypes/TabViewWithPageStyle.swift +++ b/Sources/ViewTypes/TabViewWithPageStyle.swift @@ -53,7 +53,7 @@ extension IntrospectableViewType where Self == TabViewWithPageStyleType { #if canImport(UIKit) extension iOSViewVersion { - @available(*, unavailable, message: "TabView {}.tabViewStyle(.page) isn't available on iOS 13") + @available(*, unavailable, message: ".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) @@ -62,7 +62,7 @@ extension iOSViewVersion { } extension tvOSViewVersion { - @available(*, unavailable, message: "TabView {}.tabViewStyle(.page) isn't available on tvOS 13") + @available(*, unavailable, message: ".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) diff --git a/Tests/Tests.xcodeproj/project.pbxproj b/Tests/Tests.xcodeproj/project.pbxproj index 193d1c514..746ca1a39 100644 --- a/Tests/Tests.xcodeproj/project.pbxproj +++ b/Tests/Tests.xcodeproj/project.pbxproj @@ -111,6 +111,7 @@ D5B67B842A0D318F007D5D9B /* TextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B67B832A0D318F007D5D9B /* TextFieldTests.swift */; }; D5F0BE4D29C0DBE800AD95AB /* TestsHostApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F0BE4C29C0DBE800AD95AB /* TestsHostApp.swift */; }; D5F0BE6A29C0DC4900AD95AB /* PlatformVersionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F0BE6729C0DC4900AD95AB /* PlatformVersionTests.swift */; }; + D5F26E022A561130001209E6 /* PageControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F26E012A561130001209E6 /* PageControlTests.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 */ @@ -196,6 +197,7 @@ 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 /* PlatformVersionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlatformVersionTests.swift; sourceTree = ""; }; + D5F26E012A561130001209E6 /* PageControlTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageControlTests.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 */ @@ -269,6 +271,7 @@ D58547F72A1CDD740068ADF4 /* NavigationStackTests.swift */, D5F8D5EE2A1E87950054E9AB /* NavigationViewWithColumnsStyleTests.swift */, D5F8D5EC2A1E7B490054E9AB /* NavigationViewWithStackStyleTests.swift */, + D5F26E012A561130001209E6 /* PageControlTests.swift */, D57506792A27BF6C00A628E4 /* PickerWithMenuStyleTests.swift */, D57506772A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift */, D58119D52A23AED70081F853 /* PickerWithWheelStyleTests.swift */, @@ -604,6 +607,7 @@ D58119D02A23A62C0081F853 /* SliderTests.swift in Sources */, D5ADFAD02A4A3E54009494FD /* PopoverTests.swift in Sources */, D58119D82A23B3B00081F853 /* ButtonTests.swift in Sources */, + D5F26E022A561130001209E6 /* PageControlTests.swift in Sources */, D55F448D2A1FF209003381E4 /* ListTests.swift in Sources */, D5AAF56F2A502EF000CAFFB6 /* MapTests.swift in Sources */, D58547FA2A1D12270068ADF4 /* NavigationSplitViewTests.swift in Sources */, diff --git a/Tests/Tests/ViewTypes/PageControlTests.swift b/Tests/Tests/ViewTypes/PageControlTests.swift new file mode 100644 index 000000000..ad1b4b3dc --- /dev/null +++ b/Tests/Tests/ViewTypes/PageControlTests.swift @@ -0,0 +1,31 @@ +#if !os(macOS) && !LEGACY_MACOS_SDK +import SwiftUI +import SwiftUIIntrospect +import XCTest + +@available(iOS 14, tvOS 14, *) +final class PageControlTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformPageControl = UIPageControl + #endif + + func testPageControl() throws { + guard #available(iOS 14, tvOS 14, *) else { + throw XCTSkip() + } + + XCTAssertViewIntrospection(of: PlatformPageControl.self) { spies in + let spy = spies[0] + + TabView { + Text("Page 1").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red) + Text("Page 2").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.blue) + } + .tabViewStyle(.page(indexDisplayMode: .always)) + #if os(iOS) || os(tvOS) + .introspect(.pageControl, on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy) + #endif + } + } +} +#endif diff --git a/Tests/Tests/ViewTypes/TabViewWithPageStyleTests.swift b/Tests/Tests/ViewTypes/TabViewWithPageStyleTests.swift index eac652c0d..8d9fa0a22 100644 --- a/Tests/Tests/ViewTypes/TabViewWithPageStyleTests.swift +++ b/Tests/Tests/ViewTypes/TabViewWithPageStyleTests.swift @@ -18,10 +18,8 @@ final class TabViewWithPageStyleTests: XCTestCase { let spy = spies[0] TabView { - ZStack { - Color.red - Text("Something") - } + Text("Page 1").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red) + Text("Page 2").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.blue) } .tabViewStyle(.page) #if os(iOS) || os(tvOS) @@ -39,11 +37,11 @@ final class TabViewWithPageStyleTests: XCTestCase { let spy = spies[0] TabView { - ZStack { Color.red; Text("1") } + Text("Page 1").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red) #if os(iOS) || os(tvOS) .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") } + Text("Page 2").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.blue) } .tabViewStyle(.page) } From 6a074945519a83ba1daad1cc261baa8f1e46666d Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Thu, 6 Jul 2023 15:05:51 +0100 Subject: [PATCH 089/116] Add view controller introspection (#298) --- CHANGELOG.md | 1 + README.md | 1 + Sources/ViewTypes/ViewController.swift | 72 +++++++++++++++++++ Tests/Tests.xcodeproj/project.pbxproj | 4 ++ .../Tests/ViewTypes/ViewControllerTests.swift | 46 ++++++++++++ 5 files changed, 124 insertions(+) create mode 100644 Sources/ViewTypes/ViewController.swift create mode 100644 Tests/Tests/ViewTypes/ViewControllerTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 3684082a6..9b43015fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Changelog ## master +- Added: view controller introspection (#298) - Added: page control introspection (#297) ## [0.8.0] diff --git a/README.md b/README.md index fece6509c..5516246df 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,7 @@ Introspection - [`Toggle` with `switch` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/togglewithswitchstyletype) - [`VideoPlayer`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/videoplayertype) - [`View`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/viewtype) +- [`ViewController`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/viewcontrollertype) - [`Window`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/windowtype) **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). diff --git a/Sources/ViewTypes/ViewController.swift b/Sources/ViewTypes/ViewController.swift new file mode 100644 index 000000000..ce1c1be8b --- /dev/null +++ b/Sources/ViewTypes/ViewController.swift @@ -0,0 +1,72 @@ +import SwiftUI + +/// An abstract representation of the receiving SwiftUI view's view controller, +/// or the closest ancestor view controller if missing. +/// +/// ### iOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// NavigationView { +/// Text("Root").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red) +/// .introspect(.viewController, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // some subclass of UIHostingController +/// } +/// } +/// .navigationViewStyle(.stack) +/// .introspect(.viewController, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UINavigationController +/// } +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// NavigationView { +/// Text("Root").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red) +/// .introspect(.viewController, on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // some subclass of UIHostingController +/// } +/// } +/// .navigationViewStyle(.stack) +/// .introspect(.viewController, on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UINavigationController +/// } +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// Not available. +/// +public struct ViewControllerType: IntrospectableViewType { + public var scope: IntrospectionScope { [.receiver, .ancestor] } +} + +extension IntrospectableViewType where Self == ViewControllerType { + public static var viewController: 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) + public static let v17 = Self(for: .v17) +} + +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 v17 = Self(for: .v17) +} +#endif diff --git a/Tests/Tests.xcodeproj/project.pbxproj b/Tests/Tests.xcodeproj/project.pbxproj index 746ca1a39..3d8327acc 100644 --- a/Tests/Tests.xcodeproj/project.pbxproj +++ b/Tests/Tests.xcodeproj/project.pbxproj @@ -112,6 +112,7 @@ D5F0BE4D29C0DBE800AD95AB /* TestsHostApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F0BE4C29C0DBE800AD95AB /* TestsHostApp.swift */; }; D5F0BE6A29C0DC4900AD95AB /* PlatformVersionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F0BE6729C0DC4900AD95AB /* PlatformVersionTests.swift */; }; D5F26E022A561130001209E6 /* PageControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F26E012A561130001209E6 /* PageControlTests.swift */; }; + D5F26E042A56E74B001209E6 /* ViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F26E032A56E74B001209E6 /* ViewControllerTests.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 */ @@ -198,6 +199,7 @@ D5F0BE5D29C0DC0000AD95AB /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; D5F0BE6729C0DC4900AD95AB /* PlatformVersionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlatformVersionTests.swift; sourceTree = ""; }; D5F26E012A561130001209E6 /* PageControlTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageControlTests.swift; sourceTree = ""; }; + D5F26E032A56E74B001209E6 /* ViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerTests.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 */ @@ -297,6 +299,7 @@ D575068B2A27D40500A628E4 /* ToggleWithSwitchStyleTests.swift */, D503B2AB2A49BFE300027F5F /* VideoPlayerTests.swift */, D58119C52A227E930081F853 /* ViewTests.swift */, + D5F26E032A56E74B001209E6 /* ViewControllerTests.swift */, D534D4DB2A4A596200218BFB /* WindowTests.swift */, ); path = ViewTypes; @@ -630,6 +633,7 @@ D575068C2A27D40500A628E4 /* ToggleWithSwitchStyleTests.swift in Sources */, D58119C42A211B8A0081F853 /* ListCellTests.swift in Sources */, D57506A22A281B9C00A628E4 /* SearchFieldTests.swift in Sources */, + D5F26E042A56E74B001209E6 /* ViewControllerTests.swift in Sources */, D58119C62A227E930081F853 /* ViewTests.swift in Sources */, D575067E2A27C43400A628E4 /* ListWithGroupedStyleTests.swift in Sources */, D575069C2A27F68700A628E4 /* ProgressViewWithCircularStyleTests.swift in Sources */, diff --git a/Tests/Tests/ViewTypes/ViewControllerTests.swift b/Tests/Tests/ViewTypes/ViewControllerTests.swift new file mode 100644 index 000000000..bdc987b8b --- /dev/null +++ b/Tests/Tests/ViewTypes/ViewControllerTests.swift @@ -0,0 +1,46 @@ +#if os(iOS) || os(tvOS) +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class ViewControllerTests: XCTestCase { + func testViewController() { + XCTAssertViewIntrospection(of: PlatformViewController.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + TabView { + NavigationView { + Text("Root").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red) + .introspect( + .viewController, + on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), + customize: spy2 + ) + } + .navigationViewStyle(.stack) + .tabItem { + Image(systemName: "1.circle") + Text("Tab 1") + } + .introspect( + .viewController, + on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), + customize: spy1 + ) + } + .introspect( + .viewController, + on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), + customize: spy0 + ) + } extraAssertions: { + XCTAssert($0[safe: 0] is UITabBarController) + XCTAssert($0[safe: 1] is UINavigationController) + XCTAssert(String(describing: $0[safe: 2]).contains("UIHostingController")) + XCTAssert($0[safe: 1] === $0[safe: 2]?.parent) + } + } +} +#endif From 124f3bf94be063ef319307f26663df6856ce06e5 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Thu, 6 Jul 2023 17:14:17 +0100 Subject: [PATCH 090/116] Gardening (#299) --- Sources/IntrospectionView.swift | 2 +- Tests/Tests/TestUtils.swift | 34 ++++++++++++++++----------------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/Sources/IntrospectionView.swift b/Sources/IntrospectionView.swift index 6842003c0..9ad399764 100644 --- a/Sources/IntrospectionView.swift +++ b/Sources/IntrospectionView.swift @@ -154,7 +154,7 @@ final class IntrospectionPlatformViewController: PlatformViewController { self.id = id super.init(nibName: nil, bundle: nil) self.handler = { [weak self] in - guard let self = self else { + guard let self else { return } handler?(self) diff --git a/Tests/Tests/TestUtils.swift b/Tests/Tests/TestUtils.swift index 1570ad9aa..b2dcd2dd7 100644 --- a/Tests/Tests/TestUtils.swift +++ b/Tests/Tests/TestUtils.swift @@ -28,36 +28,36 @@ enum TestUtils { } #endif -func XCTAssertViewIntrospection( - of type: PV.Type, - @ViewBuilder view: (Spies) -> V, - extraAssertions: ([PV]) -> Void = { _ in }, +func XCTAssertViewIntrospection( + of type: Entity.Type, + @ViewBuilder view: (Spies) -> some View, + extraAssertions: ([Entity]) -> Void = { _ in }, file: StaticString = #file, line: UInt = #line ) { - let spies = Spies() + let spies = Spies() let view = view(spies) TestUtils.present(view: view) XCTWaiter(delegate: spies).wait(for: spies.expectations.values.map(\.0), timeout: 3) - extraAssertions(spies.objects.sorted(by: { $0.key < $1.key }).map(\.value)) + extraAssertions(spies.entities.sorted(by: { $0.key < $1.key }).map(\.value)) } -final class Spies: NSObject, XCTWaiterDelegate { - private(set) var objects: [Int: PV] = [:] +final class Spies: NSObject, XCTWaiterDelegate { + private(set) var entities: [Int: Entity] = [:] private(set) var expectations: [ObjectIdentifier: (XCTestExpectation, StaticString, UInt)] = [:] subscript( number: Int, file: StaticString = #file, line: UInt = #line - ) -> (PV) -> Void { + ) -> (Entity) -> 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) + if let entity = entities[number] { + XCTAssert(entity === $0, "Found view was overriden by another view", file: file, line: line) } - objects[number] = $0 + entities[number] = $0 expectation.fulfill() } } @@ -97,12 +97,10 @@ final class Spies: NSObject, XCTWaiterDelegate { 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] + guard indices.contains(index) else { + XCTFail("Index \(index) is out of bounds", file: file, line: line) + return nil } + return self[index] } } From adb9e7a69fd75322dcdee0c20f2fb6640d6f0087 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Thu, 6 Jul 2023 18:24:49 +0100 Subject: [PATCH 091/116] Bump to 0.9.0 (#300) --- CHANGELOG.md | 2 ++ README.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b43015fc..37c4b866c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Changelog ## master +## [0.9.0] + - Added: view controller introspection (#298) - Added: page control introspection (#297) diff --git a/README.md b/README.md index 5516246df..466544925 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Install ```swift let package = Package( dependencies: [ - .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.8.0"), + .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.9.0"), ], targets: [ .target(name: <#Target Name#>, dependencies: [ From 2cb404d17d044995fe644b2e9fc020af75bf54ba Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Thu, 6 Jul 2023 19:21:08 +0100 Subject: [PATCH 092/116] Gardening [skip ci] --- Package@swift-5.7.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Package@swift-5.7.swift b/Package@swift-5.7.swift index 25ea09e1f..23c8c66cd 100644 --- a/Package@swift-5.7.swift +++ b/Package@swift-5.7.swift @@ -10,12 +10,12 @@ let package = Package( .macOS(.v10_15), ], products: [ - // legacy library + // old module .library(name: "Introspect", targets: ["Introspect"]), .library(name: "Introspect-Static", type: .static, targets: ["Introspect"]), .library(name: "Introspect-Dynamic", type: .dynamic, targets: ["Introspect"]), - // new experimental library + // new module .library(name: "SwiftUIIntrospect", targets: ["SwiftUIIntrospect"]), .library(name: "SwiftUIIntrospect-Static", type: .static, targets: ["SwiftUIIntrospect"]), .library(name: "SwiftUIIntrospect-Dynamic", type: .dynamic, targets: ["SwiftUIIntrospect"]), From 2b8e809a21e588cf27aefb1aef65f223e8572f45 Mon Sep 17 00:00:00 2001 From: Kenta Kubo <601636+kkk669@users.noreply.github.com> Date: Tue, 11 Jul 2023 08:53:20 +0900 Subject: [PATCH 093/116] Fix broken links in README.md (#303) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 466544925..747230c51 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ SwiftUI Introspect > **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. +> [`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. > From b400ef522405bc6cb8fa67fa18056ffe99e679a6 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Wed, 12 Jul 2023 09:12:16 +0100 Subject: [PATCH 094/116] Only box up content for `.view` introspection (#305) --- CHANGELOG.md | 2 ++ Sources/Introspect.swift | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37c4b866c..074f26365 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Changelog ## master +- Fixed: only box up content for `.view` introspection (#305) + ## [0.9.0] - Added: view controller introspection (#298) diff --git a/Sources/Introspect.swift b/Sources/Introspect.swift index 0379f59f9..272f0e899 100644 --- a/Sources/Introspect.swift +++ b/Sources/Introspect.swift @@ -70,10 +70,14 @@ struct IntrospectModifier Date: Wed, 12 Jul 2023 09:12:31 +0100 Subject: [PATCH 095/116] Change order of vended products (#306) --- Package@swift-5.7.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Package@swift-5.7.swift b/Package@swift-5.7.swift index 23c8c66cd..1d3b33d5b 100644 --- a/Package@swift-5.7.swift +++ b/Package@swift-5.7.swift @@ -10,15 +10,15 @@ let package = Package( .macOS(.v10_15), ], products: [ - // old module - .library(name: "Introspect", targets: ["Introspect"]), - .library(name: "Introspect-Static", type: .static, targets: ["Introspect"]), - .library(name: "Introspect-Dynamic", type: .dynamic, targets: ["Introspect"]), - // new module .library(name: "SwiftUIIntrospect", targets: ["SwiftUIIntrospect"]), .library(name: "SwiftUIIntrospect-Static", type: .static, targets: ["SwiftUIIntrospect"]), .library(name: "SwiftUIIntrospect-Dynamic", type: .dynamic, targets: ["SwiftUIIntrospect"]), + + // old module + .library(name: "Introspect", targets: ["Introspect"]), + .library(name: "Introspect-Static", type: .static, targets: ["Introspect"]), + .library(name: "Introspect-Dynamic", type: .dynamic, targets: ["Introspect"]), ], targets: [ .target( From 730ab9e6cdbb3122ad88277b295c4cecd284a311 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Wed, 12 Jul 2023 09:15:41 +0100 Subject: [PATCH 096/116] Bump to 0.9.1 (#307) --- CHANGELOG.md | 2 ++ README.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 074f26365..d18b93028 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Changelog ## master +## [0.9.1] + - Fixed: only box up content for `.view` introspection (#305) ## [0.9.0] diff --git a/README.md b/README.md index 747230c51..99fbb19a7 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Install ```swift let package = Package( dependencies: [ - .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.9.0"), + .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.9.1"), ], targets: [ .target(name: <#Target Name#>, dependencies: [ From 7165cadf7fc9e176c93f155169903142cc1eab74 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Wed, 12 Jul 2023 18:54:39 +0100 Subject: [PATCH 097/116] Disable "Autocreate schemes" (#308) --- .../xcshareddata/WorkspaceSettings.xcsettings | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 SwiftUIIntrospect.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/SwiftUIIntrospect.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/SwiftUIIntrospect.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..08de0be8d --- /dev/null +++ b/SwiftUIIntrospect.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded + + + From 3e52295edab4b2b1f1c9180fbf1c7b4e6a8ada30 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 18 Jul 2023 17:47:06 +0100 Subject: [PATCH 098/116] Fix occasionally wrong status bar style (#313) --- Sources/IntrospectionView.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Sources/IntrospectionView.swift b/Sources/IntrospectionView.swift index 9ad399764..fc2304756 100644 --- a/Sources/IntrospectionView.swift +++ b/Sources/IntrospectionView.swift @@ -169,6 +169,12 @@ final class IntrospectionPlatformViewController: PlatformViewController { } #if canImport(UIKit) + #if os(iOS) + override var preferredStatusBarStyle: UIStatusBarStyle { + parent?.preferredStatusBarStyle ?? super.preferredStatusBarStyle + } + #endif + override func viewDidLoad() { super.viewDidLoad() view.introspectionController = self From cf02def6b56c56828e6c34edbf8905cb213f8450 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Wed, 19 Jul 2023 21:25:52 +0100 Subject: [PATCH 099/116] Add UI Test suite (#314) --- .github/workflows/ci.yml | 8 +- .../xcshareddata/swiftpm/Package.resolved | 23 + Tests/Tests.xcodeproj/project.pbxproj | 602 +++++++++++++++++- .../SwiftUIIntrospectUITests.xcscheme | 82 +++ Tests/UITests/StatusBarStyleUITests.swift | 30 + Tests/UITests/UITestCase.swift | 21 + Tests/UITests/UITests.xctestplan | 24 + .../test.ipad-ios-14-screenshot-1.png | Bin 0 -> 64307 bytes .../test.ipad-ios-15-screenshot-1.png | Bin 0 -> 76109 bytes .../test.ipad-ios-16-screenshot-1.png | Bin 0 -> 77439 bytes .../test.iphone-ios-14-screenshot-1.png | Bin 0 -> 66635 bytes .../test.iphone-ios-15-screenshot-1.png | Bin 0 -> 31691 bytes .../test.iphone-ios-16-screenshot-1.png | Bin 0 -> 68961 bytes .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 13 + .../Assets.xcassets/Contents.json | 6 + Tests/UITestsHostApp/Info.plist | 8 + .../Preview Assets.xcassets/Contents.json | 6 + .../StatusBarStyle/HostingController.swift | 32 + .../StatusBarStyle/NavigationView.swift | 44 ++ .../StatusBarStyle/RootView.swift | 52 ++ Tests/UITestsHostApp/TestCases.swift | 3 + Tests/UITestsHostApp/UITestsHostApp.swift | 28 + fastlane/Fastfile | 5 + 24 files changed, 981 insertions(+), 17 deletions(-) create mode 100644 SwiftUIIntrospect.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 Tests/Tests.xcodeproj/xcshareddata/xcschemes/SwiftUIIntrospectUITests.xcscheme create mode 100644 Tests/UITests/StatusBarStyleUITests.swift create mode 100644 Tests/UITests/UITestCase.swift create mode 100644 Tests/UITests/UITests.xctestplan create mode 100644 Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.ipad-ios-14-screenshot-1.png create mode 100644 Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.ipad-ios-15-screenshot-1.png create mode 100644 Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.ipad-ios-16-screenshot-1.png create mode 100644 Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.iphone-ios-14-screenshot-1.png create mode 100644 Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.iphone-ios-15-screenshot-1.png create mode 100644 Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.iphone-ios-16-screenshot-1.png create mode 100644 Tests/UITestsHostApp/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Tests/UITestsHostApp/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Tests/UITestsHostApp/Assets.xcassets/Contents.json create mode 100644 Tests/UITestsHostApp/Info.plist create mode 100644 Tests/UITestsHostApp/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 Tests/UITestsHostApp/StatusBarStyle/HostingController.swift create mode 100644 Tests/UITestsHostApp/StatusBarStyle/NavigationView.swift create mode 100644 Tests/UITestsHostApp/StatusBarStyle/RootView.swift create mode 100644 Tests/UITestsHostApp/TestCases.swift create mode 100644 Tests/UITestsHostApp/UITestsHostApp.swift diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fccaa0da4..7df222e6b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -132,9 +132,9 @@ jobs: run: fastlane test platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }} scheme:Introspect - if: ${{ join(matrix.platform, ' ') != 'macos 11' }} - name: Run Tests (SwiftUIIntrospect, Debug) + name: Run Tests (SwiftUIIntrospect) 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 + - if: ${{ matrix.platform[0] == 'ios' && matrix.platform[1] >= '14' && matrix.platform[1] <= '16' }} + name: Run UI Tests (SwiftUIIntrospect) + run: fastlane test platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }} scheme:SwiftUIIntrospectUITests configuration:Debug diff --git a/SwiftUIIntrospect.xcworkspace/xcshareddata/swiftpm/Package.resolved b/SwiftUIIntrospect.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 000000000..5d7d67f23 --- /dev/null +++ b/SwiftUIIntrospect.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,23 @@ +{ + "pins" : [ + { + "identity" : "simulatorstatusmagic", + "kind" : "remoteSourceControl", + "location" : "https://github.com/shinydevelopment/SimulatorStatusMagic", + "state" : { + "revision" : "76095ec820674457a11dc3cc621d8a5c369eea0e", + "version" : "2.7.0" + } + }, + { + "identity" : "swift-snapshot-testing", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-snapshot-testing.git", + "state" : { + "revision" : "dc46eeb3928a75390651fac6c1ef7f93ad59a73b", + "version" : "1.11.1" + } + } + ], + "version" : 2 +} diff --git a/Tests/Tests.xcodeproj/project.pbxproj b/Tests/Tests.xcodeproj/project.pbxproj index 3d8327acc..a0f992898 100644 --- a/Tests/Tests.xcodeproj/project.pbxproj +++ b/Tests/Tests.xcodeproj/project.pbxproj @@ -97,6 +97,14 @@ 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 */; }; + D58D832E2A66BDD500A203BE /* StatusBarStyleUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58D832D2A66BDD500A203BE /* StatusBarStyleUITests.swift */; }; + D58D83382A66C01300A203BE /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = D58D83372A66C01300A203BE /* SnapshotTesting */; }; + D58D833A2A66C04600A203BE /* SwiftUIIntrospect in Frameworks */ = {isa = PBXBuildFile; productRef = D58D83392A66C04600A203BE /* SwiftUIIntrospect */; }; + D58D83422A66C5EC00A203BE /* UITestsHostApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58D83412A66C5EC00A203BE /* UITestsHostApp.swift */; }; + D58D83462A66C5EF00A203BE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D58D83452A66C5EF00A203BE /* Assets.xcassets */; }; + D58D83492A66C5EF00A203BE /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D58D83482A66C5EF00A203BE /* Preview Assets.xcassets */; }; + D58D83502A66C67A00A203BE /* TestCases.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58D834F2A66C67A00A203BE /* TestCases.swift */; }; + D5983E7D2A66FD3F00C50953 /* TestCases.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58D834F2A66C67A00A203BE /* TestCases.swift */; }; D5AAF56F2A502EF000CAFFB6 /* MapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AAF56E2A502EF000CAFFB6 /* MapTests.swift */; }; D5AD0D912A114B98003D8DEC /* TextFieldWithVerticalAxisTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AD0D902A114B98003D8DEC /* TextFieldWithVerticalAxisTests.swift */; }; D5ADFACC2A4A22AE009494FD /* SheetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ADFACB2A4A22AE009494FD /* SheetTests.swift */; }; @@ -108,9 +116,15 @@ D5ADFAD52A4A4653009494FD /* SheetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ADFACB2A4A22AE009494FD /* SheetTests.swift */; }; D5ADFAD62A4A4653009494FD /* VideoPlayerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D503B2AB2A49BFE300027F5F /* VideoPlayerTests.swift */; }; D5ADFAD72A4A4653009494FD /* PopoverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5ADFACF2A4A3E54009494FD /* PopoverTests.swift */; }; + D5AEC33F2A66F31F0015AC1D /* UITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AEC33E2A66F31F0015AC1D /* UITestCase.swift */; }; + D5AEC3442A66F6470015AC1D /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AEC3412A66F6470015AC1D /* RootView.swift */; }; + D5AEC3452A66F6470015AC1D /* HostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AEC3422A66F6470015AC1D /* HostingController.swift */; }; + D5AEC3462A66F6470015AC1D /* NavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AEC3432A66F6470015AC1D /* NavigationView.swift */; }; + D5AEC3482A66F6AA0015AC1D /* SwiftUIIntrospect in Frameworks */ = {isa = PBXBuildFile; productRef = D5AEC3472A66F6AA0015AC1D /* SwiftUIIntrospect */; }; D5B67B842A0D318F007D5D9B /* TextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B67B832A0D318F007D5D9B /* TextFieldTests.swift */; }; D5F0BE4D29C0DBE800AD95AB /* TestsHostApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F0BE4C29C0DBE800AD95AB /* TestsHostApp.swift */; }; D5F0BE6A29C0DC4900AD95AB /* PlatformVersionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F0BE6729C0DC4900AD95AB /* PlatformVersionTests.swift */; }; + D5F210812A66DDCB002B8385 /* SimulatorStatusMagic in Frameworks */ = {isa = PBXBuildFile; productRef = D5F210802A66DDCB002B8385 /* SimulatorStatusMagic */; }; D5F26E022A561130001209E6 /* PageControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F26E012A561130001209E6 /* PageControlTests.swift */; }; D5F26E042A56E74B001209E6 /* ViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F26E032A56E74B001209E6 /* ViewControllerTests.swift */; }; D5F8D5ED2A1E7B490054E9AB /* NavigationViewWithStackStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F8D5EC2A1E7B490054E9AB /* NavigationViewWithStackStyleTests.swift */; }; @@ -118,19 +132,19 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - D50E2F5B2A2B9F6600BAFB03 /* PBXContainerItemProxy */ = { + D50E2F912A2B9FDE00BAFB03 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D5F0BE3F29C0DB9700AD95AB /* Project object */; proxyType = 1; - remoteGlobalIDString = D5F0BE4829C0DBE800AD95AB; - remoteInfo = TestsHostApp; + remoteGlobalIDString = D50E2F4D2A2B9DEE00BAFB03; + remoteInfo = LegacyTestsHostApp; }; - D50E2F912A2B9FDE00BAFB03 /* PBXContainerItemProxy */ = { + D58D834D2A66C61700A203BE /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D5F0BE3F29C0DB9700AD95AB /* Project object */; proxyType = 1; - remoteGlobalIDString = D50E2F4D2A2B9DEE00BAFB03; - remoteInfo = LegacyTestsHostApp; + remoteGlobalIDString = D58D833E2A66C5EC00A203BE; + remoteInfo = UITestsHostApp; }; D5F0BE6129C0DC0000AD95AB /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -148,6 +162,7 @@ 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 = ""; }; D534D4DB2A4A596200218BFB /* WindowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowTests.swift; sourceTree = ""; }; + D549D9732A66D876005D4FB5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; D55F448C2A1FF209003381E4 /* ListTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTests.swift; sourceTree = ""; }; D568532B2A49DBB10039A99F /* SignInWithAppleButtonTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInWithAppleButtonTests.swift; sourceTree = ""; }; D57506772A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickerWithSegmentedStyleTests.swift; sourceTree = ""; }; @@ -187,12 +202,24 @@ 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 = ""; }; + D58D832B2A66BDD500A203BE /* UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + D58D832D2A66BDD500A203BE /* StatusBarStyleUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBarStyleUITests.swift; sourceTree = ""; }; + D58D833F2A66C5EC00A203BE /* UITestsHostApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UITestsHostApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; + D58D83412A66C5EC00A203BE /* UITestsHostApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestsHostApp.swift; sourceTree = ""; }; + D58D83452A66C5EF00A203BE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + D58D83482A66C5EF00A203BE /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + D58D834F2A66C67A00A203BE /* TestCases.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestCases.swift; sourceTree = ""; }; D5AAF56E2A502EF000CAFFB6 /* MapTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTests.swift; sourceTree = ""; }; D5AD0D902A114B98003D8DEC /* TextFieldWithVerticalAxisTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldWithVerticalAxisTests.swift; sourceTree = ""; }; D5ADFACB2A4A22AE009494FD /* SheetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SheetTests.swift; sourceTree = ""; }; D5ADFACD2A4A3482009494FD /* FullScreenCoverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenCoverTests.swift; sourceTree = ""; }; D5ADFACF2A4A3E54009494FD /* PopoverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoverTests.swift; sourceTree = ""; }; D5ADFAD12A4A41CB009494FD /* SignInWithAppleButtonTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignInWithAppleButtonTests.swift; sourceTree = ""; }; + D5AEC33E2A66F31F0015AC1D /* UITestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestCase.swift; sourceTree = ""; }; + D5AEC3412A66F6470015AC1D /* RootView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = ""; }; + D5AEC3422A66F6470015AC1D /* HostingController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HostingController.swift; sourceTree = ""; }; + D5AEC3432A66F6470015AC1D /* NavigationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationView.swift; sourceTree = ""; }; + D5B5B03E2A6725500086B9DE /* UITests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = UITests.xctestplan; path = "/Users/davdroman/Developer/davdroman/swiftui-introspect/Tests/UITests/UITests.xctestplan"; 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 = ""; }; @@ -220,6 +247,24 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D58D83282A66BDD500A203BE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D5F210812A66DDCB002B8385 /* SimulatorStatusMagic in Frameworks */, + D58D83382A66C01300A203BE /* SnapshotTesting in Frameworks */, + D58D833A2A66C04600A203BE /* SwiftUIIntrospect in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D58D833C2A66C5EC00A203BE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D5AEC3482A66F6AA0015AC1D /* SwiftUIIntrospect in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; D5F0BE4629C0DBE800AD95AB /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -246,6 +291,63 @@ path = LegacyTestsHostApp; sourceTree = ""; }; + D5459AA32A67072F00F0D737 /* __Snapshots__ */ = { + isa = PBXGroup; + children = ( + D5459AA42A67072F00F0D737 /* StatusBarStyleUITests */, + ); + path = __Snapshots__; + sourceTree = ""; + }; + D5459AA42A67072F00F0D737 /* StatusBarStyleUITests */ = { + isa = PBXGroup; + children = ( + ); + path = StatusBarStyleUITests; + sourceTree = ""; + }; + D58D832C2A66BDD500A203BE /* UITests */ = { + isa = PBXGroup; + children = ( + D5B5B03E2A6725500086B9DE /* UITests.xctestplan */, + D58D832D2A66BDD500A203BE /* StatusBarStyleUITests.swift */, + D5AEC33E2A66F31F0015AC1D /* UITestCase.swift */, + D5459AA32A67072F00F0D737 /* __Snapshots__ */, + ); + path = UITests; + sourceTree = ""; + }; + D58D83402A66C5EC00A203BE /* UITestsHostApp */ = { + isa = PBXGroup; + children = ( + D549D9732A66D876005D4FB5 /* Info.plist */, + D58D83412A66C5EC00A203BE /* UITestsHostApp.swift */, + D58D834F2A66C67A00A203BE /* TestCases.swift */, + D5AEC3402A66F6210015AC1D /* StatusBarStyle */, + D58D83452A66C5EF00A203BE /* Assets.xcassets */, + D58D83472A66C5EF00A203BE /* Preview Content */, + ); + path = UITestsHostApp; + sourceTree = ""; + }; + D58D83472A66C5EF00A203BE /* Preview Content */ = { + isa = PBXGroup; + children = ( + D58D83482A66C5EF00A203BE /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + D5AEC3402A66F6210015AC1D /* StatusBarStyle */ = { + isa = PBXGroup; + children = ( + D5AEC3422A66F6470015AC1D /* HostingController.swift */, + D5AEC3432A66F6470015AC1D /* NavigationView.swift */, + D5AEC3412A66F6470015AC1D /* RootView.swift */, + ); + path = StatusBarStyle; + sourceTree = ""; + }; D5B67B852A0D3193007D5D9B /* ViewTypes */ = { isa = PBXGroup; children = ( @@ -311,6 +413,8 @@ D5F0BE4B29C0DBE800AD95AB /* TestsHostApp */, D50E2F562A2B9EB600BAFB03 /* LegacyTestsHostApp */, D5F0BE5E29C0DC0000AD95AB /* Tests */, + D58D83402A66C5EC00A203BE /* UITestsHostApp */, + D58D832C2A66BDD500A203BE /* UITests */, D5F0BE4A29C0DBE800AD95AB /* Products */, D5F0BE7029C0E12300AD95AB /* Frameworks */, ); @@ -323,6 +427,8 @@ D5F0BE5D29C0DC0000AD95AB /* Tests.xctest */, D50E2F552A2B9DEE00BAFB03 /* LegacyTestsHostApp.app */, D50E2F902A2B9F6600BAFB03 /* LegacyTests.xctest */, + D58D832B2A66BDD500A203BE /* UITests.xctest */, + D58D833F2A66C5EC00A203BE /* UITestsHostApp.app */, ); name = Products; sourceTree = ""; @@ -383,7 +489,6 @@ buildRules = ( ); dependencies = ( - D50E2F5A2A2B9F6600BAFB03 /* PBXTargetDependency */, D50E2F922A2B9FDE00BAFB03 /* PBXTargetDependency */, ); name = LegacyTests; @@ -394,6 +499,49 @@ productReference = D50E2F902A2B9F6600BAFB03 /* LegacyTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + D58D832A2A66BDD500A203BE /* UITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = D58D83352A66BDD500A203BE /* Build configuration list for PBXNativeTarget "UITests" */; + buildPhases = ( + D58D83272A66BDD500A203BE /* Sources */, + D58D83282A66BDD500A203BE /* Frameworks */, + D58D83292A66BDD500A203BE /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + D58D834E2A66C61700A203BE /* PBXTargetDependency */, + ); + name = UITests; + packageProductDependencies = ( + D58D83372A66C01300A203BE /* SnapshotTesting */, + D58D83392A66C04600A203BE /* SwiftUIIntrospect */, + D5F210802A66DDCB002B8385 /* SimulatorStatusMagic */, + ); + productName = UITests; + productReference = D58D832B2A66BDD500A203BE /* UITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; + D58D833E2A66C5EC00A203BE /* UITestsHostApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = D58D834C2A66C5EF00A203BE /* Build configuration list for PBXNativeTarget "UITestsHostApp" */; + buildPhases = ( + D58D833B2A66C5EC00A203BE /* Sources */, + D58D833C2A66C5EC00A203BE /* Frameworks */, + D58D833D2A66C5EC00A203BE /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = UITestsHostApp; + packageProductDependencies = ( + D5AEC3472A66F6AA0015AC1D /* SwiftUIIntrospect */, + ); + productName = UITestsHostApp; + productReference = D58D833F2A66C5EC00A203BE /* UITestsHostApp.app */; + productType = "com.apple.product-type.application"; + }; D5F0BE4829C0DBE800AD95AB /* TestsHostApp */ = { isa = PBXNativeTarget; buildConfigurationList = D5F0BE5829C0DBE900AD95AB /* Build configuration list for PBXNativeTarget "TestsHostApp" */; @@ -439,12 +587,19 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1420; + LastSwiftUpdateCheck = 1500; LastUpgradeCheck = 1420; TargetAttributes = { D50E2F592A2B9F6600BAFB03 = { TestTargetID = D50E2F4D2A2B9DEE00BAFB03; }; + D58D832A2A66BDD500A203BE = { + CreatedOnToolsVersion = 15.0; + TestTargetID = D58D833E2A66C5EC00A203BE; + }; + D58D833E2A66C5EC00A203BE = { + CreatedOnToolsVersion = 15.0; + }; D5F0BE4829C0DBE800AD95AB = { CreatedOnToolsVersion = 14.2; }; @@ -464,6 +619,10 @@ Base, ); mainGroup = D5F0BE3E29C0DB9700AD95AB; + packageReferences = ( + D58D83362A66C01300A203BE /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */, + D5F2107D2A66DC85002B8385 /* XCRemoteSwiftPackageReference "SimulatorStatusMagic" */, + ); productRefGroup = D5F0BE4A29C0DBE800AD95AB /* Products */; projectDirPath = ""; projectRoot = ""; @@ -472,6 +631,8 @@ D5F0BE5C29C0DC0000AD95AB /* Tests */, D50E2F4D2A2B9DEE00BAFB03 /* LegacyTestsHostApp */, D50E2F592A2B9F6600BAFB03 /* LegacyTests */, + D58D833E2A66C5EC00A203BE /* UITestsHostApp */, + D58D832A2A66BDD500A203BE /* UITests */, ); }; /* End PBXProject section */ @@ -491,6 +652,22 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D58D83292A66BDD500A203BE /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D58D833D2A66C5EC00A203BE /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D58D83492A66C5EF00A203BE /* Preview Assets.xcassets in Resources */, + D58D83462A66C5EF00A203BE /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; D5F0BE4729C0DBE800AD95AB /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -573,6 +750,28 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D58D83272A66BDD500A203BE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D58D832E2A66BDD500A203BE /* StatusBarStyleUITests.swift in Sources */, + D5AEC33F2A66F31F0015AC1D /* UITestCase.swift in Sources */, + D5983E7D2A66FD3F00C50953 /* TestCases.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D58D833B2A66C5EC00A203BE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D58D83422A66C5EC00A203BE /* UITestsHostApp.swift in Sources */, + D58D83502A66C67A00A203BE /* TestCases.swift in Sources */, + D5AEC3462A66F6470015AC1D /* NavigationView.swift in Sources */, + D5AEC3452A66F6470015AC1D /* HostingController.swift in Sources */, + D5AEC3442A66F6470015AC1D /* RootView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; D5F0BE4529C0DBE800AD95AB /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -645,16 +844,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 */; }; + D58D834E2A66C61700A203BE /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D58D833E2A66C5EC00A203BE /* UITestsHostApp */; + targetProxy = D58D834D2A66C61700A203BE /* PBXContainerItemProxy */; + }; D5F0BE6229C0DC0000AD95AB /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = D5F0BE4829C0DBE800AD95AB /* TestsHostApp */; @@ -981,6 +1180,328 @@ }; name = Release; }; + D58D83332A66BDD500A203BE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + 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_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = ""; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + 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; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.siteline.UITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = UITestsHostApp; + TVOS_DEPLOYMENT_TARGET = 14.0; + }; + name = Debug; + }; + D58D83342A66BDD500A203BE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + 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_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = ""; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + 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; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.siteline.UITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = UITestsHostApp; + TVOS_DEPLOYMENT_TARGET = 14.0; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + D58D834A2A66C5EF00A203BE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + 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_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_ASSET_PATHS = "\"UITestsHostApp/Preview Content\""; + ENABLE_PREVIEWS = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + 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_FILE = UITestsHostApp/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.siteline.UITestsHostApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + D58D834B2A66C5EF00A203BE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + 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_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_ASSET_PATHS = "\"UITestsHostApp/Preview Content\""; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_PREVIEWS = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + 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_FILE = UITestsHostApp/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.siteline.UITestsHostApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; D5F0BE4329C0DB9700AD95AB /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1346,6 +1867,24 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + D58D83352A66BDD500A203BE /* Build configuration list for PBXNativeTarget "UITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D58D83332A66BDD500A203BE /* Debug */, + D58D83342A66BDD500A203BE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D58D834C2A66C5EF00A203BE /* Build configuration list for PBXNativeTarget "UITestsHostApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D58D834A2A66C5EF00A203BE /* Debug */, + D58D834B2A66C5EF00A203BE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; D5F0BE4229C0DB9700AD95AB /* Build configuration list for PBXProject "Tests" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -1375,6 +1914,25 @@ }; /* End XCConfigurationList section */ +/* Begin XCRemoteSwiftPackageReference section */ + D58D83362A66C01300A203BE /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/pointfreeco/swift-snapshot-testing.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.11.1; + }; + }; + D5F2107D2A66DC85002B8385 /* XCRemoteSwiftPackageReference "SimulatorStatusMagic" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/shinydevelopment/SimulatorStatusMagic"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.7.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + /* Begin XCSwiftPackageProductDependency section */ D50E2F5C2A2B9F6600BAFB03 /* SwiftUIIntrospect */ = { isa = XCSwiftPackageProductDependency; @@ -1384,6 +1942,24 @@ isa = XCSwiftPackageProductDependency; productName = SwiftUIIntrospect; }; + D58D83372A66C01300A203BE /* SnapshotTesting */ = { + isa = XCSwiftPackageProductDependency; + package = D58D83362A66C01300A203BE /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */; + productName = SnapshotTesting; + }; + D58D83392A66C04600A203BE /* SwiftUIIntrospect */ = { + isa = XCSwiftPackageProductDependency; + productName = SwiftUIIntrospect; + }; + D5AEC3472A66F6AA0015AC1D /* SwiftUIIntrospect */ = { + isa = XCSwiftPackageProductDependency; + productName = SwiftUIIntrospect; + }; + D5F210802A66DDCB002B8385 /* SimulatorStatusMagic */ = { + isa = XCSwiftPackageProductDependency; + package = D5F2107D2A66DC85002B8385 /* XCRemoteSwiftPackageReference "SimulatorStatusMagic" */; + productName = SimulatorStatusMagic; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = D5F0BE3F29C0DB9700AD95AB /* Project object */; diff --git a/Tests/Tests.xcodeproj/xcshareddata/xcschemes/SwiftUIIntrospectUITests.xcscheme b/Tests/Tests.xcodeproj/xcshareddata/xcschemes/SwiftUIIntrospectUITests.xcscheme new file mode 100644 index 000000000..2745a9788 --- /dev/null +++ b/Tests/Tests.xcodeproj/xcshareddata/xcschemes/SwiftUIIntrospectUITests.xcscheme @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/UITests/StatusBarStyleUITests.swift b/Tests/UITests/StatusBarStyleUITests.swift new file mode 100644 index 000000000..0404986da --- /dev/null +++ b/Tests/UITests/StatusBarStyleUITests.swift @@ -0,0 +1,30 @@ +import SnapshotTesting +import XCTest + +final class StatusBarStyleUITests: UITestCase { + override var testCase: TestCase { + .statusBarStyle + } + + func test() throws { + guard #unavailable(iOS 17) else { + throw XCTSkip("SimulatorStatusMagic stopped working in iOS 17, so we can no longer consistently compare status bar screenshots") + } + + app.buttons["Navigate To Detail"].tap() + app.buttons["Navigate To Detail"].tap() + app.buttons["Navigate Back"].tap() + + let iOSDevice = UIDevice.current.userInterfaceIdiom == .pad ? "ipad" : "iphone" + let iOSVersion = ProcessInfo().operatingSystemVersion + func screenshotName(_ number: Int) -> String { + "\(iOSDevice)-ios-\(iOSVersion.majorVersion)-screenshot-\(number)" + } + + assertSnapshot( + matching: app.windows.firstMatch.screenshot().image, + as: .image(perceptualPrecision: 0.95), + named: screenshotName(1) + ) + } +} diff --git a/Tests/UITests/UITestCase.swift b/Tests/UITests/UITestCase.swift new file mode 100644 index 000000000..d99d199b1 --- /dev/null +++ b/Tests/UITests/UITestCase.swift @@ -0,0 +1,21 @@ +import SimulatorStatusMagic +import XCTest + +class UITestCase: XCTestCase { + var testCase: TestCase { + preconditionFailure("Please override this property") + } + + let app = XCUIApplication() + + override func invokeTest() { + SDStatusBarManager.sharedInstance().enableOverrides() + + continueAfterFailure = false + + app.launchEnvironment["testCase"] = testCase.rawValue + app.launch() + + super.invokeTest() + } +} diff --git a/Tests/UITests/UITests.xctestplan b/Tests/UITests/UITests.xctestplan new file mode 100644 index 000000000..403bcc61a --- /dev/null +++ b/Tests/UITests/UITests.xctestplan @@ -0,0 +1,24 @@ +{ + "configurations" : [ + { + "id" : "DD0EEECD-4762-4A68-91A8-F7B5A2209B45", + "name" : "Test Scheme Action", + "options" : { + + } + } + ], + "defaultOptions" : { + + }, + "testTargets" : [ + { + "target" : { + "containerPath" : "container:Tests.xcodeproj", + "identifier" : "D58D832A2A66BDD500A203BE", + "name" : "UITests" + } + } + ], + "version" : 1 +} diff --git a/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.ipad-ios-14-screenshot-1.png b/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.ipad-ios-14-screenshot-1.png new file mode 100644 index 0000000000000000000000000000000000000000..f008882b2f4f0ec2e931b56c45ee39349c2675e9 GIT binary patch literal 64307 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ⅆEtWz8^?Li*`j1_I6jF`6q6pkwZROrwNle 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 literal 0 HcmV?d00001 diff --git a/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.iphone-ios-14-screenshot-1.png b/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.iphone-ios-14-screenshot-1.png new file mode 100644 index 0000000000000000000000000000000000000000..74dfd54e481e020527b57054c42dc442bc201521 GIT binary patch literal 66635 zcmeFadpwiM z`)FX_OR0a3mVWYb)j!+o1{Y5=4RyB%-qzi@VC%nP#a`XT*UIzB#~p#4yFJfYEsl0^ z;J}acH1N-%rT4|{@FWuq*n7o_(tob+t=@*+x9gi47{8>ir z;R~1OXo8V8yGHx`0jf4WBZ8pd9RVRtMw}^ahRi3fSScmFcJrRocNaIDSEDy0T{p%3 z&(6P2V?s)j{=KVBszf8y@aBg9p15}NV^~T2|Noe|^~=XBS!LNi|Mjya|5^5prJ(zN z7(0n(ksijCAwT!Fq(i!^nPtPPJU}C;3O#mTTeLnn>w{M3>L3nsf^p$A#wg+8q(}Nc z5xvK{f3?ghwq7ZJq(}!Le9fQ9N++uXMpoI_R}@~g?)!3|B5G=h-2`(8zvMr?u4N1k zYYhX>6kba&we+Tiht9CHb;gM8W-^zfuE<=Ax-N4w%36lMGX_k?>?H?{id!x=f<`jP zVZ(AQ=d-+8!{Pp4FSdwr@KMDsP4_kMUFcA;&SPtq|nM%YJ$AjSS^ zR$*n8+z~mRt`#O8@~Y4QbQM%yIAJAezOwJ;F4!$}!SM~u%ZY{+xwQ_~$r~$u$rv;l zGjc5*LBR8A+*-jCGiA?+DbaA*Q_xR?vA{+h5bc1!E&8sPFh3A#9S9h2}Y6p zm>n2(?iX0D-JELfSVitVT{SpRS!gWL(&4I;%e&(mySG|)Z|rJmMg1`YDM452b1F3C zpbRJ5C_FbS=qZeu3~Q|ZSSi9(M$ARb)((%Vb`9ltPmSDR+owT9ex0lG|4^*%>Q`gF z7&Q^mYb@}ba_@?yPvE(W|5(9&-5)}~Qqi?${WnMQvxLEvc-B;O`6PnCkkF~usq=me zSYl3`*ye-xD=b4qr)YiyUsra2 zf^+JDARQXE&^AwNxXYl|6+JSMfP5*^=>d znUmp{Aa4@JjaSq2ZyyL2r1pVqtt_MC=HtRV|`(3~J5^bNZsF`Ae5+j0&Rg3Pw zr{l(5&~*zUc3KhYtsd7RySu1JUYRY@bIvTV>Pi`x3M{Ug+GNseP_SyJ){9d{kGrd( zU`!zPa!azLO$nBHyR=mKOPX@?7*uf?5dojX(bF zpX{@k_+|r(-@d33Nz>9{c*qCuDbFSH&>TvtF7<&OejP=P1Rp;&*)`j%9Qd>9FZ<#` zT0{Uy*B6wC&aHia&$fSW2jyis`I;8D&B16c1j8}2mmw>|l!A~#?&%%D^9t5UF!L6S zxt4G!*c7>CQFui*7SssY4PBBJ_X*)G?k1XtBgZHRnf;WTlQ@#0 zx6qzfPMe<}VvYx@23{*Q@!Ej;UfLCA#$D3|o15U4!MZcUwe%pc=<$yfMv=^k1k78@ zX3Sf63QFWUfGcy@EwRN-^rvV4okvM1vo0K=&xM#uZrwAv&+XOqkHF%2nQ}5WC65Zx zM)^$5WEgkbK#t~4e4Q7PT>@)8T(NUWAbyONlDER&3lcEfW^u(9!rqf1?gfoRQhWLW z-g_ZC+;Rd-^faDFuZEO5PF)xuk{w^ebZ5Fud1Ccb_1}DAw$8iqoa4B$-pJxHvuFGl zbGMlvDbReG6AUMJ)J2TODGCdZ7%-Tt@*Y`eou8IhE4<-h8EPSkv5v?_y{IbEqldpn zA7U!m7}a-2Kr|o25Ph^+ngtl+CV9*wrtH@EHO%0!^|PpyT?X`33WC2RuIwi>83iey z=4+`UO70BT;V-UF)lA>#TUysZ2J@yCfjc);A8d4-`NI(X#VRnMeTW?H8JqDK*4&H< zjrqXiJl7&|vvo6#vc$oXn5YR*abE5SZW#517gJ=hyPJqY4mEVA!MHLR?GnL>aH05u zU#w7A6sD%@_zS70Qsqk>yP|svOhB0hXKYOP#NJqg*mI=I80d&MfGv-xvDP(Ts+ww|aB|OnZJ%e}tOM@jZc(2~oiA&#Qas^?H zxP>qhH>cc%44%<-$U~yG(=amWa2$8*FqmKXc`QkvOgEz!V>Mf zsdz`!;)DxTkQfsr;^$d0orSuj5fd?wf$bGe^{&29zwA7BCSVGYnm(z{|*w;cAz94ZqNj`EC^;>${T4rAUP~XsnRs@3x9ACO6mptL1o zPkT%0I~Pjb21KIw!rlNf=DdT$)x`iE5C8VgWSqNSrnx{|?1}%}=%pevQCv!%YeA(~ zOn&M%OM+2lOXYrF^v!KHi3peU`R!iQ-~6JDg@oasQEF_*98xEsjWl_>Iq4obvi|pc zUizm-thLA3PbD4j4@6XT$d$ly$u~a+)e#4T4#j1J-*$dLF+p@8PUGHm3L;1k;L6lyT@ zwu*B=c|2g|Qr;k%x%r*S-%#){_JR!lfu$j`DB5~lMWR`VHLf0O7?z=XM~Lz{Rv>z1 z75}@QTvo2OKS7Q1@{%ah4R1U#Z-%GN^&fDf?1{D7x)dSc^NvD!G;NG_b~HRtIPp1U z!rs@ygFZEWrL^UCsf^0)Lj1ejlq2tn0|sOBZK%N4tzBwNb8X{s9cDv!z_C&-L#m-R z%V^x3%1B$Z2w%0yEutu306b8~OrU-u{g_Ai<8Kd&aUh6LI6smm5SLvGRS|rzE1*ti zzZ38Ae{6}_STeu=H(uY6J{U3f!mu5=_9-Mo7dv$TX>G?Ohbq)Aq-tVApX0E2-!AX= z?&hxU)$bsG6ybBL$mPmS6U|m2k~C!M1nAE7Y{J_B82L%vH4OKjsc`s|vwT-GmaR za`)`n+oh90vSJ`PbC-($FtUz}F(%*Crl7EGIM+TTX$tFaR384b@g~7M!oRz&-lsBz zlZA!b8T($d4uf@BG-0^Hv9ts1h;_e89g%O>UJ}x@L*5|-|2578l$T(^E%HQ{^3qVK z_xT;$DT!6N^$AW5<;t~itf&I7Ox`bxV9 z+iU8NBOosPl5C%Uzf|D!P-mEskz|&3 zkEcLzMGD%r(LWRrDg1@E*u{P6(W1!vnk3bSAIe@W{mY35=3^N~vcH@4;2h8)eKrH8Vlc zJWk;h$%`?TSqejsbxDqKjo(T3@ojM@L6VWLZ&?JKvdLaB7Y!sB~H`SPp`$=;*PCs7mK89~#x*$^!zL~VSc#3!Vc zlIyXlw_gD@mY{I%3S@kVtHyr@Wc6KDa|NHql%v`W#TpK5NtZyUYEY$-XsjRJsI|u7 zwyQp6!5Z8gLhefcyY#msHLU$M|KKfRMLM#H_UR}GD)JlvTSCVl3<0q8l>4@-W-Hrg z&cXsjn!Vx{@h;r%-jJSP?@0KKU*Ls&&7*HJb4VdF=Azj%`Uc?z6nJ{YCFJmzkvXly zly_rc50{9f$4_b%kq4DrXBR^yvo#X>UGq7Xv#kd&By?snA7k{IMmG)gU;=8_jmnsy z=tE7E_@vF7HyMG z_)BZDmsvgfT?}Bz$xPDk3LMz-rG(km!>73{>X*Sab|nb;W_>XG+f5bwS6r2zIjzq6 zDs3QlqFpWpf$RPLT;^PX}_@nA*CRrc)f;UUKkWJi_MLi1xan93gXR&NK42C~Y8 znk zqpu1^Wzwxx+B?k0yS;hc(V^ydWmG00Q!F}&8q2(9Y_!Lrjit;eIVJ0t1rnD2L@vzix#3 z?rw!|tobS9(*&eJ2_lCfKI8@;0Eh~tYnUA_Wj?yXqj_069^yNe8qEIv+GFM+va zP&fRZjDwL1L<6pmhrbrtAaw{$oyl$C6{!S=ahn!?__4jCus7ibeaTk%yumgz=qS^M zHPv$ycN*X0uH?l#k*X+3pYxKzd>?V6=(~bNPfG!`oYK}|T;tK{)#B%hB8D~ncnR#4 zO1eerGsga3wW>sQ;44pYckA$;;P2Tkknsr5i1~ZbQ8pgTr{=_LC7PpArFBsmc2W}UP#gz(N5x6t(Sm1>~$G~8?k?+u5Wp_*D ztwv#F%qH?p)m(Nu$)U$^_Iq6-*~=qBf^#nwhrfYRDzmaQM78YxUnqXMu%=#qql9)+ z^fv`SgpKp(owGbXR3pK77jP!^u7Of@>=7(WB|1=qIbhl01Rtb7+cZ8otL8>oa0MHr z%J1$7W9Hd%g1R$XqFTxKb1HFcMRL~_-9MMyY;f&v4OFJ8mhtDtU>{5R%{)gk4lilmn)~15?h(eR~04PEXPF zDHL}Pzf&}mRFyfj5FK6(wiLmG=rt(ws&=}1y3j-};dfPI&*?pXzC>5%A!G}?d-Ab= z=F|Pu<7t6j{#~BxMZ)5BR}c%M|Ek@L?@>8Rgf?TiB+UIR&BlL9iU4oaByaye7oEkL z|5ME`vB3OFLzlej^qyO%G3OH-9U}j!{Vkef`IsfA0APCAKFhwbq(_$HVi`A}GJdiMNbsZ)zy|F^-Uu8z=I-TPGdBXFJ4Ub?qK#9Jq z|67WbUi%2*zafQ{#!IEn6L;fGUs^`$cnmy=SGkxoo^}de zPV%02p$g)>Z9=ybS12P$W*V6c_7K3F)-w5bY*9V z5!D-g?{Ke!bh`ExXnqd9<9-eL@KQ4V&9wi4x zCY!F~@w!KcC-YEa>z>EJ=Py<#O1F$Az}oJGwNFp-LD$o#HI*pNGLo6c*Hnu659xHV zsJ2Ve-MvHkyL*Z^dIWC{8cyn$Gfyb^h;atfOmnOKs7Q}Ma>2t|_x=}$FgJoV^S4GC z{RK>rMO0v-{+EfPk!UF+7Yci_f~zRdWa*)Z(YR136AQtO&hA>EuNor|9ocXe?)^{t z$ehW(2QLm8&b%Md`PRq3-EW$sf7Vy0KC!DxUC4bPKzTK_an{z!?+*D?^^_LM&O{6f zdJapt3XlYqSj+M}BWr^BZXIVZ38S5~B2rIosj}j(dO6_aHCaE&HRFXDymFh^{4&%V zeoDrlwN=E9+(ajPWCffy{W-9GMAps~qrJ`J+Tf2!Zl#M@PCWT2$1tGTf{!-UdqT-X zn)vh&Jk4}U(S_Dwnslnlx9mCg;ckyk`X=VbN!JJ|T-NEfwr_$2lF9Vna{_ftIX3N{ z9wJ)II~`@T7ZbQSqMGMob<5u~V(vV@OpbQ!@$C}riLFubJvwAlEKmY^xc#32f8R`r zn3+()qE1%!=uGg$*GK3F08_1=%joHP^(gWQ#F#g7xcWTb$lOZd{mD-XLD99Q&~(RG z@b;BbszB8V@SBA1*LUpwn7(dX@#L5paSwM__SF0KA~^q2eUb|pcwl<(s)QKU*icl) z!q}>t_3&{PjhTeoJWU!lnpWSCVi!2oq+)oxg}38d*7cS~*B93%0yCtE&THgJi|Q|| z;c%YfIDyPLd~hdaKr{yFgL_t69&@nZ@XtqCj)`jrpkDq@U0;=r)q#f9`c<;HElyTZ zzG={_Q7x%vaHp%n!CanHdM~ft{-KNyCcCT87Frk_eO!G}Ck3q{ZRyTTn z+Rmh4#GqEI8TK@%vU!GR>N(Lsn>5xAv%1P9Jz}B>R?A2pG-$f7@ae7jWf|)J+`YfBDMkIoI z#OyrltWJwXgrW(iJ|P1&yFnuSR z{JKC_8#&4&eWIjQRy%D*=8826Ei@n!ahdqxnf>{raY68fpXu;R=I3KZ(XU3HRoKj` zkI*uARNv`5&xeNGt?qw6GNcliViNW}8s$4|-JVyqA!!Ix6fq<_EIE5rPnYdGZ{nr)qEc$K`<-RmIe&IQFqq)G|gd1a7@h%X4Y&*-_LU-j87q;(rYL*-edE z`YeKS+4bs!IR57=c{8y$ja4#fU=g|TzYL$mnR-d`NKiPAeX#$Khj5qTk7#G3(^%@Qv<0M}>h*!W=#ln0K zXctPqTTe#`cms#Vt+AYRsIb<3pBcROQzQVP$se`>>s>`J#r~yT6w`Ir4*(ZnP^{q< zJm;%rrPO$+?m_IZ7d9G}^lTvh4&N(V_)!B?kB{|GmZVkWZB{C&$Sg|zxh^OC-F&#c z-Qk$BWOqTgD*A^VwlznPvCvfZqk#~EbVzAX=%m|MPf=nkIpu3Pj6AHj;Or| zH+E#04P;V1&lW>gxlrENIQZ5*-GN@V9hAFP>{viSTPW?sAi$n_iU!E;K>!ebH?ak=X73Df%QiHLB$e;K%p=$B!2a?T$K9 z=gqY1F~Rq1Rdwi(c}k4cpElAHVa}8-%%U&WJE=v%S$@T>6&+IxwXK?=op3usX{tsj z&GSamvluI!B+fi2WN_5sXHBc~hneK?9d&OL%czIW4gZlm5ar<8-g`hDofcsqG`gTZ zbG{1xBx8?xFJ}u{W6P(bMI&?=|Fc0d>@YfNi^JGOOofCULW8_&coQxozoHA~bFA57ylOPbnJ!p*ehdYiQ$A`6) zyS9=nZ&x5rDo68q=_Xk^Y3t8J(~p|><~j-J_3Yez{60W6lwn>^O{=1JBVN2S(g&zG zM&TfLWNopAgMH?a9S_#JZ?>mM=#KwH zxdMUXu?H(6E4TeCB4>JbK>j8fOuq=>`Hnr5ofWw61^^#%*|*VtgbqRTpGT(LMhAV1 z{qc@*#@4rdf8p)=K=jG80bPO{AGD4BhNS5!Un3jM0P`obJA&HfWSH^MK(IYOz9Wf# zjGTLV3*}(*lOxBV5`$a&`E^hKP$j*&!9AK0D9mpcUKu5L_4?;JscllE42+8IP0}s4 zhLq=wDP#Ipg!k<~R{ml4#_2&l`J^8-)}qcL-P6pu`Xw^^|8`mK?M|7^Iv5S9 z!1Vyz?8DUKT{-U?I>O^Vn_CO7k(3m6QbIL5j}?#p*)SPW|BX)#ZF{P2+#-o%YVbQ> zp%YKG(ju^655-0MnUeR<06ITl?RKEPM{g|qO{zij8IBpct{@l8x zMIG&;HBC=6;-<=jKA;8@AI8HtKOTjd4eUJ$;Jn2PtjPS}^YNC1O z)u$T+duF%YM%#QK3f?5t%b=s3!P+HO>o%uimhRmgQ;d9_ZCi(#X9*TQi)Kb1Es8t| ze)KVmT^HSmsi=C6c*l4ls5dP#z7v?XSM$lq>hXmG>Le?FQ+S1q)d(pvvp)Dd_|`?c z(b38$tY}}=+sbg~jV=wC$fKG1+eD1v9*jR%OW8zj6BXI!|20=ljFgI|{E2MU9;u-Dg8e_hPB8(uRW z>Dh=yeOUfVM|P!DJ0Q&#XTu@(mUg85c_4U#8ZkXdAPqV?^zoHop=Z`48a7Z|Fj~S3 z69iT3>EAdSai^1Hh|WxPk40)BTf6#f<3uf4x3?B~vMQgTD;M}DR+wY`lD0Q z3dhEFw{ZJnS{v1J=h>uO-Wx)FD2B5ptFhggz##-@h2xDWXYz4Iki>w5GZ7-d{NPYjUdoCyb zf-G+%v%6`VQ=bE3bwZxvlTNf2o0bQ|ZgHOE1Ef7GVBl#SX3+(RZ;}V~B0Sz>YXd1* z8PV&JO-dua(5UJ#LI!gl76xoNs`+Gg;(!^%g!_fj2@_?cyMU`N>lW-^Z-i3nhPPNN zSL;B69HEL?2fTg|Bub!9!<_(Kvj&@cg|Q7^<*T9OF_E+>+F^D&-GySkL$iq~CwmJ@ zq<-<_mvDrlx#nU29Uj+6t4?GQU+o5kygO3=@}lv`Y}yBr)6;G>twhwUPF8fWhBd|S zj+>P9u*1^b&(!#(>l*Optdc3XsBl>SS<;8;k&T!{7)Ow-rh@bpd%x~5f*qQTgI&oN zS@P>QKIRPcvx?YZ4<9NXPB)0HzozbUtLy{vtms`Ga@(|MTpoa_#VfFGnjVk0Qy#gf zEE;_n15<(c**Wl?ZC=q2>bEmiZ{?Rzi@4qnhT*p|EL0mY!I!-pvMQpqVf)s%dzZ{b zSvX`JvYHx&8OynVoxzc8D`3ovc`T47C8U#w*IqvaC9ax-diCofp=Y7rSe-so?heo|_XFNHQa{`KE??2kB(p!wW5BLp-2* z06DbwMYZvwzzW0Fqlf)B-C+4F4m^)kmq=T5es#=&FyQ{S`@Vk?U?6aH8Zq@M!R=Lm6PWkEIh8WpJUkR^-gys8+nv%s46kx0Kg=H}{OHn6P|tb=fyQooM!Umh zjtJk39%_`$`0>|31Mwl7H{3cPzeB@<8GWXg`EdKYL`|dFxG?fQj79X@a0%|hn=N_S z+i!_uK_7GUR84a1(bt{c_sq4eN+e}Q#dKjcQ&Y4Ay|t}MF!`9$T-{xiqSBozhZC^B zV&Pq>R=aL!I;pOwu%}CW`h*$)Lz|ai$P|1_Ez;h6>1uk)gNvsZ#XZ+orDD<`vs+F! z4klk%bx33TgWx|JUNMW}aBarnHF-k!<~P;>WMEEGO&m-!x`bbAvr@Y@KRBObmuVc+ zXgR2|eQ3DVx#L)Y+WN&5yg0Zvi|>EU`Cpggo9|Rh_{K4-KmJ`v!Dvk)?$Av4ekWjj zJvAY7F>jd^t#p#@FK-gQg>e?*2LMI;%a=Tga(sWezkRWW$>1}98<0!A4ZkFpzauo3 z-8fL+ZuWt=@8IGL6|}CxP@jZ0@Ms}shJ)Gse9UMG2w;#y1^oVvDwp!(PrVsQGrY~j z4wzQVUO_lHdT{oF;g(>};IfOnG?l;wuIHyigT=7WqX0Nv$*K-z+hE=z<=s!c)m@Y3 zvX~lc(v#380TWnN4z4APyfb|igE@oy6rMxsW6*AP46g(j+YY10-a-8NF>}@7 zfnpW@zKz8i<4;fA=u=YwRR`V_mu$B(_tq6uq=4YQz}Vv$ko2w{@NJ{KeUZ-US!9&k zt-NbrTqv_P564k-L?bNs2}yT>U$6OXiuB4E+%XeDfm;|{H4HBf(L(!uGyn0ouad6P znHKXv)M7B#VJbe}?2GD~Il_6C27O=a{+AiBfcOaemxN5NBw{l_l*x?A?Zf-(`|hFa```D@ ze)k?d@ao~+vCU_W8*OVhr@;$s*{=TXtQVEm2rw~xq%>Kqi(q|28)X?iY2|(U_CitE z9f6tzWPo%bDl!UQ!P{84P{bn1&JpY5{DX%VjHm?-Acv4y{*bP9hWI{9pI9u&A1d0> zYb?r;M}VT^QMZt+Gu?5`CILOg)aQxDY_4xp%BEY~Yt~}NfS)joh*ASHBQ8A6%o;bX zSQw0ogGkH-RN+Ab<1TwO9TsAGRbx??{dBF!r+D(4$Fpt~7zEKjiDP!StdmR^BAeSR z9LqS>E{J672=-6KQ0pFRlL*esdtalC7V40HkOm(#LbZxitlEAu6C(s9vkbAEwdg(W z=UE+^M>5!=+L8@#M-}Qj4s`CQnyPGMEsUuX=`H(p9PUgcL|Ye@xyFX}UJsqK6r~5( z@!_mTI`9bbITd+e;k=jVnvLTdVXnZvhCkNM2A%XEVRn{U-=q?*Lud8(20dt|M=U(9 zm3g;~7}}haUNyRldPkQsa1cwJhnRH>$?60_=M8}gs}2{)+nMM)&lOYt05CoE%Gk&s4!GA3-dV z&}lE}qR>Fi?ftAA+@}J673v`%JNtW2Jk*$In{>lyy>CEFG$Lvo8diVy2HnfJ`L}+ElLwaXg zDvmj<4dqvDa2YPvLjI`KVyDfJG)26MkJT4nz_>R1c78RmN6?s|iC7+kY8mbtBJ$TU zkst;1JVfSnV@d(;d3)GL!5U58*b;+kfZ`>XI2xdN#cY@QHYY>|au3tqCW~qObIq@;P zwwXBhvo;~5_DXs)^BMW(^8>WFTQqj9j)PLsQH!t%U#D}uEf`cqwK#7Bos*zysX!FV zZ_*EDl1Yc`z%4P z>{#zTS<&1|?3jo@yb>#zf8`B9bqwbyA>67-Q@o&XLPqUv19T1B!T&0J zik1jC%3O}cTMu>zF}kFHcWFv>rBrw6UFcJw7j8l^l9n*+T&f{1=nHR z_&>ISqPyjsv8QM)J5@SD1|K3D6gQ!GC0cP0W3~p@x5+=NwQRvu#BLlRlvFp}HgK3o z8&a7COhetX#RR8g6e3IrO{vR9GOl? zhEulgSC{;Sv*a%=(|vc8q@>5!eZhp6*4~1jkH7SC`=ULzmS`Av%vz^}y~mpCqhZJW zI?k}Y6kzDCkQg(yUnLa>(rfWUbt=r||x%QUpc9NN`YE>wTn zktA1iKCVzN0|*E)RgAI3*zOFR%!xo3a>^nO z?D~xZ9Uc#WZpOtlq-3F#2Uf!#rRHJYQTvkyxw&^xxu3Jmu z6?Q_Su#e)Fb$~KppMu9aJNON^dkC?}@z#MlUt{Eyla?*QcV`0=UR8OolEv(k@;sA=ZbZB^mp#%CKP zLkWo~aGyAyOoLg+K}(kar#J3l_lpoG7Pdf-wmZpJPX>CE6e{?BqCDPio9meP;muiI zlz`UQDp{jsQv7_dL$y#bUwbNvSa1B8Mt?WW`Z@{037H_OxGlKWD?@!6Zi)Pso@nS_ zj5rw~8nQ2`H-;s1$Oc^aYp5b5JZ1BqJK^iU0CzC%)`YFAQx2IJH~M)WcaB}(NHVSn z70oOBRY*cTU{bBCok}Qfi9D-K^eZ=#b$u^XYc3?!c5TeCE6#ApXgWry)u656KibF( z{T7Wtwe%KnKDVAQwG4sWz%ouAV;czXrxfB2eb)MVq_uAzeM*sG`2GevAnOkYmnL^q z@AdFGmYfyhFQ8l3cO_W|V(@`MS^YeQ>(vHEug>qYdu%Hg2$=lWh6y8)zke8 z+YXUBx8$TIf_87B3qYHy&xXDKfXvm<8Tccr7Q_KBwe#-zjZ~YzXimLhu&?XbRL9ND z1N_R_rpnR7&-c*c5fN3@5gARYGm^8zzk7}^0qfL}5#8@JUjfl@OjgQw)3=g3XMy#c z9pY+)DrZ{3)%Q)lal*CDS*RQ_hRJoPpt{PTvhM~OCCEyO!>P13dM5VeSDMdDR z{$ZJhhl!TuD`UfV^xq5nTCDy3*~!wzfnU<*{if!B405h3+E3@CM4Tu*Cg6pGIH>0r ze;ppFC;u3xFTdKBJM2*8wZWH4Fy7f zudl6sb_oUk+s*uD$75J-iig{7#i>L8-MhI*aV7cS|1>>623G8Q>wv}|_QM|dZ>zVX z+wWojryV=8L#jlBJN+TfWtH;k|Lk2Ug;d=`as8jJ?W-HUU^<#hLFZYRU*=uv4gc`R z|K*#X`$B87|J8Br2>%(OJxPpHK9>&-Sp4TMxoE59^52ZX}VKT96^ zADP0alIEM#QNj1}#on_cT8J-SZoO?GQa7Dh=MwIsdX-9EOvzQ|Y?u^Xb}4q7&4Q4w(IEJPY;|}J<*s5MWgfv%And4!`d7L8_S6CIkv;x1Bve7BcGaK z9apfwx%+28>wp1JOEmZ(nA01ft#jR+TG1|Krl7dmGDQv@v$sf?^F}>FYabBB81;Rx zuW>5JgswZaiIes-Hj%u;!!B;bqiASRknrW*UMYOVJaDzM1l%Kf>EQs!5~buyHRhrh z#j{B-dst3VnGyU7n*ODvND~U8i2XZPUTY%+^+zyN%))@Q;~nkT$aYt7Yv6HpDd`{U zzhFZ8oGb(ZCyz^Yr`Mbz?--i<9&b%D-bmKLb#m{tJkNxw)&v`P^V7h%oYoXrcP)J_ z;?KJ^e{KD>9%p38ww+%vy4UD3H;b}3DxqW@)ef$Ic?hs?Xv_-27Zvug?r@NWDik-O zJg^Y#n>D~~V>ciofp4`ZlROg>YK}_bp}3L5vdp=AE{tSK;R{YijCm`Dq#V|R>&g(u zWNQTjdY_ITelvfGD$B#wAJw+<%T$>_#ab!*)iJx!GY_+MP;Q-(&8PD5uC|3x0iaN$ z0}|gxYn9;U`p?R931o%Cp`;pH+J17pa8X<#s~Vy&RyF7NOdMd>aTqhfDn=8d-5;pC z=GjJJweqt1w+A{Kw4T8j2Q~Ksv2=GyvVO}x4bRcrH~7upp=WY^V}p_vs;|Z*lUfu? z-hnKn79I4dtK6ui%d|z6%v>7^kH??@r4KU_=t;`v`l3O?x>sf*eu7m13LVtfuPgHN=TWumcwRiFch3n~vn%nESE)}O^~t)w90E?5|lIr%&UPBxhTZjd$CW6~1{3*!gs zwUX>P)cNlW^KSdHI8hgJjph-GPUg&OpHNV*Xow^81Lq14MxG5cF-23E&!WU^a|r`w z$F21_ksWVBIb33`3&egVT-p5e+j%80q$QZA0YDhBf*jb3m>}NTe?Gvi3HbRvQ`{9|r`< zFW>#8mJHX=2BoP|2IvdHM2M|!(#dQog?YGR12k3(BhG9l*su_r;EfxM8AEd~pR&5C zM+gUzj!_jEQ>r?B`M4Wpx3AmrDdf^gJtm?)){=U8mEt1LFXn@`B`{l?Ib+J1>ASHs zy{SiEA;2B)XAjLOETdKO&tb$$fjaeBp}vT-!NSot738mAj+N|kN^25&~*0U{2}sy#!#UIg^1 z)KXXQvmf&yA1VN%418>gPv}>^hmpY?gfSU5Bx;zO)Dm~oMwrftdR_&%UWQc#bE?mj zXf*m63siNiJwrh+j#JQhamk6>W~&-vpDt!u=B;51As!~k7Ctr))1^zpDr3WRpv7c_*`G$WYvs=d=W7dGjyPMGrmz7 z{wi$T*P6RuUf{x3&^0c8uqKXlHmEc{MaIOhrz*PJPLt+0wbNf1y=$XFKI*5fWHZXy zMYx{R+8G=R*K56v1hRRti4!v)*Qhw1*msA-d1ZG7uuaJ3f1qr1@+5-1L*-Q^|A5M9 zkemz9jl$YFPjXC#s`Ct7YI?xo^q*?84RNv-MxqM!uL#eEQ(1r#Vtci1rS>_?SMn$v z6~+PF3B3lHi~|%x!1uiAq0H3NCp|vS8jo&gXCnKe5lmzIE~GVOK7D^m(pzM|x$c3B z_ifE~DK1KOdF4_(A~f}V!u&bska9%_aXBaDEY9;=NgHxaYbuNrCMa(v(pvUE34A5U z`$R#eu;IY6+Gel{vkb!9jI)O71D#*eC+2NHO+C8vpHYX4w0qHMur{b-Yq%T8((q~4 zozM0SjlSyuB{O^u5D1J-%nZXyKsv5k3|CE{7R82yhw*)C4kbS@-n`u!I2nIOKd{*x zdGWI4^w}sS!3OIv9K}kBPL8+6SJ4|}n%)ttPB^5E7E1C{P}$fhC4`YVhHKfi)#JEA zF#C4K)>1=~6M~J0f*q1u?-Y~gN&@ABwBzCr{VYd#5WV zKx|$soxB$HA)0qq+`uXdl6%yxCtuaLtJ>G*6^_~S$fFgIO}OvU1Am;v^D>PkJ?2Pj zYI6hTH!9_EsF z6dBeTuuM_n*&kMx;nBlztMGG(ZqD=>cK8!c%+2c7cKe^~cX+E4pNnvQ8SngrS1z?+ z-anWwi;*yYSjA%@@Sb3b6aQgTn{+4{Q+<>wy(1k~yf2dk0fdQ!`*!fEOkmc)Sv~ZJ%K{&b9dqJ4L!j?l9r_#pG(%^+*~C;_t`~w_Rk?4 z*SQhl_&zx4V_4wrfy0|~+$lhD0~_$@X}@fr3ol<+4;hl*!*T;7S`@_UI zOpb{uiMr((u3_%b{;%1P`s_!DH9^=aN^W9C=+g&<-;ryATsFbal{Lmp92lN|7V5(w zRn7lR@N?$r4*g`@i-TN6pHnk_*quHHh>z&t1wAbmOVeQ?f^(eGAcOejsuoMx#PF4u z*!I3=%zRdVd(>_RwAr9MFU)(OQY9JSl+FU5s8c!v83Mc{nHDstK;1rY<56T$RRT#V zuscqU$7@g!>7JqtsP|U=2@p^;@WnfkG&fq^TD<)nC;bn|iRW$sGY%1dcu>&VV!^ET z&try>EUfQ9{HFc1O}+Mno_Lz}M@9c&M#@~6HZ4P8XotrfWWs2Z9qI_2|DM7AJcXSK zL~LX0h7WKMEOA}bejKE}p$;1BVab2j7i`PEVU}f0`>L}TdpDP2ug{ojbVn)^P!%DD zKv{!yi-_xsN&w}>Ltx}m4Vse;ZLAJXPta0g6>8sk@=zDyj3^IgyYYIO<9|Y98^P$d z{e-iX)ss^}{5;1?E?T9q9m%Pr*ChpEQLbUszNy-=2*I?9M7OU4fe!*rz-G~r)W2FP zw$u}bCPu=4zPyL5)tqe1tsXMi&zOB>j!U5x0A(77p@gDPrgTIIDUg%78xQ9HiY4`b z#ga0+xJ|M?y*1Tu`__HUBGjzllY+J#@(#cX8 zl`iTPs3E&2Q;zLD@BpuE;|k29qYeKBUbnls&g^)E6KPP%EaOB(rmg0f(JB`UhT>d< z#1P8pd(3HPudh-o8pydq-mTf!)5C_ zxmSNQG=vd>?6JlUD6j!SGc!M@LdM+j^`fC)L7AC1_gZe)qVnj7jNKVbeFUod;pQxH zj1f0DX{4_-OA(HYP5eQf8{joG-yV9Q<}4pHkPnmie%V5eb>!qV*FD{Ayig~J08AVR z)*JCd0u?Di8jd>JlR7_E;aBD-DEG!b!)B(zkfg0VV?jQQbFjxR)OR{=-mvp1%DMQf zq9)(P8t>6xcmAl68Kqd-_aMnnilcACZUJOojQQL{P^mXH_1B|Q0p$;%_Ja})@zY?@m2{kG4E5`w{OPcJr5@n0uu;t%qXmhN z(W^f=A9uj+qPC;90Dox=ExpHZ>~?sJk;j2kbfF&o?{(JqnZud7% zcN{+cCHVhq?@Gg(y1Fh!C{jhl0aQRxD>yA7wlihpZuHr`w_7=h<;tGg^ zqz#wOR77Pg>D7($G_-tdjHqHGfJ0Os{ z=${Q~fk(qNbLYJp8~SZ5W4x`;jK$)t@8ymC&KmP<7($UN4dM83b~`I~-*~cX!)3El!;17OEE@-FJ-JRgrol<8@yjBV8vFxyVo|a7TOZ;epVBb==kZ zGq$-+A{q5SBXyfm*{k_+h6?zwT3f|JD)Z{x0QYeUe@xJcNW-0INrB6tC&T)NAZOJu;nFPtwwN6GII`?oci30Ls} zd&t%C&8Z?sZvd}GRc;1v4xt%7IsdL#9l$|bIX+>cmP*H^lx7PX$zX)pGuK4xpywSc z?02N^TIT%LnznDaLqQ}S4O4LtFM8^^v&>&=0}F!%jEc#@E?ytCMfh$0>kv%sru0jp zy-C8M$c3)QRymFR%=5be+s&xvDQ}pt!C$#5qYdiXWWh3CH zVK|ZTwXBw8eg2w)mj>DcVKU;m@Q$!=Th@^7F9+*He_`2ck$1Nln&`5;1v9${|4wOd zY0|{V`m*|J`Z(5Chu4SU2t)$^$7{Vq6N#`j%yA$Gw%9#vibvkIkg!3ak^eQ|JnpNw zv~s1X!3<=9b}yEBi@TY5fix?3`>mJcqv@6}D> zS)ziGuL0hdIAoP<0wFc2pL^`VgRry2BPr}*0c-Aa_sS@R7N?&Unj3}=f<3bytF+Hn z!LDjDiX8?UXT+gyoRr1odSd5AFs$M#F2)7x!Uq5+l0vsWQFlp}bga`Yfb+Jx z#uC4nBaJV`BqgJyQ4E`&WT7Kq;_fX`kt*xoc6P_kcP&&ncYwYqiG{)peJgTvx#nQbO&X!Le5hbx@@>thqnH}!~@`LLde|VH(F__mIk-K zlms`1ZIoDuWArlu@AJ9bby=6ay^8KqUjY{oE_d%i)>0_fn2e=o%-L%l6`nkH@!A=2 zH4Ef1_}{$T(lD24PVuC*m*~lCqqFi==xMds=OM>?oR|~LV8#k!{vg;$!?<<~U!yXL ztfwI)vAbfof$;W|u%@Oag;pHkfZfvsI4i5tfDwP>38g?9-F`S}xJ73+w)-^aGXHr* zcc|o=C@5ms#YD=&{CK|Z7C+BMDMX2PsRiLVsBt2$APatoI+W{&se&%a!vwmyD}1t=&2$Bq9&Yh zseFebZYd<<8#hx*b<<8^Zs#O>k!i9kp`yj&$QED9b_Q%xdZkB{o8|z^xEe}mi6DqA z6bi9#J8Ho9UE{D<>k>M%#18Xa!rhB8~u;F1PeX{!(STe?{5Aqu>_^ z$^c*Zq|eNs{K_{$wpJ3^<^H`Ss%sUiJDW5zB3)Tyf2TH1x_GvK3Zq&p-*!BYXB7cf zk^WarJhwiK$=19IcU-{cmy=z8=vK04Sqv=k%~Eg*bwfc+&t^)W!sbj^OnU4x=YqfS ztD=VmQ%h69Y6BjW=hS}_1n61uE}S%qgewlRUowc26iptNwn zgZfs@E=a&fOMcpD;&}^~0sD2_Yn_yVw#QHOndAwsR8?D!l&=ni+j)g0??|C9I^o3P zeGz}XJ5F@$iB*8R$9KOy!Zs9_yt#@x(=E%1Qh=31VcwB4{NFLfMY?hUM^VUd&kn*~v z3{cZCujzFHD8Cq-^G4?yPv$H2Y^Qa54#=qnceXU5KAyGuIuTIy0)E@3r$KuA&NpXRH}nVmumvly9dAw!U}laAu-*lid-uFL9WzjGPvkYNFpZ5>gwe^3yDrl z_JSmYpOSC<7p=Mt7kj6$DHZob8=1gMBxJ`#430{zF1^7oB2|GJhUPd&D*jiQ*+Dl! z#R_R@hvPhVm{9Acz`a&z(<%tPKxm2}TAMgL+)mYp;T)1*J6`H49z+Shr419|@Um5K zr}RF+{WFV~G5&VB`KOB=HdVoXKUdLXU~OOaHifIR{W%+pJxQAyomj-sBv?!|9asvq0$iEopeQ0%Lz=fVZW@9!u$9b(wxwv^nj&}gg9l&7Z9U7C+d zH!Io53X)Gn`+&d~1LKW!_Klt}BcOlkTLP8@P>K&useMP!Z_AiP+VG%hGR=;iSt#;9 zL1tL%?HX@>)cK4V9&?JCS%@r>-R-tMg?S}s%mqS=0Ar|(*0cUQ~-Gt($?#X6Ytf)E4!xD&np(anX|%3zV$Bnq4N@_`$Z)?u&;`{#s+;+RFQs$A35e|aK0Ep8%RA7*G!jSoLX&x z%nS6>f^-#P0YA@8O*pdm2J!n*sw`Thklj?G>~cP7{J~BFk0noS%}_i8grha49DN#T zb8T&$%ABf??|iLFL8d=bDW)!K8+G=m#8RaRcTPQ0b(`rn)1uQMduX;g#%kXjRE|nD znRTj6e(EYy1o7}J**RE|>d-KuMU*l5bxZ|S^KOKor3jJtpQ%yZLx zxCPT=p1tKW)2M&>g__F@Df#*zpLyh^P%bLf7$Nhiem2N_ zpDH_mR-clpntH`QI1T9KRTM!&z7Gl=pQu%WBCkSXpOFHn5(p^rDl4ZyavD(Ng(5GM zHh(5vhfv49fAtH7K$`-pLZA%-Z5j||05L9#BoiX60QcX(f=~y9IvQ9IVf8;uSlPF; zKJ)_k0H3!(#JzH>7(^dH^Z}5oAm$ljo*~ilGh!)}1420|bg&Of zo1wHBN}Hi#HK^7Pl8xrS7Yv|^1E}HvsyKiu4j|b`9V!!7iiePF^gk>cK~mAw_LcWr zRUpg@VO|LHLYNoAyb$JvFt1`$B(%Ho|Fb!D(i?*$`82y}9+h3bTl3vP?x95OC4t)? zd;D&eq8TW7xp!9ds#?SO#MRr4H8R$f*r2}f{JjXx?#yMT5gXBmnrM8YeD(%a$JH5I zRD(btVy~K;bJTKX?5q9eBWbq|s5!K(8?MNi+glT-_FhkY-!?_#Wj3ipz5LFMjcIc~ z0=W-?QzQniX*8a(H@JM|)b{GBT{{;*>!;4a4W(+|tebiO$TB~!L;_Z=>t1^P6m!N5 z3PA5-MGyJu?Qyq2`|fDFL<_Yf&?0>5`^rC#PXLMxM4$EHKA3SYWH!ib@0W}zwA%jN zYaMz?|Gm!;@@AyORD65eN!u@FrO z(UjAk;WL7tOG^7nH8stryY06hfW*HK;A=)yMd4$^vrWoy{apm-bZ>+goZ^Xi`QPe=JqN_Yc;aB1hIoG)rTrYy%NtJf z~702HiA}WRP&I6Jv8={#%vAH zt6a z&5e~)V`M$`qLQ0KX8;e2ToqwkpJL9DVkvY3`|NnN*sI{mJIX8R6CJOd!*Pf=TFtnebXO+PpQ8V}}^WY9P^CTEzh?A;&BM|h6YR`E`PQ&$nO!e>#o ze6P_T1_pr%QGcs?CugkaWpOBoYKazrbmF~js6ykK;`B1yNHjor)C1ednbIetb@ye_ z>ci%}3-P{hGHH3E)*7?1mJ#FdXTp&vyl&|GQ7U45#)`JX9I9Y}X+*j@uIT>I@!;j^ z!{ETe7pPxoY&iyNp)RrP)xwi|EcmW5Yt&7srxBc?Nray4M|BNc{D&bS@u_dL%0C&39d&6fA7v9QHcGC5ctZl6}? z{Z8_aet0ibGx_(n29?}V-^YcmoK}TQ-=73FeW&WO5V2v>v^Ef1|LOXIlSj(HJg4!F zpQ>5^e>JDn>X$wI^31`tLUKV^+<0XyK(_+zq60M5z`xzQob2;=9R2P80B1Km_y7O^ literal 0 HcmV?d00001 diff --git a/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.iphone-ios-15-screenshot-1.png b/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.iphone-ios-15-screenshot-1.png new file mode 100644 index 0000000000000000000000000000000000000000..d8c9541b96cee7b5c7c8d631c54c24bf8b553cb3 GIT binary patch literal 31691 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 literal 0 HcmV?d00001 diff --git a/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.iphone-ios-16-screenshot-1.png b/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.iphone-ios-16-screenshot-1.png new file mode 100644 index 0000000000000000000000000000000000000000..a5129697e39301780e4fb8bd0d64950ce3bab7ab GIT binary patch literal 68961 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 g5dlBvQhbq`S}Y88TIYUH4*Xf~w%wJn + + + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/Tests/UITestsHostApp/Preview Content/Preview Assets.xcassets/Contents.json b/Tests/UITestsHostApp/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Tests/UITestsHostApp/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tests/UITestsHostApp/StatusBarStyle/HostingController.swift b/Tests/UITestsHostApp/StatusBarStyle/HostingController.swift new file mode 100644 index 000000000..1b13e8b13 --- /dev/null +++ b/Tests/UITestsHostApp/StatusBarStyle/HostingController.swift @@ -0,0 +1,32 @@ +// +// HostingController.swift +// Blago +// +// Created by Dmytro Chumakov on 04.05.2023. +// + +import SwiftUI + +final class HostingController: UIHostingController where ContentView: View { + + var statusBarStyle: UIStatusBarStyle = .darkContent + var isInteractivePopGestureEnabled = true + + override var preferredStatusBarStyle: UIStatusBarStyle { + statusBarStyle + } + + override func viewDidLoad() { + super.viewDidLoad() + navigationController?.interactivePopGestureRecognizer?.isEnabled = isInteractivePopGestureEnabled + } + + override func viewWillLayoutSubviews() { + super.viewWillLayoutSubviews() + + guard #available(iOS 16, *) else { + navigationController?.setNavigationBarHidden(true, animated: false) + return + } + } +} diff --git a/Tests/UITestsHostApp/StatusBarStyle/NavigationView.swift b/Tests/UITestsHostApp/StatusBarStyle/NavigationView.swift new file mode 100644 index 000000000..85e353547 --- /dev/null +++ b/Tests/UITestsHostApp/StatusBarStyle/NavigationView.swift @@ -0,0 +1,44 @@ + +// +// NavigationController.swift +// Blago +// +// Created by Dmytro Chumakov on 11.05.2023. +// + +import UIKit + +// MARK: - NavigationController + +final class NavigationController: UINavigationController { + + static var shared: UINavigationController? + + // MARK: Lifecycle + + override init(rootViewController: UIViewController) { + super.init(rootViewController: rootViewController) + setNavigationBarHidden(true, animated: false) + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + topViewController?.preferredStatusBarStyle ?? .default + } + + override func viewDidLoad() { + super.viewDidLoad() + interactivePopGestureRecognizer?.delegate = self + } +} + +// MARK: UIGestureRecognizerDelegate + +extension NavigationController: UIGestureRecognizerDelegate { + func gestureRecognizerShouldBegin(_: UIGestureRecognizer) -> Bool { + viewControllers.count > 1 + } +} diff --git a/Tests/UITestsHostApp/StatusBarStyle/RootView.swift b/Tests/UITestsHostApp/StatusBarStyle/RootView.swift new file mode 100644 index 000000000..478b1ec99 --- /dev/null +++ b/Tests/UITestsHostApp/StatusBarStyle/RootView.swift @@ -0,0 +1,52 @@ +// +// RootView.swift +// Showcase +// +// Created by Orackle on 14.07.2023. +// + +import SwiftUI +import SwiftUIIntrospect + +struct RootView: View { + + var body: some View { + VStack { + Button("Navigate To Detail", action: navigateToDetail) + } + } + + private func navigateToDetail() { + let controller = HostingController(rootView: DetailView()) + controller.statusBarStyle = .lightContent + NavigationController.shared?.pushViewController(controller, animated: true) + } +} + +struct DetailView: View { + @Environment(\.presentationMode) var dismiss + + var body: some View { + ZStack { + Color.red.edgesIgnoringSafeArea(.all) + + VStack { + Button("Navigate To Detail", action: navigateToDetail) + Button("Navigate Back", action: goBack) + } + } + .introspect(.viewController, on: .iOS(.v13, .v14, .v15, .v16)) { viewController in + /// some customizations there + } + } + + private func goBack() { + dismiss.wrappedValue.dismiss() + } + + private func navigateToDetail() { + let controller = HostingController(rootView: DetailView()) + controller.statusBarStyle = .lightContent + NavigationController.shared?.pushViewController(controller, animated: true) + } +} diff --git a/Tests/UITestsHostApp/TestCases.swift b/Tests/UITestsHostApp/TestCases.swift new file mode 100644 index 000000000..64285580a --- /dev/null +++ b/Tests/UITestsHostApp/TestCases.swift @@ -0,0 +1,3 @@ +public enum TestCase: String { + case statusBarStyle = "Status Bar Style" +} diff --git a/Tests/UITestsHostApp/UITestsHostApp.swift b/Tests/UITestsHostApp/UITestsHostApp.swift new file mode 100644 index 000000000..0069c252a --- /dev/null +++ b/Tests/UITestsHostApp/UITestsHostApp.swift @@ -0,0 +1,28 @@ +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) + guard + let testCaseRawValue = ProcessInfo.processInfo.environment["testCase"], + let testCase = TestCase(rawValue: testCaseRawValue) + else { + preconditionFailure("entryViewController not set") + } + + window?.rootViewController = { + switch testCase { + case .statusBarStyle: + let navController = NavigationController(rootViewController: HostingController(rootView: RootView())) + NavigationController.shared = navController + return navController + } + }() + window?.makeKeyAndVisible() + return true + } +} diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 987b4c2dd..f2b53a67c 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -89,6 +89,9 @@ lane :test do |options| "Introspect" when "SwiftUIIntrospectTests" is_legacy_sdk ? "LegacySwiftUIIntrospectTests" : "SwiftUIIntrospectTests" + when "SwiftUIIntrospectUITests" + next if is_legacy_sdk + "SwiftUIIntrospectUITests" else raise "Unsupported scheme: #{scheme}" end @@ -100,6 +103,8 @@ lane :test do |options| ensure_devices_found: true, force_quit_simulator: true, disable_concurrent_testing: true, + result_bundle: true, + output_directory: Dir.pwd + "/test_output", ) end end From b94da693e57eaf79d16464b8b7c90d09cba4e290 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Wed, 19 Jul 2023 21:28:26 +0100 Subject: [PATCH 100/116] Bump to 0.9.2 (#315) --- CHANGELOG.md | 6 ++++++ README.md | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d18b93028..ade6d7a29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ Changelog ## master +## [0.9.2] + +- Fixed: occasionally wrong status bar style (#313) +- Infrastructure: added UI Test suite (#314) +- Infrastructure: disabled "Autocreate schemes" (#308) + ## [0.9.1] - Fixed: only box up content for `.view` introspection (#305) diff --git a/README.md b/README.md index 99fbb19a7..4798cde56 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Install ```swift let package = Package( dependencies: [ - .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.9.1"), + .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.9.2"), ], targets: [ .target(name: <#Target Name#>, dependencies: [ From 22d75759bbab73061a23724d921afbcaa07cbbe6 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Thu, 20 Jul 2023 14:50:39 +0100 Subject: [PATCH 101/116] Add `SecureField` introspection (#317) --- CHANGELOG.md | 2 + README.md | 1 + Sources/ViewTypes/SecureField.swift | 79 ++++++++++++++++ Tests/Tests.xcodeproj/project.pbxproj | 4 + Tests/Tests/ViewTypes/SecureFieldTests.swift | 95 ++++++++++++++++++++ 5 files changed, 181 insertions(+) create mode 100644 Sources/ViewTypes/SecureField.swift create mode 100644 Tests/Tests/ViewTypes/SecureFieldTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index ade6d7a29..635d2a3fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Changelog ## master +- Added: `SecureField` introspection (#317) + ## [0.9.2] - Fixed: occasionally wrong status bar style (#313) diff --git a/README.md b/README.md index 4798cde56..14952d41a 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,7 @@ Introspection - [`ProgressView` with `.linear` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/progressviewwithlinearstyletype) - [`ScrollView`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/scrollviewtype) - [`.searchable`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/searchfieldtype) +- [`SecureField`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/securefieldtype) - [`.sheet`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/sheettype) - [`SignInWithAppleButton`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/SignInWithAppleButtonType) - [`Slider`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/slidertype) diff --git a/Sources/ViewTypes/SecureField.swift b/Sources/ViewTypes/SecureField.swift new file mode 100644 index 000000000..bae630596 --- /dev/null +++ b/Sources/ViewTypes/SecureField.swift @@ -0,0 +1,79 @@ +import SwiftUI + +/// An abstract representation of the `SecureField` type in SwiftUI. +/// +/// ### iOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var text = "Lorem ipsum" +/// +/// var body: some View { +/// SecureField("Secure Field", text: $text) +/// .introspect(.secureField, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UISecureField +/// } +/// } +/// } +/// ``` +/// +/// ### tvOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var text = "Lorem ipsum" +/// +/// var body: some View { +/// SecureField("Secure Field", text: $text) +/// .introspect(.secureField, on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { +/// print(type(of: $0)) // UISecureField +/// } +/// } +/// } +/// ``` +/// +/// ### macOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var text = "Lorem ipsum" +/// +/// var body: some View { +/// SecureField("Secure Field", text: $text) +/// .introspect(.secureField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { +/// print(type(of: $0)) // NSSecureField +/// } +/// } +/// } +/// ``` +public struct SecureFieldType: IntrospectableViewType {} + +extension IntrospectableViewType where Self == SecureFieldType { + public static var secureField: 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) + public static let v17 = Self(for: .v17) +} + +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 v17 = Self(for: .v17) +} +#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) + public static let v14 = Self(for: .v14) +} +#endif diff --git a/Tests/Tests.xcodeproj/project.pbxproj b/Tests/Tests.xcodeproj/project.pbxproj index a0f992898..515dc8e6b 100644 --- a/Tests/Tests.xcodeproj/project.pbxproj +++ b/Tests/Tests.xcodeproj/project.pbxproj @@ -81,6 +81,7 @@ 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 */; }; + D57E66FA2A6956EB0092F43E /* SecureFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57E66F92A6956EB0092F43E /* SecureFieldTests.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 */; }; @@ -187,6 +188,7 @@ 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 = ""; }; + D57E66F92A6956EB0092F43E /* SecureFieldTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureFieldTests.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 = ""; }; @@ -384,6 +386,7 @@ D575069D2A27F80E00A628E4 /* ProgressViewWithLinearStyleTests.swift */, D50FFE8D2A17E2A400C32641 /* ScrollViewTests.swift */, D57506A12A281B9C00A628E4 /* SearchFieldTests.swift */, + D57E66F92A6956EB0092F43E /* SecureFieldTests.swift */, D5ADFACB2A4A22AE009494FD /* SheetTests.swift */, D5ADFAD12A4A41CB009494FD /* SignInWithAppleButtonTests.swift */, D58119CF2A23A62C0081F853 /* SliderTests.swift */, @@ -784,6 +787,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D57E66FA2A6956EB0092F43E /* SecureFieldTests.swift in Sources */, D50FFE8E2A17E2A400C32641 /* ScrollViewTests.swift in Sources */, D58547F82A1CDD740068ADF4 /* NavigationStackTests.swift in Sources */, D57506982A27F32800A628E4 /* DatePickerWithGraphicalStyleTests.swift in Sources */, diff --git a/Tests/Tests/ViewTypes/SecureFieldTests.swift b/Tests/Tests/ViewTypes/SecureFieldTests.swift new file mode 100644 index 000000000..1319cfdb2 --- /dev/null +++ b/Tests/Tests/ViewTypes/SecureFieldTests.swift @@ -0,0 +1,95 @@ +import SwiftUI +import SwiftUIIntrospect +import XCTest + +final class SecureFieldTests: XCTestCase { + #if canImport(UIKit) + typealias PlatformSecureField = UITextField + #elseif canImport(AppKit) + typealias PlatformSecureField = NSTextField + #endif + + func testSecureField() { + XCTAssertViewIntrospection(of: PlatformSecureField.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + VStack { + SecureField("", text: .constant("Secure Field 0")) + #if os(iOS) || os(tvOS) + .introspect(.secureField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) + #elseif os(macOS) + .introspect(.secureField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) + #endif + .cornerRadius(8) + + SecureField("", text: .constant("Secure Field 1")) + #if os(iOS) || os(tvOS) + .introspect(.secureField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) + #elseif os(macOS) + .introspect(.secureField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) + #endif + .cornerRadius(8) + + SecureField("", text: .constant("Secure Field 2")) + #if os(iOS) || os(tvOS) + .introspect(.secureField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) + #elseif os(macOS) + .introspect(.secureField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(UIKit) + XCTAssertEqual($0[safe: 0]?.text, "Secure Field 0") + XCTAssertEqual($0[safe: 1]?.text, "Secure Field 1") + XCTAssertEqual($0[safe: 2]?.text, "Secure Field 2") + #elseif canImport(AppKit) + XCTAssertEqual($0[safe: 0]?.stringValue, "Secure Field 0") + XCTAssertEqual($0[safe: 1]?.stringValue, "Secure Field 1") + XCTAssertEqual($0[safe: 2]?.stringValue, "Secure Field 2") + #endif + } + } + + func testSecureFieldsEmbeddedInList() { + XCTAssertViewIntrospection(of: PlatformSecureField.self) { spies in + let spy0 = spies[0] + let spy1 = spies[1] + let spy2 = spies[2] + + List { + SecureField("", text: .constant("Secure Field 0")) + #if os(iOS) || os(tvOS) + .introspect(.secureField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) + #elseif os(macOS) + .introspect(.secureField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) + #endif + + SecureField("", text: .constant("Secure Field 1")) + #if os(iOS) || os(tvOS) + .introspect(.secureField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) + #elseif os(macOS) + .introspect(.secureField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) + #endif + + SecureField("", text: .constant("Secure Field 2")) + #if os(iOS) || os(tvOS) + .introspect(.secureField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) + #elseif os(macOS) + .introspect(.secureField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) + #endif + } + } extraAssertions: { + #if canImport(UIKit) + XCTAssertEqual($0[safe: 0]?.text, "Secure Field 0") + XCTAssertEqual($0[safe: 1]?.text, "Secure Field 1") + XCTAssertEqual($0[safe: 2]?.text, "Secure Field 2") + #elseif canImport(AppKit) + XCTAssertEqual($0[safe: 0]?.stringValue, "Secure Field 0") + XCTAssertEqual($0[safe: 1]?.stringValue, "Secure Field 1") + XCTAssertEqual($0[safe: 2]?.stringValue, "Secure Field 2") + #endif + } + } +} From d9db33c228cc3721a4b4f8cf58d61955f8003342 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Thu, 20 Jul 2023 15:06:12 +0100 Subject: [PATCH 102/116] Fix manifest line in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 14952d41a..9b297e223 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ SwiftUI Introspect > **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. +> [`SwiftUIIntrospect`](./Package@swift-5.7.swift#L14) 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. > From ccb973cfff703cba53fb88197413485c060eb26b Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Thu, 20 Jul 2023 17:18:27 +0100 Subject: [PATCH 103/116] Bump to 0.10.0 (#319) --- CHANGELOG.md | 2 ++ README.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 635d2a3fb..b698fadcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Changelog ## master +## [0.10.0] + - Added: `SecureField` introspection (#317) ## [0.9.2] diff --git a/README.md b/README.md index 9b297e223..1c66b1fb0 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Install ```swift let package = Package( dependencies: [ - .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.9.2"), + .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.10.0"), ], targets: [ .target(name: <#Target Name#>, dependencies: [ From e93995cb75170a18113ddf1e3cf0d0668c33c8b2 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Wed, 2 Aug 2023 02:38:32 +0100 Subject: [PATCH 104/116] [CI] Run tests on iOS & tvOS 17 (#323) --- .github/workflows/ci.yml | 21 +++++++++++++-------- fastlane/Fastfile | 2 ++ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7df222e6b..4b9419fc4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,11 +47,13 @@ jobs: - [ios, 14] - [ios, 15] - [ios, 16] + - [ios, 17] - [tvos, 13] - [tvos, 14] - [tvos, 15] - [tvos, 16] + - [tvos, 17] - [macos, 11] - [macos, 12] @@ -70,7 +72,9 @@ jobs: install: true - platform: [ios, 16] runtime: iOS 16.4 - install: false + xcode: 14.3.1 + - platform: [ios, 17] + runtime: iOS 17.0 - platform: [tvos, 13] runtime: tvOS 13.4 @@ -85,20 +89,21 @@ jobs: install: true - platform: [tvos, 16] runtime: tvOS 16.4 - install: false + - platform: [tvos, 17] + runtime: tvOS 17.0 - platform: [macos, 11] + runtime: macOS 11 os: macos-11 xcode: 13.2.1 - install: false - platform: [macos, 12] + runtime: macOS 12 os: macos-12 xcode: 14.2 - install: false - platform: [macos, 13] + runtime: macOS 13 os: macos-13 - xcode: 14.3 - install: false + xcode: 15.0 steps: - name: Git Checkout uses: actions/checkout@v3 @@ -110,7 +115,7 @@ jobs: github.com/XcodesOrg/xcodes - name: Select Xcode version - run: sudo xcodes select ${{ matrix.xcode || '14.3' }} + run: sudo xcodes select ${{ matrix.xcode || '15.0' }} - if: ${{ matrix.install }} name: Install Required Runtime (${{ matrix.runtime }}) @@ -127,7 +132,7 @@ jobs: 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' }} + - if: ${{ join(matrix.platform, ' ') != 'ios 13' && join(matrix.platform, ' ') != 'tvos 13' && join(matrix.platform, ' ') != 'ios 17' && join(matrix.platform, ' ') != 'tvos 17' }} name: Run Tests (Introspect) run: fastlane test platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }} scheme:Introspect diff --git a/fastlane/Fastfile b/fastlane/Fastfile index f2b53a67c..e2d9d7752 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -6,12 +6,14 @@ devices = { 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)"], + 17 => ["iPhone 14 (17.0)", "iPad Pro (11-inch) (4th generation) (17.0)"], }, "tvos" => { 13 => ["Apple TV (13.4)"], 14 => ["Apple TV (14.5)"], 15 => ["Apple TV (15.4)"], 16 => ["Apple TV (16.4)"], + 17 => ["Apple TV (17.0)"], }, } From 6b90a2f293b8826311c52f9b0adbc6e9fa563464 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Thu, 17 Aug 2023 21:02:18 +0100 Subject: [PATCH 105/116] Add visionOS support (#327) Co-authored-by: Scott Sykora --- .github/workflows/ci.yml | 31 ++-- CHANGELOG.md | 3 + .../Showcase.xcodeproj/project.pbxproj | 10 +- Examples/Showcase/Showcase/App.swift | 2 +- Examples/Showcase/Showcase/AppView.swift | 134 +++++++++++++----- README.md | 4 + Sources/PlatformVersion.swift | 24 ++++ Sources/PlatformViewVersion.swift | 11 ++ Sources/ViewTypes/Button.swift | 6 +- Sources/ViewTypes/ColorPicker.swift | 20 +++ Sources/ViewTypes/DatePicker.swift | 21 ++- .../DatePickerWithCompactStyle.swift | 22 ++- .../ViewTypes/DatePickerWithFieldStyle.swift | 6 +- ...ift => DatePickerWithGraphicalStyle.swift} | 22 ++- .../DatePickerWithStepperFieldStyle.swift | 6 +- .../ViewTypes/DatePickerWithWheelStyle.swift | 21 ++- Sources/ViewTypes/Form.swift | 20 +++ Sources/ViewTypes/FormWithGroupedStyle.swift | 26 +++- Sources/ViewTypes/FullScreenCover.swift | 27 +++- Sources/ViewTypes/List.swift | 21 +++ Sources/ViewTypes/ListCell.swift | 21 +++ Sources/ViewTypes/ListWithBorderedStyle.swift | 6 +- Sources/ViewTypes/ListWithGroupedStyle.swift | 23 ++- .../ViewTypes/ListWithInsetGroupedStyle.swift | 23 ++- Sources/ViewTypes/ListWithInsetStyle.swift | 23 ++- Sources/ViewTypes/ListWithSidebarStyle.swift | 24 +++- Sources/ViewTypes/Map.swift | 19 +++ Sources/ViewTypes/NavigationSplitView.swift | 24 ++++ Sources/ViewTypes/NavigationStack.swift | 22 +++ .../NavigationViewWithColumnsStyle.swift | 24 ++++ .../NavigationViewWithStackStyle.swift | 23 +++ Sources/ViewTypes/PageControl.swift | 20 +++ Sources/ViewTypes/PickerWithMenuStyle.swift | 6 +- .../ViewTypes/PickerWithSegmentedStyle.swift | 24 ++++ Sources/ViewTypes/PickerWithWheelStyle.swift | 25 +++- Sources/ViewTypes/Popover.swift | 27 +++- .../ProgressViewWithCircularStyle.swift | 17 +++ .../ProgressViewWithLinearStyle.swift | 17 +++ Sources/ViewTypes/ScrollView.swift | 18 +++ Sources/ViewTypes/SearchField.swift | 28 ++++ Sources/ViewTypes/SecureField.swift | 19 +++ Sources/ViewTypes/Sheet.swift | 28 +++- Sources/ViewTypes/SignInWithAppleButton.swift | 21 +++ Sources/ViewTypes/Slider.swift | 5 +- Sources/ViewTypes/Stepper.swift | 5 +- Sources/ViewTypes/TabView.swift | 5 + Sources/ViewTypes/TabViewWithPageStyle.swift | 20 +++ Sources/ViewTypes/Table.swift | 39 ++++- Sources/ViewTypes/TextEditor.swift | 19 +++ Sources/ViewTypes/TextField.swift | 19 +++ .../ViewTypes/TextFieldWithVerticalAxis.swift | 18 +++ Sources/ViewTypes/Toggle.swift | 18 +++ Sources/ViewTypes/ToggleWithButtonStyle.swift | 6 +- .../ViewTypes/ToggleWithCheckboxStyle.swift | 6 +- Sources/ViewTypes/ToggleWithSwitchStyle.swift | 20 +++ Sources/ViewTypes/VideoPlayer.swift | 17 +++ Sources/ViewTypes/View.swift | 19 +++ Sources/ViewTypes/ViewController.swift | 22 +++ Sources/ViewTypes/Window.swift | 20 +++ Tests/Tests.xcodeproj/project.pbxproj | 28 +++- Tests/Tests/TestUtils.swift | 4 + Tests/Tests/ViewTypes/ButtonTests.swift | 2 +- Tests/Tests/ViewTypes/ColorPickerTests.swift | 12 +- Tests/Tests/ViewTypes/DatePickerTests.swift | 14 +- ...DatePickerWithCompactFieldStyleTests.swift | 14 +- .../DatePickerWithFieldStyleTests.swift | 2 +- .../DatePickerWithGraphicalStyleTests.swift | 14 +- ...DatePickerWithStepperFieldStyleTests.swift | 10 +- .../DatePickerWithWheelStyleTests.swift | 14 +- Tests/Tests/ViewTypes/FormTests.swift | 8 +- .../ViewTypes/FormWithGroupedStyleTests.swift | 8 +- .../ViewTypes/FullScreenCoverTests.swift | 10 +- Tests/Tests/ViewTypes/ListCellTests.swift | 8 +- Tests/Tests/ViewTypes/ListTests.swift | 24 ++-- .../ListWithBorderedStyleTests.swift | 2 +- .../ViewTypes/ListWithGroupedStyleTests.swift | 8 +- .../ListWithInsetGroupedStyleTests.swift | 10 +- .../ViewTypes/ListWithInsetStyleTests.swift | 8 +- .../ViewTypes/ListWithPlainStyleTests.swift | 8 +- .../ViewTypes/ListWithSidebarStyleTests.swift | 8 +- Tests/Tests/ViewTypes/MapTests.swift | 6 +- .../ViewTypes/NavigationSplitViewTests.swift | 10 +- .../ViewTypes/NavigationStackTests.swift | 8 +- .../NavigationViewWithColumnsStyleTests.swift | 10 +- .../NavigationViewWithStackStyleTests.swift | 8 +- Tests/Tests/ViewTypes/PageControlTests.swift | 4 +- .../ViewTypes/PickerWithMenuStyleTests.swift | 2 +- .../PickerWithSegmentedStyleTests.swift | 12 +- .../ViewTypes/PickerWithWheelStyleTests.swift | 14 +- Tests/Tests/ViewTypes/PopoverTests.swift | 7 +- .../ProgressViewWithCircularStyleTests.swift | 12 +- .../ProgressViewWithLinearStyleTests.swift | 12 +- Tests/Tests/ViewTypes/ScrollViewTests.swift | 24 ++-- Tests/Tests/ViewTypes/SearchFieldTests.swift | 18 +-- Tests/Tests/ViewTypes/SecureFieldTests.swift | 24 ++-- Tests/Tests/ViewTypes/SheetTests.swift | 18 ++- .../SignInWithAppleButtonTests.swift | 6 +- Tests/Tests/ViewTypes/SliderTests.swift | 2 +- Tests/Tests/ViewTypes/StepperTests.swift | 2 +- Tests/Tests/ViewTypes/TabViewTests.swift | 2 +- .../ViewTypes/TabViewWithPageStyleTests.swift | 8 +- Tests/Tests/ViewTypes/TableTests.swift | 26 ++-- Tests/Tests/ViewTypes/TextEditorTests.swift | 12 +- Tests/Tests/ViewTypes/TextFieldTests.swift | 24 ++-- .../TextFieldWithVerticalAxisTests.swift | 14 +- Tests/Tests/ViewTypes/ToggleTests.swift | 12 +- .../ToggleWithButtonStyleTests.swift | 2 +- .../ToggleWithCheckboxStyleTests.swift | 2 +- .../ToggleWithSwitchStyleTests.swift | 12 +- Tests/Tests/ViewTypes/VideoPlayerTests.swift | 12 +- .../Tests/ViewTypes/ViewControllerTests.swift | 10 +- Tests/Tests/ViewTypes/ViewTests.swift | 12 +- Tests/Tests/ViewTypes/WindowTests.swift | 12 +- fastlane/Fastfile | 3 + 114 files changed, 1405 insertions(+), 350 deletions(-) rename Sources/ViewTypes/{DatePickerWithGraphicalStyleType.swift => DatePickerWithGraphicalStyle.swift} (79%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b9419fc4..af22bee42 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,22 +42,6 @@ jobs: strategy: fail-fast: false matrix: - platform: - - [ios, 13] - - [ios, 14] - - [ios, 15] - - [ios, 16] - - [ios, 17] - - - [tvos, 13] - - [tvos, 14] - - [tvos, 15] - - [tvos, 16] - - [tvos, 17] - - - [macos, 11] - - [macos, 12] - - [macos, 13] include: - platform: [ios, 13] runtime: iOS 13.7 @@ -66,6 +50,7 @@ jobs: install: true - platform: [ios, 14] runtime: iOS 14.5 + xcode: 14.2 install: true - platform: [ios, 15] runtime: iOS 15.5 @@ -104,6 +89,11 @@ jobs: runtime: macOS 13 os: macos-13 xcode: 15.0 + + # FIXME: this currently hangs on CI + # - platform: [visionos, 1] + # runtime: visionOS 1.0-beta2 + # install: true steps: - name: Git Checkout uses: actions/checkout@v3 @@ -125,14 +115,17 @@ jobs: max_attempts: 3 command: sudo xcodes runtimes install '${{ matrix.runtime }}' - - name: List Available Runtimes and Simulators - run: xcrun simctl list + - if: false + name: '[Debug] List Available Runtimes, Simulators, and Destinations' + run: | + xcrun simctl list + xcodebuild -scheme "Showcase" -showdestinations - 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' && join(matrix.platform, ' ') != 'ios 17' && join(matrix.platform, ' ') != 'tvos 17' }} + - if: ${{ join(matrix.platform, ' ') != 'ios 13' && join(matrix.platform, ' ') != 'tvos 13' && join(matrix.platform, ' ') != 'ios 17' && join(matrix.platform, ' ') != 'tvos 17' && matrix.platform[0] != 'visionos' }} name: Run Tests (Introspect) run: fastlane test platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }} scheme:Introspect diff --git a/CHANGELOG.md b/CHANGELOG.md index b698fadcb..85f150bc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ Changelog ## master +- Added: visionOS support (#327) +- Infrastructure: run CI tests on iOS & tvOS 17 (#323) + ## [0.10.0] - Added: `SecureField` introspection (#317) diff --git a/Examples/Showcase/Showcase.xcodeproj/project.pbxproj b/Examples/Showcase/Showcase.xcodeproj/project.pbxproj index 171bccb93..2b7edd2c5 100644 --- a/Examples/Showcase/Showcase.xcodeproj/project.pbxproj +++ b/Examples/Showcase/Showcase.xcodeproj/project.pbxproj @@ -210,6 +210,7 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; TVOS_DEPLOYMENT_TARGET = 13.0; + XROS_DEPLOYMENT_TARGET = 1.0; }; name = Debug; }; @@ -269,6 +270,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-O"; TVOS_DEPLOYMENT_TARGET = 13.0; VALIDATE_PRODUCT = YES; + XROS_DEPLOYMENT_TARGET = 1.0; }; name = Release; }; @@ -294,11 +296,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Showcase; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx xros xrsimulator"; SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3,6"; + TARGETED_DEVICE_FAMILY = "1,2,3,6,7"; }; name = Debug; }; @@ -324,11 +328,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Showcase; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx xros xrsimulator"; SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,3,6"; + TARGETED_DEVICE_FAMILY = "1,2,3,6,7"; }; name = Release; }; diff --git a/Examples/Showcase/Showcase/App.swift b/Examples/Showcase/Showcase/App.swift index 803adc616..d4ad38a6f 100644 --- a/Examples/Showcase/Showcase/App.swift +++ b/Examples/Showcase/Showcase/App.swift @@ -13,7 +13,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { return true } } -#elseif os(macOS) +#elseif os(macOS) || os(visionOS) @main struct App: SwiftUI.App { var body: some Scene { diff --git a/Examples/Showcase/Showcase/AppView.swift b/Examples/Showcase/Showcase/AppView.swift index 75d370336..f00f5a709 100644 --- a/Examples/Showcase/Showcase/AppView.swift +++ b/Examples/Showcase/Showcase/AppView.swift @@ -4,8 +4,11 @@ import SwiftUIIntrospect struct AppView: View { var body: some View { ContentView() - #if os(iOS) || os(tvOS) - .introspect(.window, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { window in + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect( + .window, + on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1) + ) { window in window.backgroundColor = .brown } #elseif os(macOS) @@ -79,12 +82,12 @@ struct ListShowcase: View { Text("Item 1") Text("Item 2") } - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) .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, .v17)) { collectionView in + .introspect(.list, on: .iOS(.v16, .v17), .visionOS(.v1)) { collectionView in collectionView.backgroundView = UIView() collectionView.subviews.dropFirst(1).first?.backgroundColor = .cyan } @@ -104,12 +107,12 @@ struct ListShowcase: View { List { Text("Item 1") Text("Item 2") - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) .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, .v17), scope: .ancestor) { collectionView in + .introspect(.list, on: .iOS(.v16, .v17), .visionOS(.v1), scope: .ancestor) { collectionView in collectionView.backgroundView = UIView() collectionView.subviews.dropFirst(1).first?.backgroundColor = .cyan } @@ -144,8 +147,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, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { scrollView in + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect( + .scrollView, + on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1) + ) { scrollView in scrollView.layer.backgroundColor = UIColor.cyan.cgColor } #elseif os(macOS) @@ -162,8 +168,12 @@ struct ScrollViewShowcase: View { .minimumScaleFactor(0.5) .padding(.horizontal, 12) .font(.system(.subheadline, design: .monospaced)) - #if os(iOS) || os(tvOS) - .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { scrollView in + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect( + .scrollView, + on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), + scope: .ancestor + ) { scrollView in scrollView.layer.backgroundColor = UIColor.cyan.cgColor } #elseif os(macOS) @@ -188,23 +198,36 @@ struct NavigationShowcase: View { $0 } } - #if os(iOS) + #if os(iOS) || os(visionOS) .navigationBarTitle(Text("Customized"), displayMode: .inline) #elseif os(macOS) .navigationTitle(Text("Navigation")) #endif } - #if os(iOS) || os(tvOS) - .introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { navigationController in + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect( + .navigationView(style: .stack), + on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1) + ) { navigationController in navigationController.navigationBar.backgroundColor = .cyan } - .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { splitViewController in + .introspect( + .navigationView(style: .columns), + on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1) + ) { splitViewController in + #if os(visionOS) + splitViewController.preferredDisplayMode = .oneBesideSecondary + #else splitViewController.preferredDisplayMode = .oneOverSecondary + #endif } .introspect(.navigationView(style: .columns), on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { navigationController in navigationController.navigationBar.backgroundColor = .cyan } - .introspect(.searchField, on: .iOS(.v15, .v16, .v17), .tvOS(.v15, .v16, .v17)) { searchBar in + .introspect( + .searchField, + on: .iOS(.v15, .v16, .v17), .tvOS(.v15, .v16, .v17), .visionOS(.v1) + ) { searchBar in searchBar.backgroundColor = .red #if os(iOS) searchBar.searchTextField.backgroundColor = .purple @@ -232,6 +255,10 @@ struct PresentationShowcase: View { ) { presentationController in presentationController.containerView?.backgroundColor = .red.withAlphaComponent(0.75) } + #elseif os(visionOS) + .introspect(.sheet, on: .visionOS(.v1)) { sheetPresentationController in + sheetPresentationController.containerView?.backgroundColor = .red.withAlphaComponent(0.75) + } #endif } @@ -239,10 +266,10 @@ struct PresentationShowcase: View { Button("Full Screen Cover", action: { isFullScreenPresented = true }) .fullScreenCover(isPresented: $isFullScreenPresented) { Button("Dismiss", action: { isFullScreenPresented = false }) - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) .introspect( .fullScreenCover, - on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17) + on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .visionOS(.v1) ) { presentationController in presentationController.containerView?.backgroundColor = .red.withAlphaComponent(0.75) } @@ -250,14 +277,14 @@ struct PresentationShowcase: View { } } - #if os(iOS) + #if os(iOS) || os(visionOS) Button("Popover", action: { isPopoverPresented = true }) .popover(isPresented: $isPopoverPresented) { Button("Dismiss", action: { isPopoverPresented = false }) .padding() .introspect( .popover, - on: .iOS(.v13, .v14, .v15, .v16, .v17) + on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1) ) { presentationController in presentationController.containerView?.backgroundColor = .red.withAlphaComponent(0.75) } @@ -275,8 +302,11 @@ struct GenericViewShowcase: View { .lineLimit(1) .minimumScaleFactor(0.5) .font(.system(.subheadline, design: .monospaced)) - #if os(iOS) || os(tvOS) - .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { view in + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect( + .view, + on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1) + ) { view in view.backgroundColor = .cyan } #elseif os(macOS) @@ -287,8 +317,11 @@ struct GenericViewShowcase: View { Button("A button", action: {}) .padding(5) - #if os(iOS) || os(tvOS) - .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { view in + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect( + .view, + on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1) + ) { view in view.backgroundColor = .lightGray } #elseif os(macOS) @@ -298,8 +331,11 @@ struct GenericViewShowcase: View { #endif Image(systemName: "scribble") - #if os(iOS) || os(tvOS) - .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { view in + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect( + .view, + on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1) + ) { view in view.backgroundColor = .blue } #elseif os(macOS) @@ -309,8 +345,11 @@ struct GenericViewShowcase: View { #endif } .padding() - #if os(iOS) || os(tvOS) - .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { view in + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect( + .view, + on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1) + ) { view in view.backgroundColor = .red } #elseif os(macOS) @@ -333,8 +372,11 @@ struct SimpleElementsShowcase: View { VStack { HStack { TextField("Text Field Red", text: $textFieldValue) - #if os(iOS) || os(tvOS) - .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { textField in + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect( + .textField, + on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1) + ) { textField in textField.backgroundColor = .red } #elseif os(macOS) @@ -345,8 +387,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, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { textField in + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect( + .textField, + on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1) + ) { textField in textField.backgroundColor = .green } #elseif os(macOS) @@ -358,8 +403,11 @@ struct SimpleElementsShowcase: View { HStack { Toggle("Toggle Red", isOn: $toggleValue) - #if os(iOS) - .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { toggle in + #if os(iOS) || os(visionOS) + .introspect( + .toggle, + on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1) + ) { toggle in toggle.backgroundColor = .red } #elseif os(macOS) @@ -369,8 +417,11 @@ struct SimpleElementsShowcase: View { #endif Toggle("Toggle Green", isOn: $toggleValue) - #if os(iOS) - .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { toggle in + #if os(iOS) || os(visionOS) + .introspect( + .toggle, + on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1) + ) { toggle in toggle.backgroundColor = .green } #elseif os(macOS) @@ -381,6 +432,7 @@ struct SimpleElementsShowcase: View { } #if !os(tvOS) + #if !os(visionOS) HStack { Slider(value: $sliderValue, in: 0...100) #if os(iOS) @@ -432,13 +484,14 @@ struct SimpleElementsShowcase: View { } #endif } + #endif HStack { DatePicker(selection: $datePickerValue) { Text("DatePicker Red") } - #if os(iOS) - .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { datePicker in + #if os(iOS) || os(visionOS) + .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1)) { datePicker in datePicker.backgroundColor = .red } #elseif os(macOS) @@ -456,8 +509,11 @@ struct SimpleElementsShowcase: View { Text("Option 3").tag(2) } .pickerStyle(SegmentedPickerStyle()) - #if os(iOS) || os(tvOS) - .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { datePicker in + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect( + .picker(style: .segmented), + on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1) + ) { datePicker in datePicker.backgroundColor = .red } #elseif os(macOS) diff --git a/README.md b/README.md index 1c66b1fb0..2267af7cb 100644 --- a/README.md +++ b/README.md @@ -231,6 +231,10 @@ extension tvOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) extension macOSViewVersion { public static let v10_15 = Self(for: .v10_15) diff --git a/Sources/PlatformVersion.swift b/Sources/PlatformVersion.swift index ad085d1cd..93552cf82 100644 --- a/Sources/PlatformVersion.swift +++ b/Sources/PlatformVersion.swift @@ -273,3 +273,27 @@ extension macOSVersion { #endif } } + +public struct visionOSVersion: PlatformVersion { + public let condition: PlatformVersionCondition? + + public init(condition: () -> PlatformVersionCondition?) { + self.condition = condition() + } +} + +extension visionOSVersion { + public static let v1 = visionOSVersion { + #if os(visionOS) + if #available(visionOS 2, *) { + return .past + } + if #available(visionOS 1, *) { + return .current + } + return .future + #else + return nil + #endif + } +} diff --git a/Sources/PlatformViewVersion.swift b/Sources/PlatformViewVersion.swift index fa3aeeda8..3d331363b 100644 --- a/Sources/PlatformViewVersion.swift +++ b/Sources/PlatformViewVersion.swift @@ -40,6 +40,15 @@ public struct PlatformViewVersionPredicate>) -> Self { Self([versions.lowerBound], matches: \.isCurrentOrPast) } + + public static func visionOS(_ versions: (visionOSViewVersion)...) -> Self { + Self(versions, matches: \.isCurrent) + } + + @_spi(Advanced) + public static func visionOS(_ versions: PartialRangeFrom>) -> Self { + Self([versions.lowerBound], matches: \.isCurrentOrPast) + } } public typealias iOSViewVersion = @@ -48,6 +57,8 @@ public typealias tvOSViewVersion public typealias macOSViewVersion = PlatformViewVersion +public typealias visionOSViewVersion = + PlatformViewVersion public enum PlatformViewVersion { @_spi(Internals) case available(Version, IntrospectionSelector?) diff --git a/Sources/ViewTypes/Button.swift b/Sources/ViewTypes/Button.swift index 36793a094..363540606 100644 --- a/Sources/ViewTypes/Button.swift +++ b/Sources/ViewTypes/Button.swift @@ -22,9 +22,13 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// Not available. public struct ButtonType: IntrospectableViewType {} -#if os(macOS) +#if !os(iOS) && !os(tvOS) && !os(visionOS) extension IntrospectableViewType where Self == ButtonType { public static var button: Self { .init() } } diff --git a/Sources/ViewTypes/ColorPicker.swift b/Sources/ViewTypes/ColorPicker.swift index 31f690240..2c8764834 100644 --- a/Sources/ViewTypes/ColorPicker.swift +++ b/Sources/ViewTypes/ColorPicker.swift @@ -35,6 +35,21 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var color = Color.red +/// +/// var body: some View { +/// ColorPicker("Pick a color", selection: $color) +/// .introspect(.colorPicker, on: .visionOS(.v1)) { +/// print(type(of: $0)) // UIColorPicker +/// } +/// } +/// } +/// ``` public struct ColorPickerType: IntrospectableViewType {} #if !os(tvOS) @@ -52,6 +67,11 @@ extension iOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +@available(iOS 14, *) +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) @available(macOS 11, *) extension macOSViewVersion { diff --git a/Sources/ViewTypes/DatePicker.swift b/Sources/ViewTypes/DatePicker.swift index db199aea8..f298f0252 100644 --- a/Sources/ViewTypes/DatePicker.swift +++ b/Sources/ViewTypes/DatePicker.swift @@ -33,9 +33,24 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var date = Date() +/// +/// var body: some View { +/// DatePicker("Pick a date", selection: $date) +/// .introspect(.datePicker, on: .visionOS(.v1)) { +/// print(type(of: $0)) // UIDatePicker +/// } +/// } +/// } +/// ``` public struct DatePickerType: IntrospectableViewType {} -#if os(iOS) || os(macOS) +#if !os(tvOS) extension IntrospectableViewType where Self == DatePickerType { public static var datePicker: Self { .init() } } @@ -48,6 +63,10 @@ extension iOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) extension macOSViewVersion { public static let v10_15 = Self(for: .v10_15) diff --git a/Sources/ViewTypes/DatePickerWithCompactStyle.swift b/Sources/ViewTypes/DatePickerWithCompactStyle.swift index 6769cd2f1..120f886b5 100644 --- a/Sources/ViewTypes/DatePickerWithCompactStyle.swift +++ b/Sources/ViewTypes/DatePickerWithCompactStyle.swift @@ -37,13 +37,29 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var date = Date() +/// +/// var body: some View { +/// DatePicker("Pick a date", selection: $date) +/// .datePickerStyle(.compact) +/// .introspect(.datePicker(style: .compact), on: .visionOS(.v1)) { +/// print(type(of: $0)) // UIDatePicker +/// } +/// } +/// } +/// ``` public struct DatePickerWithCompactStyleType: IntrospectableViewType { public enum Style { case compact } } -#if os(iOS) || os(macOS) +#if !os(tvOS) extension IntrospectableViewType where Self == DatePickerWithCompactStyleType { public static func datePicker(style: Self.Style) -> Self { .init() } } @@ -57,6 +73,10 @@ extension iOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) && !targetEnvironment(macCatalyst) extension macOSViewVersion { @available(*, unavailable, message: ".datePickerStyle(.compact) isn't available on macOS 10.15") diff --git a/Sources/ViewTypes/DatePickerWithFieldStyle.swift b/Sources/ViewTypes/DatePickerWithFieldStyle.swift index 6aea42ec7..ec1afcced 100644 --- a/Sources/ViewTypes/DatePickerWithFieldStyle.swift +++ b/Sources/ViewTypes/DatePickerWithFieldStyle.swift @@ -25,13 +25,17 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// Not available. public struct DatePickerWithFieldStyleType: IntrospectableViewType { public enum Style { case field } } -#if os(macOS) +#if !os(iOS) && !os(tvOS) && !os(visionOS) extension IntrospectableViewType where Self == DatePickerWithFieldStyleType { public static func datePicker(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/DatePickerWithGraphicalStyleType.swift b/Sources/ViewTypes/DatePickerWithGraphicalStyle.swift similarity index 79% rename from Sources/ViewTypes/DatePickerWithGraphicalStyleType.swift rename to Sources/ViewTypes/DatePickerWithGraphicalStyle.swift index ddfae0c6b..dfe1f20f9 100644 --- a/Sources/ViewTypes/DatePickerWithGraphicalStyleType.swift +++ b/Sources/ViewTypes/DatePickerWithGraphicalStyle.swift @@ -37,13 +37,29 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var date = Date() +/// +/// var body: some View { +/// DatePicker("Pick a date", selection: $date) +/// .datePickerStyle(.graphical) +/// .introspect(.datePicker(style: .graphical), on: .visionOS(.v1)) { +/// print(type(of: $0)) // UIDatePicker +/// } +/// } +/// } +/// ``` public struct DatePickerWithGraphicalStyleType: IntrospectableViewType { public enum Style { case graphical } } -#if os(iOS) || os(macOS) +#if !os(tvOS) extension IntrospectableViewType where Self == DatePickerWithGraphicalStyleType { public static func datePicker(style: Self.Style) -> Self { .init() } } @@ -57,6 +73,10 @@ extension iOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) && !targetEnvironment(macCatalyst) extension macOSViewVersion { public static let v10_15 = Self(for: .v10_15) diff --git a/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift b/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift index 3cc3d9e89..047fd45ac 100644 --- a/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift +++ b/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift @@ -25,13 +25,17 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// Not available. public struct DatePickerWithStepperFieldStyleType: IntrospectableViewType { public enum Style { case stepperField } } -#if os(macOS) +#if !os(iOS) && !os(tvOS) && !os(visionOS) extension IntrospectableViewType where Self == DatePickerWithStepperFieldStyleType { public static func datePicker(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/DatePickerWithWheelStyle.swift b/Sources/ViewTypes/DatePickerWithWheelStyle.swift index d87d4aba6..b5801db97 100644 --- a/Sources/ViewTypes/DatePickerWithWheelStyle.swift +++ b/Sources/ViewTypes/DatePickerWithWheelStyle.swift @@ -26,13 +26,28 @@ import SwiftUI /// /// Not available. /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var date = Date() +/// +/// var body: some View { +/// DatePicker("Pick a date", selection: $date) +/// .datePickerStyle(.wheel) +/// .introspect(.datePicker(style: .wheel), on: .visionOS(.v1)) { +/// print(type(of: $0)) // UIDatePicker +/// } +/// } +/// } +/// ``` public struct DatePickerWithWheelStyleType: IntrospectableViewType { public enum Style { case wheel } } -#if os(iOS) +#if !os(tvOS) && !os(macOS) extension IntrospectableViewType where Self == DatePickerWithWheelStyleType { public static func datePicker(style: Self.Style) -> Self { .init() } } @@ -45,5 +60,9 @@ extension iOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #endif #endif diff --git a/Sources/ViewTypes/Form.swift b/Sources/ViewTypes/Form.swift index 4ba05620c..5957522f3 100644 --- a/Sources/ViewTypes/Form.swift +++ b/Sources/ViewTypes/Form.swift @@ -43,6 +43,22 @@ import SwiftUI /// /// Not available. /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// Form { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// .introspect(.form, on: .visionOS(.v1)) { +/// print(type(of: $0)) // UICollectionView +/// } +/// } +/// } +/// ``` public struct FormType: IntrospectableViewType {} #if !os(macOS) @@ -69,5 +85,9 @@ extension tvOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #endif #endif diff --git a/Sources/ViewTypes/FormWithGroupedStyle.swift b/Sources/ViewTypes/FormWithGroupedStyle.swift index 6f38a31dc..dec129432 100644 --- a/Sources/ViewTypes/FormWithGroupedStyle.swift +++ b/Sources/ViewTypes/FormWithGroupedStyle.swift @@ -14,7 +14,7 @@ import SwiftUI /// } /// .formStyle(.grouped) /// .introspect(.form(style: .grouped), on: .iOS(.v16, .v17)) { -/// print(type(of: $0)) // UITableView +/// print(type(of: $0)) // UICollectionView /// } /// } /// } @@ -32,7 +32,7 @@ import SwiftUI /// } /// .formStyle(.grouped) /// .introspect(.form(style: .grouped), on: .tvOS(.v16, .v17)) { -/// print(type(of: $0)) // UICollectionView +/// print(type(of: $0)) // UITableView /// } /// } /// } @@ -55,6 +55,24 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// Form { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// .formStyle(.grouped) +/// .introspect(.form(style: .grouped), on: .visionOS(.v1)) { +/// print(type(of: $0)) // UICollectionView +/// } +/// } +/// } +/// ``` public struct FormWithGroupedStyleType: IntrospectableViewType { public enum Style { case grouped @@ -90,6 +108,10 @@ extension tvOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) extension macOSViewVersion { @available(*, unavailable, message: ".formStyle(.grouped) isn't available on macOS 10.15") diff --git a/Sources/ViewTypes/FullScreenCover.swift b/Sources/ViewTypes/FullScreenCover.swift index 143295d7a..3dea536c3 100644 --- a/Sources/ViewTypes/FullScreenCover.swift +++ b/Sources/ViewTypes/FullScreenCover.swift @@ -42,11 +42,28 @@ import SwiftUI /// /// Not available. /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var isPresented = false +/// +/// var body: some View { +/// Button("Present", action: { isPresented = true }) +/// .fullScreenCover(isPresented: $isPresented) { +/// Button("Dismiss", action: { isPresented = false }) +/// .introspect(.fullScreenCover, on: .visionOS(.v1)) { +/// print(type(of: $0)) // UIPresentationController +/// } +/// } +/// } +/// } +/// ``` public struct FullScreenCoverType: IntrospectableViewType { public var scope: IntrospectionScope { .ancestor } } -#if os(iOS) || os(tvOS) +#if !os(macOS) extension IntrospectableViewType where Self == FullScreenCoverType { public static var fullScreenCover: Self { .init() } } @@ -77,5 +94,13 @@ extension tvOSViewVersion { .from(UIViewController.self, selector: \.presentationController) } } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1, selector: selector) + + private static var selector: IntrospectionSelector { + .from(UIViewController.self, selector: \.presentationController) + } +} #endif #endif diff --git a/Sources/ViewTypes/List.swift b/Sources/ViewTypes/List.swift index e5d0faccc..a770131a6 100644 --- a/Sources/ViewTypes/List.swift +++ b/Sources/ViewTypes/List.swift @@ -55,6 +55,23 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// List { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// .introspect(.list, on: .visionOS(.v1)) { +/// print(type(of: $0)) // UICollectionView +/// } +/// } +/// } +/// ``` public struct ListType: IntrospectableViewType { public enum Style { case plain @@ -85,6 +102,10 @@ extension tvOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) extension macOSViewVersion { public static let v10_15 = Self(for: .v10_15) diff --git a/Sources/ViewTypes/ListCell.swift b/Sources/ViewTypes/ListCell.swift index fe7aa0883..daa0947d2 100644 --- a/Sources/ViewTypes/ListCell.swift +++ b/Sources/ViewTypes/ListCell.swift @@ -55,6 +55,23 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// List { +/// ForEach(1...3, id: \.self) { int in +/// Text("Item \(int)") +/// .introspect(.listCell, on: .visionOS(.v1)) { +/// print(type(of: $0)) // UICollectionViewCell +/// } +/// } +/// } +/// } +/// } +/// ``` public struct ListCellType: IntrospectableViewType { public var scope: IntrospectionScope { .ancestor } } @@ -82,6 +99,10 @@ extension tvOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) extension macOSViewVersion { public static let v10_15 = Self(for: .v10_15) diff --git a/Sources/ViewTypes/ListWithBorderedStyle.swift b/Sources/ViewTypes/ListWithBorderedStyle.swift index 2c196d23c..294bc6381 100644 --- a/Sources/ViewTypes/ListWithBorderedStyle.swift +++ b/Sources/ViewTypes/ListWithBorderedStyle.swift @@ -27,13 +27,17 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// Not available. public struct ListWithBorderedStyleType: IntrospectableViewType { public enum Style { case bordered } } -#if os(macOS) +#if !os(iOS) && !os(tvOS) && !os(visionOS) extension IntrospectableViewType where Self == ListWithBorderedStyleType { public static func list(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/ListWithGroupedStyle.swift b/Sources/ViewTypes/ListWithGroupedStyle.swift index 459e264c5..e1213e73e 100644 --- a/Sources/ViewTypes/ListWithGroupedStyle.swift +++ b/Sources/ViewTypes/ListWithGroupedStyle.swift @@ -45,13 +45,30 @@ import SwiftUI /// /// Not available. /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// List { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// .listStyle(.grouped) +/// .introspect(.list(style: .grouped), on: .visionOS(.v1)) { +/// print(type(of: $0)) // UICollectionView +/// } +/// } +/// } +/// ``` public struct ListWithGroupedStyleType: IntrospectableViewType { public enum Style { case grouped } } -#if os(iOS) || os(tvOS) +#if !os(macOS) extension IntrospectableViewType where Self == ListWithGroupedStyleType { public static func list(style: Self.Style) -> Self { .init() } } @@ -75,5 +92,9 @@ extension tvOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #endif #endif diff --git a/Sources/ViewTypes/ListWithInsetGroupedStyle.swift b/Sources/ViewTypes/ListWithInsetGroupedStyle.swift index 63e6a2898..8b5bd7485 100644 --- a/Sources/ViewTypes/ListWithInsetGroupedStyle.swift +++ b/Sources/ViewTypes/ListWithInsetGroupedStyle.swift @@ -31,13 +31,30 @@ import SwiftUI /// /// Not available. /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// List { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// .listStyle(.insetGrouped) +/// .introspect(.list(style: .insetGrouped), on: .visionOS(.v1)) { +/// print(type(of: $0)) // UICollectionView +/// } +/// } +/// } +/// ``` public struct ListWithInsetGroupedStyleType: IntrospectableViewType { public enum Style { case insetGrouped } } -#if os(iOS) +#if !os(tvOS) && !os(macOS) extension IntrospectableViewType where Self == ListWithInsetGroupedStyleType { public static func list(style: Self.Style) -> Self { .init() } } @@ -54,5 +71,9 @@ extension iOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #endif #endif diff --git a/Sources/ViewTypes/ListWithInsetStyle.swift b/Sources/ViewTypes/ListWithInsetStyle.swift index ee08aa034..1ae24014d 100644 --- a/Sources/ViewTypes/ListWithInsetStyle.swift +++ b/Sources/ViewTypes/ListWithInsetStyle.swift @@ -45,13 +45,30 @@ import SwiftUI /// } /// ``` /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// List { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// .listStyle(.inset) +/// .introspect(.list(style: .inset), on: .visionOS(.v1)) { +/// print(type(of: $0)) // UICollectionView +/// } +/// } +/// } +/// ``` public struct ListWithInsetStyleType: IntrospectableViewType { public enum Style { case inset } } -#if os(iOS) || os(macOS) +#if !os(tvOS) extension IntrospectableViewType where Self == ListWithInsetStyleType { public static func list(style: Self.Style) -> Self { .init() } } @@ -68,6 +85,10 @@ extension iOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) extension macOSViewVersion { @available(*, unavailable, message: ".listStyle(.inset) isn't available on macOS 10.15") diff --git a/Sources/ViewTypes/ListWithSidebarStyle.swift b/Sources/ViewTypes/ListWithSidebarStyle.swift index 4d127dfce..9a7a1eae9 100644 --- a/Sources/ViewTypes/ListWithSidebarStyle.swift +++ b/Sources/ViewTypes/ListWithSidebarStyle.swift @@ -44,13 +44,31 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// List { +/// Text("Item 1") +/// Text("Item 2") +/// Text("Item 3") +/// } +/// .listStyle(.sidebar) +/// .introspect(.list(style: .sidebar), on: .visionOS(.v1)) { +/// print(type(of: $0)) // UICollectionView +/// } +/// } +/// } +/// ``` public struct ListWithSidebarStyleType: IntrospectableViewType { public enum Style { case sidebar } } -#if os(iOS) || os(macOS) +#if !os(tvOS) extension IntrospectableViewType where Self == ListWithSidebarStyleType { public static func list(style: Self.Style) -> Self { .init() } } @@ -67,6 +85,10 @@ extension iOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) extension macOSViewVersion { public static let v10_15 = Self(for: .v10_15) diff --git a/Sources/ViewTypes/Map.swift b/Sources/ViewTypes/Map.swift index 891d3b38e..3344e03cf 100644 --- a/Sources/ViewTypes/Map.swift +++ b/Sources/ViewTypes/Map.swift @@ -46,6 +46,21 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 51.507222, longitude: -0.1275), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)) +/// +/// var body: some View { +/// Map(coordinateRegion: $region) +/// .introspect(.map, on: .visionOS(.v1)) { +/// print(type(of: $0)) // MKMapView +/// } +/// } +/// } +/// ``` public struct MapType: IntrospectableViewType {} #if canImport(MapKit) @@ -81,4 +96,8 @@ extension macOSViewVersion { public static let v13 = Self(for: .v13) public static let v14 = Self(for: .v14) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #endif diff --git a/Sources/ViewTypes/NavigationSplitView.swift b/Sources/ViewTypes/NavigationSplitView.swift index 72b0d5647..be8b5eacf 100644 --- a/Sources/ViewTypes/NavigationSplitView.swift +++ b/Sources/ViewTypes/NavigationSplitView.swift @@ -53,6 +53,22 @@ import SwiftUI /// } /// ``` /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// NavigationSplitView { +/// Text("Root") +/// } detail: { +/// Text("Detail") +/// } +/// .introspect(.navigationSplitView, on: .visionOS(.v1)) { +/// print(type(of: $0)) // UISplitViewController +/// } +/// } +/// } +/// ``` public struct NavigationSplitViewType: IntrospectableViewType {} extension IntrospectableViewType where Self == NavigationSplitViewType { @@ -91,6 +107,14 @@ extension tvOSViewVersion { .default.withAncestorSelector(\.navigationController) } } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1, selector: selector) + + private static var selector: IntrospectionSelector { + .default.withAncestorSelector(\.splitViewController) + } +} #elseif canImport(AppKit) extension macOSViewVersion { @available(*, unavailable, message: "NavigationSplitView isn't available on macOS 10.15") diff --git a/Sources/ViewTypes/NavigationStack.swift b/Sources/ViewTypes/NavigationStack.swift index b1f330210..69f82ab1e 100644 --- a/Sources/ViewTypes/NavigationStack.swift +++ b/Sources/ViewTypes/NavigationStack.swift @@ -36,6 +36,20 @@ import SwiftUI /// /// Not available. /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// NavigationStack { +/// Text("Root") +/// } +/// .introspect(.navigationStack, on: .visionOS(.v1)) { +/// print(type(of: $0)) // UINavigationController +/// } +/// } +/// } +/// ``` public struct NavigationStackType: IntrospectableViewType {} extension IntrospectableViewType where Self == NavigationStackType { @@ -74,4 +88,12 @@ extension tvOSViewVersion { .default.withAncestorSelector(\.navigationController) } } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1, selector: selector) + + private static var selector: IntrospectionSelector { + .default.withAncestorSelector(\.navigationController) + } +} #endif diff --git a/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift b/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift index b1355277d..f5fcbbce5 100644 --- a/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift +++ b/Sources/ViewTypes/NavigationViewWithColumnsStyle.swift @@ -49,6 +49,22 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// NavigationView { +/// Text("Root") +/// } +/// .navigationViewStyle(DoubleColumnNavigationViewStyle()) +/// .introspect(.navigationView(style: .columns), on: .visionOS(.v1)) { +/// print(type(of: $0)) // UISplitViewController +/// } +/// } +/// } +/// ``` public struct NavigationViewWithColumnsStyleType: IntrospectableViewType { public enum Style { case columns @@ -83,6 +99,14 @@ extension tvOSViewVersion { + public static let v1 = Self(for: .v1, selector: selector) + + private static var selector: IntrospectionSelector { + .default.withAncestorSelector(\.splitViewController) + } +} #elseif canImport(AppKit) extension macOSViewVersion { public static let v10_15 = Self(for: .v10_15) diff --git a/Sources/ViewTypes/NavigationViewWithStackStyle.swift b/Sources/ViewTypes/NavigationViewWithStackStyle.swift index 1fc7397a4..a2319e8cc 100644 --- a/Sources/ViewTypes/NavigationViewWithStackStyle.swift +++ b/Sources/ViewTypes/NavigationViewWithStackStyle.swift @@ -38,6 +38,21 @@ import SwiftUI /// /// Not available. /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// NavigationView { +/// Text("Root") +/// } +/// .navigationViewStyle(.stack) +/// .introspect(.navigationView(style: .stack), on: .visionOS(.v1)) { +/// print(type(of: $0)) // UINavigationController +/// } +/// } +/// } +/// ``` public struct NavigationViewWithStackStyleType: IntrospectableViewType { public enum Style { case stack @@ -72,4 +87,12 @@ extension tvOSViewVersion { + public static let v1 = Self(for: .v1, selector: selector) + + private static var selector: IntrospectionSelector { + .default.withAncestorSelector(\.navigationController) + } +} #endif diff --git a/Sources/ViewTypes/PageControl.swift b/Sources/ViewTypes/PageControl.swift index aa5f4b6ba..dff49dade 100644 --- a/Sources/ViewTypes/PageControl.swift +++ b/Sources/ViewTypes/PageControl.swift @@ -40,6 +40,22 @@ import SwiftUI /// /// Not available. /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// TabView { +/// Text("Page 1").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red) +/// Text("Page 2").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.blue) +/// } +/// .tabViewStyle(.page(indexDisplayMode: .always)) +/// .introspect(.pageControl, on: .visionOS(.v1)) { +/// print(type(of: $0)) // UIPageControl +/// } +/// } +/// } +/// ``` public struct PageControlType: IntrospectableViewType {} extension IntrospectableViewType where Self == PageControlType { @@ -64,4 +80,8 @@ extension tvOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #endif diff --git a/Sources/ViewTypes/PickerWithMenuStyle.swift b/Sources/ViewTypes/PickerWithMenuStyle.swift index f2c1f0529..17f8b05ed 100644 --- a/Sources/ViewTypes/PickerWithMenuStyle.swift +++ b/Sources/ViewTypes/PickerWithMenuStyle.swift @@ -29,13 +29,17 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// Not available. public struct PickerWithMenuStyleType: IntrospectableViewType { public enum Style { case menu } } -#if os(macOS) +#if !os(iOS) && !os(tvOS) && !os(visionOS) extension IntrospectableViewType where Self == PickerWithMenuStyleType { public static func picker(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/PickerWithSegmentedStyle.swift b/Sources/ViewTypes/PickerWithSegmentedStyle.swift index 3eee45637..c6a83d9bc 100644 --- a/Sources/ViewTypes/PickerWithSegmentedStyle.swift +++ b/Sources/ViewTypes/PickerWithSegmentedStyle.swift @@ -61,6 +61,26 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var selection = "1" +/// +/// var body: some View { +/// Picker("Pick a number", selection: $selection) { +/// Text("1").tag("1") +/// Text("2").tag("2") +/// Text("3").tag("3") +/// } +/// .pickerStyle(.segmented) +/// .introspect(.picker(style: .segmented), on: .visionOS(.v1)) { +/// print(type(of: $0)) // UISegmentedControl +/// } +/// } +/// } +/// ``` public struct PickerWithSegmentedStyleType: IntrospectableViewType { public enum Style { case segmented @@ -87,6 +107,10 @@ extension tvOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) extension macOSViewVersion { public static let v10_15 = Self(for: .v10_15) diff --git a/Sources/ViewTypes/PickerWithWheelStyle.swift b/Sources/ViewTypes/PickerWithWheelStyle.swift index 5b3c702bf..4a826910d 100644 --- a/Sources/ViewTypes/PickerWithWheelStyle.swift +++ b/Sources/ViewTypes/PickerWithWheelStyle.swift @@ -30,13 +30,32 @@ import SwiftUI /// /// Not available. /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var selection = "1" +/// +/// var body: some View { +/// Picker("Pick a number", selection: $selection) { +/// Text("1").tag("1") +/// Text("2").tag("2") +/// Text("3").tag("3") +/// } +/// .pickerStyle(.wheel) +/// .introspect(.picker(style: .wheel), on: .visionOS(.v1)) { +/// print(type(of: $0)) // UIPickerView +/// } +/// } +/// } +/// ``` public struct PickerWithWheelStyleType: IntrospectableViewType { public enum Style { case wheel } } -#if os(iOS) +#if !os(tvOS) && !os(macOS) extension IntrospectableViewType where Self == PickerWithWheelStyleType { public static func picker(style: Self.Style) -> Self { .init() } } @@ -49,5 +68,9 @@ extension iOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #endif #endif diff --git a/Sources/ViewTypes/Popover.swift b/Sources/ViewTypes/Popover.swift index b4b9562ee..bce74f354 100644 --- a/Sources/ViewTypes/Popover.swift +++ b/Sources/ViewTypes/Popover.swift @@ -28,11 +28,28 @@ import SwiftUI /// /// Not available. /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var isPresented = false +/// +/// var body: some View { +/// Button("Present", action: { isPresented = true }) +/// .popover(isPresented: $isPresented) { +/// Button("Dismiss", action: { isPresented = false }) +/// .introspect(.popover, on: .visionOS(.v1)) { +/// print(type(of: $0)) // UIPopoverPresentationController +/// } +/// } +/// } +/// } +/// ``` public struct PopoverType: IntrospectableViewType { public var scope: IntrospectionScope { .ancestor } } -#if os(iOS) +#if !os(tvOS) && !os(macOS) extension IntrospectableViewType where Self == PopoverType { public static var popover: Self { .init() } } @@ -49,5 +66,13 @@ extension iOSViewVersion { .from(UIViewController.self, selector: \.popoverPresentationController) } } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1, selector: selector) + + private static var selector: IntrospectionSelector { + .from(UIViewController.self, selector: \.popoverPresentationController) + } +} #endif #endif diff --git a/Sources/ViewTypes/ProgressViewWithCircularStyle.swift b/Sources/ViewTypes/ProgressViewWithCircularStyle.swift index d237008f8..c88f9d02a 100644 --- a/Sources/ViewTypes/ProgressViewWithCircularStyle.swift +++ b/Sources/ViewTypes/ProgressViewWithCircularStyle.swift @@ -44,6 +44,19 @@ import SwiftUI /// } /// ``` /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// ProgressView(value: 0.5) +/// .progressViewStyle(.circular) +/// .introspect(.progressView(style: .circular), on: .visionOS(.v1)) { +/// print(type(of: $0)) // UIActivityIndicatorView +/// } +/// } +/// } +/// ``` public struct ProgressViewWithCircularStyleType: IntrospectableViewType { public enum Style { case circular @@ -72,6 +85,10 @@ extension tvOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) extension macOSViewVersion { @available(*, unavailable, message: ".progressViewStyle(.circular) isn't available on macOS 10.15") diff --git a/Sources/ViewTypes/ProgressViewWithLinearStyle.swift b/Sources/ViewTypes/ProgressViewWithLinearStyle.swift index 5c667d065..cff927107 100644 --- a/Sources/ViewTypes/ProgressViewWithLinearStyle.swift +++ b/Sources/ViewTypes/ProgressViewWithLinearStyle.swift @@ -44,6 +44,19 @@ import SwiftUI /// } /// ``` /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// ProgressView(value: 0.5) +/// .progressViewStyle(.linear) +/// .introspect(.progressView(style: .linear), on: .visionOS(.v1)) { +/// print(type(of: $0)) // UIProgressView +/// } +/// } +/// } +/// ``` public struct ProgressViewWithLinearStyleType: IntrospectableViewType { public enum Style { case linear @@ -72,6 +85,10 @@ extension tvOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) extension macOSViewVersion { @available(*, unavailable, message: ".progressViewStyle(.linear) isn't available on macOS 10.15") diff --git a/Sources/ViewTypes/ScrollView.swift b/Sources/ViewTypes/ScrollView.swift index 93ef0022f..0bd33e626 100644 --- a/Sources/ViewTypes/ScrollView.swift +++ b/Sources/ViewTypes/ScrollView.swift @@ -47,6 +47,20 @@ import SwiftUI /// } /// ``` /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// ScrollView { +/// Text("Item") +/// } +/// .introspect(.scrollView, on: .visionOS(.v1)) { +/// print(type(of: $0)) // UIScrollView +/// } +/// } +/// } +/// ``` public struct ScrollViewType: IntrospectableViewType {} extension IntrospectableViewType where Self == ScrollViewType { @@ -69,6 +83,10 @@ extension tvOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) extension macOSViewVersion { public static let v10_15 = Self(for: .v10_15) diff --git a/Sources/ViewTypes/SearchField.swift b/Sources/ViewTypes/SearchField.swift index 0bc89d013..fb14f3e57 100644 --- a/Sources/ViewTypes/SearchField.swift +++ b/Sources/ViewTypes/SearchField.swift @@ -44,6 +44,24 @@ import SwiftUI /// /// Not available. /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var searchTerm = "" +/// +/// var body: some View { +/// NavigationView { +/// Text("Root") +/// .searchable(text: $searchTerm) +/// } +/// .navigationViewStyle(.stack) +/// .introspect(.searchField, on: .visionOS(.v1)) { +/// print(type(of: $0)) // UISearchBar +/// } +/// } +/// } +/// ``` public struct SearchFieldType: IntrospectableViewType {} extension IntrospectableViewType where Self == SearchFieldType { @@ -82,4 +100,14 @@ extension tvOSViewVersion { } } } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1, selector: selector) + + private static var selector: IntrospectionSelector { + .from(UINavigationController.self) { + $0.viewIfLoaded?.allDescendants.lazy.compactMap { $0 as? UISearchBar }.first + } + } +} #endif diff --git a/Sources/ViewTypes/SecureField.swift b/Sources/ViewTypes/SecureField.swift index bae630596..29cb34c9c 100644 --- a/Sources/ViewTypes/SecureField.swift +++ b/Sources/ViewTypes/SecureField.swift @@ -46,6 +46,21 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var text = "Lorem ipsum" +/// +/// var body: some View { +/// SecureField("Secure Field", text: $text) +/// .introspect(.secureField, on: .visionOS(.v1)) { +/// print(type(of: $0)) // UISecureField +/// } +/// } +/// } +/// ``` public struct SecureFieldType: IntrospectableViewType {} extension IntrospectableViewType where Self == SecureFieldType { @@ -68,6 +83,10 @@ extension tvOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) extension macOSViewVersion { public static let v10_15 = Self(for: .v10_15) diff --git a/Sources/ViewTypes/Sheet.swift b/Sources/ViewTypes/Sheet.swift index 695e1da4a..a751a9d83 100644 --- a/Sources/ViewTypes/Sheet.swift +++ b/Sources/ViewTypes/Sheet.swift @@ -42,11 +42,28 @@ import SwiftUI /// /// Not available. /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var isPresented = false +/// +/// var body: some View { +/// Button("Present", action: { isPresented = true }) +/// .sheet(isPresented: $isPresented) { +/// Button("Dismiss", action: { isPresented = false }) +/// .introspect(.sheet, on: .visionOS(.v1)) { +/// print(type(of: $0)) // UISheetPresentationController +/// } +/// } +/// } +/// } +/// ``` public struct SheetType: IntrospectableViewType { public var scope: IntrospectionScope { .ancestor } } -#if os(iOS) || os(tvOS) +#if !os(macOS) extension IntrospectableViewType where Self == SheetType { public static var sheet: Self { .init() } } @@ -78,6 +95,15 @@ extension iOSViewVersion { .from(UIViewController.self, selector: \.sheetPresentationController) } } + +@available(iOS 15, *) +extension visionOSViewVersion { + public static let v1 = Self(for: .v1, selector: selector) + + private static var selector: IntrospectionSelector { + .from(UIViewController.self, selector: \.sheetPresentationController) + } +} #endif extension tvOSViewVersion { diff --git a/Sources/ViewTypes/SignInWithAppleButton.swift b/Sources/ViewTypes/SignInWithAppleButton.swift index 4cfcbdd04..a11edaa21 100644 --- a/Sources/ViewTypes/SignInWithAppleButton.swift +++ b/Sources/ViewTypes/SignInWithAppleButton.swift @@ -52,6 +52,23 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// SignInWithAppleButton(.signIn) { request in +/// request.requestedScopes = [.fullName, .email] +/// } onCompletion: { result in +/// // do something with result +/// } +/// .introspect(.signInWithAppleButton, on: .visionOS(.v1)) { +/// print(type(of: $0)) // ASAuthorizationAppleIDButton +/// } +/// } +/// } +/// ``` public struct SignInWithAppleButtonType: IntrospectableViewType {} #if canImport(AuthenticationServices) @@ -87,4 +104,8 @@ extension macOSViewVersion { + public static let v1 = Self(for: .v1) +} #endif diff --git a/Sources/ViewTypes/Slider.swift b/Sources/ViewTypes/Slider.swift index b8fa3455c..1e4b35ff8 100644 --- a/Sources/ViewTypes/Slider.swift +++ b/Sources/ViewTypes/Slider.swift @@ -36,9 +36,12 @@ import SwiftUI /// } /// ``` /// +/// ### visionOS +/// +/// Not available. public struct SliderType: IntrospectableViewType {} -#if !os(tvOS) +#if !os(tvOS) && !os(visionOS) extension IntrospectableViewType where Self == SliderType { public static var slider: Self { .init() } } diff --git a/Sources/ViewTypes/Stepper.swift b/Sources/ViewTypes/Stepper.swift index 8b1e37205..961203512 100644 --- a/Sources/ViewTypes/Stepper.swift +++ b/Sources/ViewTypes/Stepper.swift @@ -36,9 +36,12 @@ import SwiftUI /// } /// ``` /// +/// ### visionOS +/// +/// Not available. public struct StepperType: IntrospectableViewType {} -#if !os(tvOS) +#if !os(tvOS) && !os(visionOS) extension IntrospectableViewType where Self == StepperType { public static var stepper: Self { .init() } } diff --git a/Sources/ViewTypes/TabView.swift b/Sources/ViewTypes/TabView.swift index bb886b406..c201ae491 100644 --- a/Sources/ViewTypes/TabView.swift +++ b/Sources/ViewTypes/TabView.swift @@ -50,8 +50,12 @@ import SwiftUI /// } /// ``` /// +/// ### visionOS +/// +/// Not available. public struct TabViewType: IntrospectableViewType {} +#if !os(visionOS) extension IntrospectableViewType where Self == TabViewType { public static var tabView: Self { .init() } } @@ -89,3 +93,4 @@ extension macOSViewVersion { public static let v14 = Self(for: .v14) } #endif +#endif diff --git a/Sources/ViewTypes/TabViewWithPageStyle.swift b/Sources/ViewTypes/TabViewWithPageStyle.swift index a300a5e89..73080ed8b 100644 --- a/Sources/ViewTypes/TabViewWithPageStyle.swift +++ b/Sources/ViewTypes/TabViewWithPageStyle.swift @@ -40,6 +40,22 @@ import SwiftUI /// /// Not available. /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// TabView { +/// Text("Page 1").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red) +/// Text("Page 2").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.blue) +/// } +/// .tabViewStyle(.page(indexDisplayMode: .always)) +/// .introspect(.tabView(style: .page), on: .visionOS(.v1)) { +/// print(type(of: $0)) // UICollectionView +/// } +/// } +/// } +/// ``` public struct TabViewWithPageStyleType: IntrospectableViewType { public enum Style { case page @@ -69,5 +85,9 @@ extension tvOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #endif #endif diff --git a/Sources/ViewTypes/Table.swift b/Sources/ViewTypes/Table.swift index 134ebb228..1627209e2 100644 --- a/Sources/ViewTypes/Table.swift +++ b/Sources/ViewTypes/Table.swift @@ -70,9 +70,40 @@ import SwiftUI /// } /// ``` /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// struct Purchase: Identifiable { +/// let id = UUID() +/// let price: Decimal +/// } +/// +/// 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")) +/// } +/// } rows: { +/// TableRow(Purchase(price: 20)) +/// TableRow(Purchase(price: 50)) +/// TableRow(Purchase(price: 75)) +/// } +/// .introspect(.table, on: .visionOS(.v1)) { +/// print(type(of: $0)) // UICollectionView +/// } +/// } +/// } +/// ``` public struct TableType: IntrospectableViewType {} -#if os(iOS) || os(macOS) +#if !os(tvOS) extension IntrospectableViewType where Self == TableType { public static var table: Self { .init() } } @@ -88,7 +119,11 @@ extension iOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } -#elseif canImport(AppKit) && !targetEnvironment(macCatalyst) + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} +#elseif canImport(AppKit) extension macOSViewVersion { @available(*, unavailable, message: "Table isn't available on macOS 10.15") public static let v10_15 = Self(for: .v10_15) diff --git a/Sources/ViewTypes/TextEditor.swift b/Sources/ViewTypes/TextEditor.swift index 0991f104b..a49cbe70d 100644 --- a/Sources/ViewTypes/TextEditor.swift +++ b/Sources/ViewTypes/TextEditor.swift @@ -35,6 +35,21 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var text = "Lorem ipsum" +/// +/// var body: some View { +/// TextEditor(text: $text) +/// .introspect(.textEditor, on: .visionOS(.v1)) { +/// print(type(of: $0)) // UITextView +/// } +/// } +/// } +/// ``` public struct TextEditorType: IntrospectableViewType {} #if !os(tvOS) @@ -51,6 +66,10 @@ extension iOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) extension macOSViewVersion { @available(*, unavailable, message: "TextEditor isn't available on macOS 10.15") diff --git a/Sources/ViewTypes/TextField.swift b/Sources/ViewTypes/TextField.swift index 5214f2428..dd9907252 100644 --- a/Sources/ViewTypes/TextField.swift +++ b/Sources/ViewTypes/TextField.swift @@ -46,6 +46,21 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var text = "Lorem ipsum" +/// +/// var body: some View { +/// TextField("Text Field", text: $text) +/// .introspect(.textField, on: .visionOS(.v1)) { +/// print(type(of: $0)) // UITextField +/// } +/// } +/// } +/// ``` public struct TextFieldType: IntrospectableViewType {} extension IntrospectableViewType where Self == TextFieldType { @@ -68,6 +83,10 @@ extension tvOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) extension macOSViewVersion { public static let v10_15 = Self(for: .v10_15) diff --git a/Sources/ViewTypes/TextFieldWithVerticalAxis.swift b/Sources/ViewTypes/TextFieldWithVerticalAxis.swift index d1472bedc..8cf62f9bd 100644 --- a/Sources/ViewTypes/TextFieldWithVerticalAxis.swift +++ b/Sources/ViewTypes/TextFieldWithVerticalAxis.swift @@ -47,6 +47,20 @@ import SwiftUI /// } /// ``` /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var text = "Lorem ipsum" +/// +/// var body: some View { +/// TextField("Text Field", text: $text, axis: .vertical) +/// .introspect(.textField(axis: .vertical), on: .visionOS(.v1)) { +/// print(type(of: $0)) // UITextView +/// } +/// } +/// } +/// ``` public struct TextFieldWithVerticalAxisType: IntrospectableViewType { public enum Axis { case vertical @@ -83,6 +97,10 @@ extension tvOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) extension macOSViewVersion { @available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on macOS 10.15") diff --git a/Sources/ViewTypes/Toggle.swift b/Sources/ViewTypes/Toggle.swift index d61d29a30..0bedff39c 100644 --- a/Sources/ViewTypes/Toggle.swift +++ b/Sources/ViewTypes/Toggle.swift @@ -36,6 +36,20 @@ import SwiftUI /// } /// ``` /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var isOn = false +/// +/// var body: some View { +/// Toggle("Toggle", isOn: $isOn) +/// .introspect(.toggle, on: .visionOS(.v1)) { +/// print(type(of: $0)) // UISwitch +/// } +/// } +/// } +/// ``` public struct ToggleType: IntrospectableViewType {} #if !os(tvOS) @@ -51,6 +65,10 @@ extension iOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) extension macOSViewVersion { public static let v10_15 = Self(for: .v10_15) diff --git a/Sources/ViewTypes/ToggleWithButtonStyle.swift b/Sources/ViewTypes/ToggleWithButtonStyle.swift index 18bc80d6d..f15dcb4c4 100644 --- a/Sources/ViewTypes/ToggleWithButtonStyle.swift +++ b/Sources/ViewTypes/ToggleWithButtonStyle.swift @@ -25,13 +25,17 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// Not available. public struct ToggleWithButtonStyleType: IntrospectableViewType { public enum Style { case button } } -#if os(macOS) +#if !os(iOS) && !os(tvOS) && !os(visionOS) extension IntrospectableViewType where Self == ToggleWithButtonStyleType { public static func toggle(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/ToggleWithCheckboxStyle.swift b/Sources/ViewTypes/ToggleWithCheckboxStyle.swift index e33281bc4..afc93346b 100644 --- a/Sources/ViewTypes/ToggleWithCheckboxStyle.swift +++ b/Sources/ViewTypes/ToggleWithCheckboxStyle.swift @@ -25,13 +25,17 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// Not available. public struct ToggleWithCheckboxStyleType: IntrospectableViewType { public enum Style { case checkbox } } -#if os(macOS) +#if !os(iOS) && !os(tvOS) && !os(visionOS) extension IntrospectableViewType where Self == ToggleWithCheckboxStyleType { public static func toggle(style: Self.Style) -> Self { .init() } } diff --git a/Sources/ViewTypes/ToggleWithSwitchStyle.swift b/Sources/ViewTypes/ToggleWithSwitchStyle.swift index 07d56e249..36c0ebcd6 100644 --- a/Sources/ViewTypes/ToggleWithSwitchStyle.swift +++ b/Sources/ViewTypes/ToggleWithSwitchStyle.swift @@ -37,6 +37,22 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// @State var isOn = false +/// +/// var body: some View { +/// Toggle("Switch", isOn: $isOn) +/// .toggleStyle(.switch) +/// .introspect(.toggle(style: .switch), on: .visionOS(.v1)) { +/// print(type(of: $0)) // UISwitch +/// } +/// } +/// } +/// ``` public struct ToggleWithSwitchStyleType: IntrospectableViewType { public enum Style { case `switch` @@ -56,6 +72,10 @@ extension iOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) extension macOSViewVersion { public static let v10_15 = Self(for: .v10_15) diff --git a/Sources/ViewTypes/VideoPlayer.swift b/Sources/ViewTypes/VideoPlayer.swift index 8e2376485..d91038c42 100644 --- a/Sources/ViewTypes/VideoPlayer.swift +++ b/Sources/ViewTypes/VideoPlayer.swift @@ -40,6 +40,19 @@ import SwiftUI /// } /// } /// ``` +/// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// VideoPlayer(player: AVPlayer(url: URL(string: "https://bit.ly/swswift")!)) +/// .introspect(.videoPlayer, on: .visionOS(.v1)) { +/// print(type(of: $0)) // AVPlayerViewController +/// } +/// } +/// } +/// ``` public struct VideoPlayerType: IntrospectableViewType {} #if canImport(AVKit) @@ -67,6 +80,10 @@ extension tvOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) extension macOSViewVersion { @available(*, unavailable, message: "VideoPlayer isn't available on macOS 10.15") diff --git a/Sources/ViewTypes/View.swift b/Sources/ViewTypes/View.swift index 1a2c8e770..ca2ec07b1 100644 --- a/Sources/ViewTypes/View.swift +++ b/Sources/ViewTypes/View.swift @@ -50,6 +50,21 @@ import SwiftUI /// } /// ``` /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// HStack { +/// Image(systemName: "scribble") +/// Text("Some text") +/// } +/// .introspect(.view, on: .visionOS(.v1)) { +/// print(type(of: $0)) // some subclass of UIView +/// } +/// } +/// } +/// ``` public struct ViewType: IntrospectableViewType {} extension IntrospectableViewType where Self == ViewType { @@ -72,6 +87,10 @@ extension tvOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #elseif canImport(AppKit) extension macOSViewVersion { public static let v10_15 = Self(for: .v10_15) diff --git a/Sources/ViewTypes/ViewController.swift b/Sources/ViewTypes/ViewController.swift index ce1c1be8b..0477064b1 100644 --- a/Sources/ViewTypes/ViewController.swift +++ b/Sources/ViewTypes/ViewController.swift @@ -45,6 +45,24 @@ import SwiftUI /// /// Not available. /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// NavigationView { +/// Text("Root").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red) +/// .introspect(.viewController, on: .visionOS(.v1)) { +/// print(type(of: $0)) // some subclass of UIHostingController +/// } +/// } +/// .navigationViewStyle(.stack) +/// .introspect(.viewController, on: .visionOS(.v1)) { +/// print(type(of: $0)) // UINavigationController +/// } +/// } +/// } +/// ``` public struct ViewControllerType: IntrospectableViewType { public var scope: IntrospectionScope { [.receiver, .ancestor] } } @@ -69,4 +87,8 @@ extension tvOSViewVersion { public static let v16 = Self(for: .v16) public static let v17 = Self(for: .v17) } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1) +} #endif diff --git a/Sources/ViewTypes/Window.swift b/Sources/ViewTypes/Window.swift index bff2767c5..0cb2e2a48 100644 --- a/Sources/ViewTypes/Window.swift +++ b/Sources/ViewTypes/Window.swift @@ -41,6 +41,18 @@ import SwiftUI /// } /// ``` /// +/// ### visionOS +/// +/// ```swift +/// struct ContentView: View { +/// var body: some View { +/// Text("Content") +/// .introspect(.window, on: .visionOS(.v1)) { +/// print(type(of: $0)) // UIWindow +/// } +/// } +/// } +/// ``` public struct WindowType: IntrospectableViewType {} extension IntrospectableViewType where Self == WindowType { @@ -71,6 +83,14 @@ extension tvOSViewVersion { .from(UIView.self, selector: \.window) } } + +extension visionOSViewVersion { + public static let v1 = Self(for: .v1, selector: selector) + + private static var selector: IntrospectionSelector { + .from(UIView.self, selector: \.window) + } +} #elseif canImport(AppKit) extension macOSViewVersion { public static let v10_15 = Self(for: .v10_15, selector: selector) diff --git a/Tests/Tests.xcodeproj/project.pbxproj b/Tests/Tests.xcodeproj/project.pbxproj index 515dc8e6b..03bd41c5f 100644 --- a/Tests/Tests.xcodeproj/project.pbxproj +++ b/Tests/Tests.xcodeproj/project.pbxproj @@ -58,7 +58,6 @@ D534D4DC2A4A596200218BFB /* WindowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D534D4DB2A4A596200218BFB /* WindowTests.swift */; }; D534D4DD2A4A596200218BFB /* WindowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D534D4DB2A4A596200218BFB /* WindowTests.swift */; }; D55F448D2A1FF209003381E4 /* ListTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55F448C2A1FF209003381E4 /* ListTests.swift */; }; - D568532C2A49DBB10039A99F /* SignInWithAppleButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D568532B2A49DBB10039A99F /* SignInWithAppleButtonTests.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 */; }; @@ -165,7 +164,6 @@ D534D4DB2A4A596200218BFB /* WindowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowTests.swift; sourceTree = ""; }; D549D9732A66D876005D4FB5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; D55F448C2A1FF209003381E4 /* ListTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTests.swift; sourceTree = ""; }; - D568532B2A49DBB10039A99F /* SignInWithAppleButtonTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInWithAppleButtonTests.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 = ""; }; @@ -396,7 +394,6 @@ D58119CD2A23A4A70081F853 /* TabViewWithPageStyleTests.swift */, D58119C92A239BAC0081F853 /* TextEditorTests.swift */, D5B67B832A0D318F007D5D9B /* TextFieldTests.swift */, - D568532B2A49DBB10039A99F /* SignInWithAppleButtonTests.swift */, D5AD0D902A114B98003D8DEC /* TextFieldWithVerticalAxisTests.swift */, D58119C72A22AC130081F853 /* ToggleTests.swift */, D575068D2A27D4DC00A628E4 /* ToggleWithButtonStyleTests.swift */, @@ -799,7 +796,6 @@ D57506922A27EE4700A628E4 /* DatePickerWithWheelStyleTests.swift in Sources */, D57506822A27C74600A628E4 /* ListWithInsetGroupedStyleTests.swift in Sources */, D5ADFACE2A4A3482009494FD /* FullScreenCoverTests.swift in Sources */, - D568532C2A49DBB10039A99F /* SignInWithAppleButtonTests.swift in Sources */, D575068A2A27CE7900A628E4 /* FormWithGroupedStyleTests.swift in Sources */, D575067C2A27C24600A628E4 /* ListWithPlainStyleTests.swift in Sources */, D534D4DC2A4A596200218BFB /* WindowTests.swift in Sources */, @@ -944,12 +940,15 @@ PRODUCT_BUNDLE_IDENTIFIER = com.siteline.TestsHostApp; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3,6"; }; name = Debug; }; @@ -1024,12 +1023,15 @@ PRODUCT_BUNDLE_IDENTIFIER = com.siteline.TestsHostApp; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3,6"; }; name = Release; }; @@ -1102,6 +1104,7 @@ SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -1174,6 +1177,7 @@ SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_OPTIMIZATION_LEVEL = "-O"; @@ -1515,6 +1519,7 @@ SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator"; TARGETED_DEVICE_FAMILY = "1,3,2,6"; TVOS_DEPLOYMENT_TARGET = 13.0; + XROS_DEPLOYMENT_TARGET = 1.0; }; name = Debug; }; @@ -1527,6 +1532,7 @@ SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator"; TARGETED_DEVICE_FAMILY = "1,3,2,6"; TVOS_DEPLOYMENT_TARGET = 13.0; + XROS_DEPLOYMENT_TARGET = 1.0; }; name = Release; }; @@ -1609,12 +1615,15 @@ PRODUCT_BUNDLE_IDENTIFIER = com.siteline.TestsHostApp; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx xros xrsimulator"; SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3,6,7"; TVOS_DEPLOYMENT_TARGET = 14.0; }; name = Debug; @@ -1691,12 +1700,15 @@ PRODUCT_BUNDLE_IDENTIFIER = com.siteline.TestsHostApp; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx xros xrsimulator"; SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3,6,7"; TVOS_DEPLOYMENT_TARGET = 14.0; }; name = Release; @@ -1768,11 +1780,15 @@ PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx xros xrsimulator"; + SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_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,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestsHostApp.app/TestsHostApp"; "TEST_HOST[sdk=macosx*]" = ""; TVOS_DEPLOYMENT_TARGET = 14.0; @@ -1839,11 +1855,15 @@ PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx xros xrsimulator"; + SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_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,7"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TestsHostApp.app/TestsHostApp"; "TEST_HOST[sdk=macosx*]" = ""; TVOS_DEPLOYMENT_TARGET = 14.0; diff --git a/Tests/Tests/TestUtils.swift b/Tests/Tests/TestUtils.swift index b2dcd2dd7..50f3b902a 100644 --- a/Tests/Tests/TestUtils.swift +++ b/Tests/Tests/TestUtils.swift @@ -3,7 +3,11 @@ import XCTest #if canImport(UIKit) enum TestUtils { + #if os(visionOS) + private static let window = UIWindow(frame: .init(x: 0, y: 0, width: 800, height: 800)) + #else private static let window = UIWindow(frame: UIScreen.main.bounds) + #endif static func present(view: some View) { window.rootViewController = UIHostingController(rootView: view) diff --git a/Tests/Tests/ViewTypes/ButtonTests.swift b/Tests/Tests/ViewTypes/ButtonTests.swift index acbb3922e..d07dcd962 100644 --- a/Tests/Tests/ViewTypes/ButtonTests.swift +++ b/Tests/Tests/ViewTypes/ButtonTests.swift @@ -1,4 +1,4 @@ -#if os(macOS) +#if !os(iOS) && !os(tvOS) && !os(visionOS) import SwiftUI import SwiftUIIntrospect import XCTest diff --git a/Tests/Tests/ViewTypes/ColorPickerTests.swift b/Tests/Tests/ViewTypes/ColorPickerTests.swift index 0f550a2d5..25375eb68 100644 --- a/Tests/Tests/ViewTypes/ColorPickerTests.swift +++ b/Tests/Tests/ViewTypes/ColorPickerTests.swift @@ -25,22 +25,22 @@ final class ColorPickerTests: XCTestCase { VStack { ColorPicker("", selection: .constant(PlatformColor.red.cgColor)) - #if os(iOS) - .introspect(.colorPicker, on: .iOS(.v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(visionOS) + .introspect(.colorPicker, on: .iOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .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, .v17), customize: spy1) + #if os(iOS) || os(visionOS) + .introspect(.colorPicker, on: .iOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(macOS) .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, .v17), customize: spy2) + #if os(iOS) || os(visionOS) + .introspect(.colorPicker, on: .iOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy2) #elseif os(macOS) .introspect(.colorPicker, on: .macOS(.v11, .v12, .v13, .v14), customize: spy2) #endif diff --git a/Tests/Tests/ViewTypes/DatePickerTests.swift b/Tests/Tests/ViewTypes/DatePickerTests.swift index 2ba777591..f105cd722 100644 --- a/Tests/Tests/ViewTypes/DatePickerTests.swift +++ b/Tests/Tests/ViewTypes/DatePickerTests.swift @@ -1,4 +1,4 @@ -#if os(iOS) || os(macOS) +#if !os(tvOS) import SwiftUI import SwiftUIIntrospect import XCTest @@ -22,24 +22,24 @@ final class DatePickerTests: XCTestCase { VStack { DatePicker("", selection: .constant(date0)) - #if os(iOS) - .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(visionOS) + .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .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, .v17), customize: spy1) + #if os(iOS) || os(visionOS) + .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(macOS) .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, .v17), customize: spy2) + #if os(iOS) || os(visionOS) + .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy2) #elseif os(macOS) .introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif diff --git a/Tests/Tests/ViewTypes/DatePickerWithCompactFieldStyleTests.swift b/Tests/Tests/ViewTypes/DatePickerWithCompactFieldStyleTests.swift index abdea4a48..6ac88b55b 100644 --- a/Tests/Tests/ViewTypes/DatePickerWithCompactFieldStyleTests.swift +++ b/Tests/Tests/ViewTypes/DatePickerWithCompactFieldStyleTests.swift @@ -1,4 +1,4 @@ -#if os(iOS) || os(macOS) +#if !os(tvOS) import SwiftUI import SwiftUIIntrospect import XCTest @@ -28,8 +28,8 @@ final class DatePickerWithCompactStyleTests: XCTestCase { VStack { DatePicker("", selection: .constant(date0)) .datePickerStyle(.compact) - #if os(iOS) - .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(visionOS) + .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13, .v14), customize: spy0) #endif @@ -37,8 +37,8 @@ final class DatePickerWithCompactStyleTests: XCTestCase { DatePicker("", selection: .constant(date1)) .datePickerStyle(.compact) - #if os(iOS) - .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17), customize: spy1) + #if os(iOS) || os(visionOS) + .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(macOS) .introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13, .v14), customize: spy1) #endif @@ -46,8 +46,8 @@ final class DatePickerWithCompactStyleTests: XCTestCase { DatePicker("", selection: .constant(date2)) .datePickerStyle(.compact) - #if os(iOS) - .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17), customize: spy2) + #if os(iOS) || os(visionOS) + .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy2) #elseif os(macOS) .introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13, .v14), customize: spy2) #endif diff --git a/Tests/Tests/ViewTypes/DatePickerWithFieldStyleTests.swift b/Tests/Tests/ViewTypes/DatePickerWithFieldStyleTests.swift index fca60c790..630ceb472 100644 --- a/Tests/Tests/ViewTypes/DatePickerWithFieldStyleTests.swift +++ b/Tests/Tests/ViewTypes/DatePickerWithFieldStyleTests.swift @@ -1,4 +1,4 @@ -#if os(macOS) +#if !os(iOS) && !os(tvOS) && !os(visionOS) import SwiftUI import SwiftUIIntrospect import XCTest diff --git a/Tests/Tests/ViewTypes/DatePickerWithGraphicalStyleTests.swift b/Tests/Tests/ViewTypes/DatePickerWithGraphicalStyleTests.swift index 415692cce..c4a4fbc30 100644 --- a/Tests/Tests/ViewTypes/DatePickerWithGraphicalStyleTests.swift +++ b/Tests/Tests/ViewTypes/DatePickerWithGraphicalStyleTests.swift @@ -1,4 +1,4 @@ -#if os(iOS) || os(macOS) +#if !os(tvOS) import SwiftUI import SwiftUIIntrospect import XCTest @@ -28,8 +28,8 @@ final class DatePickerWithGraphicalStyleTests: XCTestCase { VStack { DatePicker("", selection: .constant(date0)) .datePickerStyle(.graphical) - #if os(iOS) - .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(visionOS) + .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif @@ -37,8 +37,8 @@ final class DatePickerWithGraphicalStyleTests: XCTestCase { DatePicker("", selection: .constant(date1)) .datePickerStyle(.graphical) - #if os(iOS) - .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17), customize: spy1) + #if os(iOS) || os(visionOS) + .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(macOS) .introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) #endif @@ -46,8 +46,8 @@ final class DatePickerWithGraphicalStyleTests: XCTestCase { DatePicker("", selection: .constant(date2)) .datePickerStyle(.graphical) - #if os(iOS) - .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17), customize: spy2) + #if os(iOS) || os(visionOS) + .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy2) #elseif os(macOS) .introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif diff --git a/Tests/Tests/ViewTypes/DatePickerWithStepperFieldStyleTests.swift b/Tests/Tests/ViewTypes/DatePickerWithStepperFieldStyleTests.swift index 1439d2553..b20055616 100644 --- a/Tests/Tests/ViewTypes/DatePickerWithStepperFieldStyleTests.swift +++ b/Tests/Tests/ViewTypes/DatePickerWithStepperFieldStyleTests.swift @@ -1,19 +1,19 @@ -#if os(macOS) +#if !os(iOS) && !os(tvOS) && !os(visionOS) import SwiftUI import SwiftUIIntrospect import XCTest -final class DatePickerWithWheelStyleTests: XCTestCase { +final class DatePickerWithStepperFieldStyleTests: XCTestCase { #if canImport(AppKit) && !targetEnvironment(macCatalyst) - typealias PlatformDatePickerWithWheelStyle = NSDatePicker + typealias PlatformDatePickerWithStepperFieldStyle = NSDatePicker #endif - func testDatePickerWithWheelStyle() { + func testDatePickerWithStepperFieldStyle() { let date0 = Date(timeIntervalSince1970: 0) let date1 = Date(timeIntervalSince1970: 5) let date2 = Date(timeIntervalSince1970: 10) - XCTAssertViewIntrospection(of: PlatformDatePickerWithWheelStyle.self) { spies in + XCTAssertViewIntrospection(of: PlatformDatePickerWithStepperFieldStyle.self) { spies in let spy0 = spies[0] let spy1 = spies[1] let spy2 = spies[2] diff --git a/Tests/Tests/ViewTypes/DatePickerWithWheelStyleTests.swift b/Tests/Tests/ViewTypes/DatePickerWithWheelStyleTests.swift index b7d346198..1f9df82a2 100644 --- a/Tests/Tests/ViewTypes/DatePickerWithWheelStyleTests.swift +++ b/Tests/Tests/ViewTypes/DatePickerWithWheelStyleTests.swift @@ -1,4 +1,4 @@ -#if os(iOS) +#if !os(tvOS) && !os(macOS) import SwiftUI import SwiftUIIntrospect import XCTest @@ -21,22 +21,22 @@ final class DatePickerWithWheelStyleTests: XCTestCase { VStack { DatePicker("", selection: .constant(date0)) .datePickerStyle(.wheel) - #if os(iOS) - .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(visionOS) + .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #endif .cornerRadius(8) DatePicker("", selection: .constant(date1)) .datePickerStyle(.wheel) - #if os(iOS) - .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) + #if os(iOS) || os(visionOS) + .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1) #endif .cornerRadius(8) DatePicker("", selection: .constant(date2)) .datePickerStyle(.wheel) - #if os(iOS) - .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) + #if os(iOS) || os(visionOS) + .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/FormTests.swift b/Tests/Tests/ViewTypes/FormTests.swift index 84a644e0e..b6b5d8527 100644 --- a/Tests/Tests/ViewTypes/FormTests.swift +++ b/Tests/Tests/ViewTypes/FormTests.swift @@ -19,16 +19,16 @@ final class FormTests: XCTestCase { Form { Text("Item 1") } - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) .introspect(.form, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) } - .introspect(.form, on: .iOS(.v16, .v17)) { spy0($0) } + .introspect(.form, on: .iOS(.v16, .v17), .visionOS(.v1)) { spy0($0) } #endif Form { Text("Item 1") - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) .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) } + .introspect(.form, on: .iOS(.v16, .v17), .visionOS(.v1), scope: .ancestor) { spy1($0) } #endif } } diff --git a/Tests/Tests/ViewTypes/FormWithGroupedStyleTests.swift b/Tests/Tests/ViewTypes/FormWithGroupedStyleTests.swift index abbfa3989..90df92085 100644 --- a/Tests/Tests/ViewTypes/FormWithGroupedStyleTests.swift +++ b/Tests/Tests/ViewTypes/FormWithGroupedStyleTests.swift @@ -24,8 +24,8 @@ final class FormWithGroupedStyleTests: XCTestCase { Text("Item 1") } .formStyle(.grouped) - #if os(iOS) || os(tvOS) - .introspect(.form(style: .grouped), on: .iOS(.v16, .v17)) { spy0($0) } + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.form(style: .grouped), on: .iOS(.v16, .v17), .visionOS(.v1)) { spy0($0) } .introspect(.form(style: .grouped), on: .tvOS(.v16, .v17)) { spy0($0) } #elseif os(macOS) .introspect(.form(style: .grouped), on: .macOS(.v13, .v14)) { spy0($0) } @@ -33,8 +33,8 @@ final class FormWithGroupedStyleTests: XCTestCase { Form { Text("Item 1") - #if os(iOS) || os(tvOS) - .introspect(.form(style: .grouped), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) } + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.form(style: .grouped), on: .iOS(.v16, .v17), .visionOS(.v1), 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, .v14), scope: .ancestor) { spy1($0) } diff --git a/Tests/Tests/ViewTypes/FullScreenCoverTests.swift b/Tests/Tests/ViewTypes/FullScreenCoverTests.swift index 3528ad5e5..cb83e292a 100644 --- a/Tests/Tests/ViewTypes/FullScreenCoverTests.swift +++ b/Tests/Tests/ViewTypes/FullScreenCoverTests.swift @@ -1,11 +1,13 @@ -#if os(iOS) || os(tvOS) +#if !os(macOS) import SwiftUI import SwiftUIIntrospect import XCTest final class FullScreenCoverTests: XCTestCase { func testPresentationAsFullScreenCover() throws { - throw XCTSkip("FIXME: this doesn't pass, even though introspection works in the Showcase app") + #if !os(visionOS) + throw XCTSkip("FIXME: this doesn't pass on anything other than visionOS, even though introspection works in the Showcase app") + #endif guard #available(iOS 14, tvOS 14, *) else { throw XCTSkip() @@ -17,10 +19,10 @@ final class FullScreenCoverTests: XCTestCase { Text("Root") .fullScreenCover(isPresented: .constant(true)) { Text("Content") - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) .introspect( .fullScreenCover, - on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), + on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0 ) #endif diff --git a/Tests/Tests/ViewTypes/ListCellTests.swift b/Tests/Tests/ViewTypes/ListCellTests.swift index cb9b1f80b..d6882abf9 100644 --- a/Tests/Tests/ViewTypes/ListCellTests.swift +++ b/Tests/Tests/ViewTypes/ListCellTests.swift @@ -15,9 +15,9 @@ final class ListCellTests: XCTestCase { List { Text("Item 1") - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) .introspect(.listCell, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy($0) } - .introspect(.listCell, on: .iOS(.v16, .v17)) { spy($0) } + .introspect(.listCell, on: .iOS(.v16, .v17), .visionOS(.v1)) { spy($0) } #elseif os(macOS) .introspect(.listCell, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy($0) } #endif @@ -31,9 +31,9 @@ final class ListCellTests: XCTestCase { List { Text("Item 1") - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) .introspect(.listCell, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy($0) } - .introspect(.listCell, on: .iOS(.v16, .v17)) { spy($0) } + .introspect(.listCell, on: .iOS(.v16, .v17), .visionOS(.v1)) { spy($0) } #elseif os(macOS) .introspect(.listCell, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy($0) } #endif diff --git a/Tests/Tests/ViewTypes/ListTests.swift b/Tests/Tests/ViewTypes/ListTests.swift index 642f1344d..1b8da8f07 100644 --- a/Tests/Tests/ViewTypes/ListTests.swift +++ b/Tests/Tests/ViewTypes/ListTests.swift @@ -18,18 +18,18 @@ final class ListTests: XCTestCase { List { Text("Item 1") } - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) } - .introspect(.list, on: .iOS(.v16, .v17)) { spy0($0) } + .introspect(.list, on: .iOS(.v16, .v17), .visionOS(.v1)) { spy0($0) } #elseif os(macOS) .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy0($0) } #endif List { Text("Item 1") - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) .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) } + .introspect(.list, on: .iOS(.v16, .v17), .visionOS(.v1), scope: .ancestor) { spy1($0) } #elseif os(macOS) .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) } #endif @@ -52,14 +52,14 @@ final class ListTests: XCTestCase { List { Text("Item 1") } - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy1($0) } - .introspect(.list, on: .iOS(.v16, .v17)) { spy1($0) } + .introspect(.list, on: .iOS(.v16, .v17), .visionOS(.v1)) { spy1($0) } #endif } - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) } - .introspect(.list, on: .iOS(.v16, .v17)) { spy0($0) } + .introspect(.list, on: .iOS(.v16, .v17), .visionOS(.v1)) { spy0($0) } #endif } extraAssertions: { XCTAssert($0[safe: 0] !== $0[safe: 1]) @@ -76,9 +76,9 @@ final class ListTests: XCTestCase { List { Text("Item 1") } - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) .introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) } - .introspect(.list, on: .iOS(.v16, .v17)) { spy0($0) } + .introspect(.list, on: .iOS(.v16, .v17), .visionOS(.v1)) { spy0($0) } #elseif os(macOS) .introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy0($0) } #endif @@ -88,9 +88,9 @@ final class ListTests: XCTestCase { List { Text("Item 1") - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) .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) } + .introspect(.list, on: .iOS(.v16, .v17), .visionOS(.v1), scope: .ancestor) { spy1($0) } #elseif os(macOS) .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 4a8c29a45..357937c3c 100644 --- a/Tests/Tests/ViewTypes/ListWithBorderedStyleTests.swift +++ b/Tests/Tests/ViewTypes/ListWithBorderedStyleTests.swift @@ -1,4 +1,4 @@ -#if os(macOS) +#if !os(iOS) && !os(tvOS) && !os(visionOS) import SwiftUI import SwiftUIIntrospect import XCTest diff --git a/Tests/Tests/ViewTypes/ListWithGroupedStyleTests.swift b/Tests/Tests/ViewTypes/ListWithGroupedStyleTests.swift index ef09afa35..16793db88 100644 --- a/Tests/Tests/ViewTypes/ListWithGroupedStyleTests.swift +++ b/Tests/Tests/ViewTypes/ListWithGroupedStyleTests.swift @@ -18,16 +18,16 @@ final class ListWithGroupedStyleTests: XCTestCase { Text("Item 1") } .listStyle(.grouped) - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) .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) } + .introspect(.list(style: .grouped), on: .iOS(.v16, .v17), .visionOS(.v1)) { spy0($0) } #endif List { Text("Item 1") - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) .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) } + .introspect(.list(style: .grouped), on: .iOS(.v16, .v17), .visionOS(.v1), scope: .ancestor) { spy1($0) } #endif } .listStyle(.grouped) diff --git a/Tests/Tests/ViewTypes/ListWithInsetGroupedStyleTests.swift b/Tests/Tests/ViewTypes/ListWithInsetGroupedStyleTests.swift index 074a857ba..23a539fa6 100644 --- a/Tests/Tests/ViewTypes/ListWithInsetGroupedStyleTests.swift +++ b/Tests/Tests/ViewTypes/ListWithInsetGroupedStyleTests.swift @@ -1,4 +1,4 @@ -#if os(iOS) +#if !os(tvOS) && !os(macOS) import SwiftUI import SwiftUIIntrospect import XCTest @@ -23,16 +23,16 @@ final class ListWithInsetGroupedStyleTests: XCTestCase { Text("Item 1") } .listStyle(.insetGrouped) - #if os(iOS) + #if os(iOS) || os(visionOS) .introspect(.list(style: .insetGrouped), on: .iOS(.v14, .v15)) { spy0($0) } - .introspect(.list(style: .insetGrouped), on: .iOS(.v16, .v17)) { spy0($0) } + .introspect(.list(style: .insetGrouped), on: .iOS(.v16, .v17), .visionOS(.v1)) { spy0($0) } #endif List { Text("Item 1") - #if os(iOS) + #if os(iOS) || os(visionOS) .introspect(.list(style: .insetGrouped), on: .iOS(.v14, .v15), scope: .ancestor) { spy1($0) } - .introspect(.list(style: .insetGrouped), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) } + .introspect(.list(style: .insetGrouped), on: .iOS(.v16, .v17), .visionOS(.v1), scope: .ancestor) { spy1($0) } #endif } .listStyle(.insetGrouped) diff --git a/Tests/Tests/ViewTypes/ListWithInsetStyleTests.swift b/Tests/Tests/ViewTypes/ListWithInsetStyleTests.swift index f8767cca3..86e1746ab 100644 --- a/Tests/Tests/ViewTypes/ListWithInsetStyleTests.swift +++ b/Tests/Tests/ViewTypes/ListWithInsetStyleTests.swift @@ -25,18 +25,18 @@ final class ListWithInsetStyleTests: XCTestCase { Text("Item 1") } .listStyle(.inset) - #if os(iOS) + #if os(iOS) || os(visionOS) .introspect(.list(style: .inset), on: .iOS(.v14, .v15)) { spy0($0) } - .introspect(.list(style: .inset), on: .iOS(.v16, .v17)) { spy0($0) } + .introspect(.list(style: .inset), on: .iOS(.v16, .v17), .visionOS(.v1)) { spy0($0) } #elseif os(macOS) .introspect(.list(style: .inset), on: .macOS(.v11, .v12, .v13, .v14)) { spy0($0) } #endif List { Text("Item 1") - #if os(iOS) + #if os(iOS) || os(visionOS) .introspect(.list(style: .inset), on: .iOS(.v14, .v15), scope: .ancestor) { spy1($0) } - .introspect(.list(style: .inset), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) } + .introspect(.list(style: .inset), on: .iOS(.v16, .v17), .visionOS(.v1), scope: .ancestor) { spy1($0) } #elseif os(macOS) .introspect(.list(style: .inset), on: .macOS(.v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) } #endif diff --git a/Tests/Tests/ViewTypes/ListWithPlainStyleTests.swift b/Tests/Tests/ViewTypes/ListWithPlainStyleTests.swift index 0c783cf6f..115a5a340 100644 --- a/Tests/Tests/ViewTypes/ListWithPlainStyleTests.swift +++ b/Tests/Tests/ViewTypes/ListWithPlainStyleTests.swift @@ -19,18 +19,18 @@ final class ListWithPlainStyleTests: XCTestCase { Text("Item 1") } .listStyle(.plain) - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) .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) } + .introspect(.list(style: .plain), on: .iOS(.v16, .v17), .visionOS(.v1)) { spy0($0) } #elseif os(macOS) .introspect(.list(style: .plain), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy0($0) } #endif List { Text("Item 1") - #if os(iOS) || os(tvOS) + #if os(iOS) || os(tvOS) || os(visionOS) .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) } + .introspect(.list(style: .plain), on: .iOS(.v16, .v17), .visionOS(.v1), scope: .ancestor) { spy1($0) } #elseif os(macOS) .introspect(.list(style: .plain), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) } #endif diff --git a/Tests/Tests/ViewTypes/ListWithSidebarStyleTests.swift b/Tests/Tests/ViewTypes/ListWithSidebarStyleTests.swift index 3199b9dbc..c45d456fb 100644 --- a/Tests/Tests/ViewTypes/ListWithSidebarStyleTests.swift +++ b/Tests/Tests/ViewTypes/ListWithSidebarStyleTests.swift @@ -25,18 +25,18 @@ final class ListWithSidebarStyleTests: XCTestCase { Text("Item 1") } .listStyle(.sidebar) - #if os(iOS) + #if os(iOS) || os(visionOS) .introspect(.list(style: .sidebar), on: .iOS(.v14, .v15)) { spy0($0) } - .introspect(.list(style: .sidebar), on: .iOS(.v16, .v17)) { spy0($0) } + .introspect(.list(style: .sidebar), on: .iOS(.v16, .v17), .visionOS(.v1)) { spy0($0) } #elseif os(macOS) .introspect(.list(style: .sidebar), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy0($0) } #endif List { Text("Item 1") - #if os(iOS) + #if os(iOS) || os(visionOS) .introspect(.list(style: .sidebar), on: .iOS(.v14, .v15), scope: .ancestor) { spy1($0) } - .introspect(.list(style: .sidebar), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) } + .introspect(.list(style: .sidebar), on: .iOS(.v16, .v17), .visionOS(.v1), scope: .ancestor) { spy1($0) } #elseif os(macOS) .introspect(.list(style: .sidebar), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) } #endif diff --git a/Tests/Tests/ViewTypes/MapTests.swift b/Tests/Tests/ViewTypes/MapTests.swift index ac05532be..2199b4cde 100644 --- a/Tests/Tests/ViewTypes/MapTests.swift +++ b/Tests/Tests/ViewTypes/MapTests.swift @@ -24,21 +24,21 @@ final class MapTests: XCTestCase { Map(coordinateRegion: region) .introspect( .map, - on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .macOS(.v11, .v12, .v13, .v14), + on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .macOS(.v11, .v12, .v13, .v14), .visionOS(.v1), customize: spy0 ) Map(coordinateRegion: region) .introspect( .map, - on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .macOS(.v11, .v12, .v13, .v14), + on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .macOS(.v11, .v12, .v13, .v14), .visionOS(.v1), customize: spy1 ) Map(coordinateRegion: region) .introspect( .map, - on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .macOS(.v11, .v12, .v13, .v14), + on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .macOS(.v11, .v12, .v13, .v14), .visionOS(.v1), customize: spy2 ) } diff --git a/Tests/Tests/ViewTypes/NavigationSplitViewTests.swift b/Tests/Tests/ViewTypes/NavigationSplitViewTests.swift index 39caa25e8..33c8a775d 100644 --- a/Tests/Tests/ViewTypes/NavigationSplitViewTests.swift +++ b/Tests/Tests/ViewTypes/NavigationSplitViewTests.swift @@ -5,7 +5,7 @@ import XCTest @available(iOS 16, tvOS 16, macOS 13, *) final class NavigationSplitViewTests: XCTestCase { - #if canImport(UIKit) && os(iOS) + #if canImport(UIKit) && (os(iOS) || os(visionOS)) typealias PlatformNavigationSplitView = UISplitViewController #elseif canImport(UIKit) && os(tvOS) typealias PlatformNavigationSplitView = UINavigationController @@ -32,8 +32,8 @@ final class NavigationSplitViewTests: XCTestCase { Text("Detail") } } - #if os(iOS) - .introspect(.navigationSplitView, on: .iOS(.v16, .v17), customize: spy) + #if os(iOS) || os(visionOS) + .introspect(.navigationSplitView, on: .iOS(.v16, .v17), .visionOS(.v1), customize: spy) #elseif os(tvOS) .introspect(.navigationSplitView, on: .tvOS(.v16, .v17), customize: spy) #elseif os(macOS) @@ -55,8 +55,8 @@ final class NavigationSplitViewTests: XCTestCase { ZStack { Color.red Text("Sidebar") - #if os(iOS) - .introspect(.navigationSplitView, on: .iOS(.v16, .v17), scope: .ancestor, customize: spy) + #if os(iOS) || os(visionOS) + .introspect(.navigationSplitView, on: .iOS(.v16, .v17), .visionOS(.v1), scope: .ancestor, customize: spy) #elseif os(tvOS) .introspect(.navigationSplitView, on: .tvOS(.v16, .v17), scope: .ancestor, customize: spy) #elseif os(macOS) diff --git a/Tests/Tests/ViewTypes/NavigationStackTests.swift b/Tests/Tests/ViewTypes/NavigationStackTests.swift index f32d4a062..702cbfc6e 100644 --- a/Tests/Tests/ViewTypes/NavigationStackTests.swift +++ b/Tests/Tests/ViewTypes/NavigationStackTests.swift @@ -23,8 +23,8 @@ final class NavigationStackTests: XCTestCase { Text("Something") } } - #if os(iOS) || os(tvOS) - .introspect(.navigationStack, on: .iOS(.v16, .v17), .tvOS(.v16, .v17), customize: spy) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.navigationStack, on: .iOS(.v16, .v17), .tvOS(.v16, .v17), .visionOS(.v1), customize: spy) #endif } } @@ -41,8 +41,8 @@ final class NavigationStackTests: XCTestCase { ZStack { Color.red Text("Something") - #if os(iOS) || os(tvOS) - .introspect(.navigationStack, on: .iOS(.v16, .v17), .tvOS(.v16, .v17), scope: .ancestor, customize: spy) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.navigationStack, on: .iOS(.v16, .v17), .tvOS(.v16, .v17), .visionOS(.v1), scope: .ancestor, customize: spy) #endif } } diff --git a/Tests/Tests/ViewTypes/NavigationViewWithColumnsStyleTests.swift b/Tests/Tests/ViewTypes/NavigationViewWithColumnsStyleTests.swift index b40ac5ddf..a54edd7e7 100644 --- a/Tests/Tests/ViewTypes/NavigationViewWithColumnsStyleTests.swift +++ b/Tests/Tests/ViewTypes/NavigationViewWithColumnsStyleTests.swift @@ -3,7 +3,7 @@ import SwiftUIIntrospect import XCTest final class NavigationViewWithColumnsStyleTests: XCTestCase { - #if canImport(UIKit) && os(iOS) + #if canImport(UIKit) && (os(iOS) || os(visionOS)) typealias PlatformNavigationViewWithColumnsStyle = UISplitViewController #elseif canImport(UIKit) && os(tvOS) typealias PlatformNavigationViewWithColumnsStyle = UINavigationController @@ -22,8 +22,8 @@ final class NavigationViewWithColumnsStyleTests: XCTestCase { } } .navigationViewStyle(DoubleColumnNavigationViewStyle()) - #if os(iOS) - .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy) + #if os(iOS) || os(visionOS) + .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy) #elseif os(tvOS) .introspect(.navigationView(style: .columns), on: .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy) #elseif os(macOS) @@ -40,8 +40,8 @@ final class NavigationViewWithColumnsStyleTests: XCTestCase { ZStack { Color.red Text("Something") - #if os(iOS) - .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor, customize: spy) + #if os(iOS) || os(visionOS) + .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), scope: .ancestor, customize: spy) #elseif os(tvOS) .introspect(.navigationView(style: .columns), on: .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor, customize: spy) #elseif os(macOS) diff --git a/Tests/Tests/ViewTypes/NavigationViewWithStackStyleTests.swift b/Tests/Tests/ViewTypes/NavigationViewWithStackStyleTests.swift index 51635d840..eac7e45a2 100644 --- a/Tests/Tests/ViewTypes/NavigationViewWithStackStyleTests.swift +++ b/Tests/Tests/ViewTypes/NavigationViewWithStackStyleTests.swift @@ -19,8 +19,8 @@ final class NavigationViewWithStackStyleTests: XCTestCase { } } .navigationViewStyle(.stack) - #if os(iOS) || os(tvOS) - .introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy) #endif } } @@ -33,8 +33,8 @@ final class NavigationViewWithStackStyleTests: XCTestCase { ZStack { Color.red Text("Something") - #if os(iOS) || os(tvOS) - .introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor, customize: spy) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), scope: .ancestor, customize: spy) #endif } } diff --git a/Tests/Tests/ViewTypes/PageControlTests.swift b/Tests/Tests/ViewTypes/PageControlTests.swift index ad1b4b3dc..261b3574b 100644 --- a/Tests/Tests/ViewTypes/PageControlTests.swift +++ b/Tests/Tests/ViewTypes/PageControlTests.swift @@ -22,8 +22,8 @@ final class PageControlTests: XCTestCase { Text("Page 2").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.blue) } .tabViewStyle(.page(indexDisplayMode: .always)) - #if os(iOS) || os(tvOS) - .introspect(.pageControl, on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.pageControl, on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy) #endif } } diff --git a/Tests/Tests/ViewTypes/PickerWithMenuStyleTests.swift b/Tests/Tests/ViewTypes/PickerWithMenuStyleTests.swift index d8dc312a3..c0c629621 100644 --- a/Tests/Tests/ViewTypes/PickerWithMenuStyleTests.swift +++ b/Tests/Tests/ViewTypes/PickerWithMenuStyleTests.swift @@ -1,4 +1,4 @@ -#if os(macOS) +#if !os(iOS) && !os(tvOS) && !os(visionOS) import SwiftUI import SwiftUIIntrospect import XCTest diff --git a/Tests/Tests/ViewTypes/PickerWithSegmentedStyleTests.swift b/Tests/Tests/ViewTypes/PickerWithSegmentedStyleTests.swift index 903ab89be..54123e4e7 100644 --- a/Tests/Tests/ViewTypes/PickerWithSegmentedStyleTests.swift +++ b/Tests/Tests/ViewTypes/PickerWithSegmentedStyleTests.swift @@ -20,8 +20,8 @@ final class PickerWithSegmentedStyleTests: XCTestCase { Text("1").tag("1") } .pickerStyle(.segmented) - #if os(iOS) || os(tvOS) - .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif @@ -32,8 +32,8 @@ final class PickerWithSegmentedStyleTests: XCTestCase { Text("2").tag("2") } .pickerStyle(.segmented) - #if os(iOS) || os(tvOS) - .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(macOS) .introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) #endif @@ -45,8 +45,8 @@ final class PickerWithSegmentedStyleTests: XCTestCase { Text("3").tag("3") } .pickerStyle(.segmented) - #if os(iOS) || os(tvOS) - .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy2) #elseif os(macOS) .introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif diff --git a/Tests/Tests/ViewTypes/PickerWithWheelStyleTests.swift b/Tests/Tests/ViewTypes/PickerWithWheelStyleTests.swift index 4efdd61c8..8c05558e7 100644 --- a/Tests/Tests/ViewTypes/PickerWithWheelStyleTests.swift +++ b/Tests/Tests/ViewTypes/PickerWithWheelStyleTests.swift @@ -1,4 +1,4 @@ -#if os(iOS) +#if !os(tvOS) && !os(macOS) import SwiftUI import SwiftUIIntrospect import XCTest @@ -19,8 +19,8 @@ final class PickerWithWheelStyleTests: XCTestCase { Text("1").tag("1") } .pickerStyle(.wheel) - #if os(iOS) - .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(visionOS) + .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #endif .cornerRadius(8) @@ -29,8 +29,8 @@ final class PickerWithWheelStyleTests: XCTestCase { Text("2").tag("2") } .pickerStyle(.wheel) - #if os(iOS) - .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) + #if os(iOS) || os(visionOS) + .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1) #endif .cornerRadius(8) @@ -40,8 +40,8 @@ final class PickerWithWheelStyleTests: XCTestCase { Text("3").tag("3") } .pickerStyle(.wheel) - #if os(iOS) - .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) + #if os(iOS) || os(visionOS) + .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy2) #endif } } extraAssertions: { diff --git a/Tests/Tests/ViewTypes/PopoverTests.swift b/Tests/Tests/ViewTypes/PopoverTests.swift index 71f667abe..272fd6bcb 100644 --- a/Tests/Tests/ViewTypes/PopoverTests.swift +++ b/Tests/Tests/ViewTypes/PopoverTests.swift @@ -1,4 +1,4 @@ -#if os(iOS) +#if !os(tvOS) && !os(macOS) import SwiftUI import SwiftUIIntrospect import XCTest @@ -8,6 +8,9 @@ final class PopoverTests: XCTestCase { if (UIDevice.current.userInterfaceIdiom == .pad) { throw XCTSkip("FIXME: does not pass on iPad, even though it works in Showcase app") } + #if os(visionOS) + throw XCTSkip("FIXME: does not pass on visionOS, even though it works in Showcase app") + #endif XCTAssertViewIntrospection(of: UIPopoverPresentationController.self) { spies in let spy0 = spies[0] @@ -17,7 +20,7 @@ final class PopoverTests: XCTestCase { Text("Popover") .introspect( .popover, - on: .iOS(.v13, .v14, .v15, .v16, .v17), + on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0 ) } diff --git a/Tests/Tests/ViewTypes/ProgressViewWithCircularStyleTests.swift b/Tests/Tests/ViewTypes/ProgressViewWithCircularStyleTests.swift index b5d58baad..4234c9137 100644 --- a/Tests/Tests/ViewTypes/ProgressViewWithCircularStyleTests.swift +++ b/Tests/Tests/ViewTypes/ProgressViewWithCircularStyleTests.swift @@ -22,24 +22,24 @@ final class ProgressViewWithCircularStyleTests: XCTestCase { VStack { ProgressView(value: 0.25) .progressViewStyle(.circular) - #if os(iOS) || os(tvOS) - .introspect(.progressView(style: .circular), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.progressView(style: .circular), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .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, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy1) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.progressView(style: .circular), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(macOS) .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, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy2) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.progressView(style: .circular), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy2) #elseif os(macOS) .introspect(.progressView(style: .circular), on: .macOS(.v11, .v12, .v13, .v14), customize: spy2) #endif diff --git a/Tests/Tests/ViewTypes/ProgressViewWithLinearStyleTests.swift b/Tests/Tests/ViewTypes/ProgressViewWithLinearStyleTests.swift index b1751efdd..9dac3147b 100644 --- a/Tests/Tests/ViewTypes/ProgressViewWithLinearStyleTests.swift +++ b/Tests/Tests/ViewTypes/ProgressViewWithLinearStyleTests.swift @@ -22,24 +22,24 @@ final class ProgressViewWithLinearStyleTests: XCTestCase { VStack { ProgressView(value: 0.25) .progressViewStyle(.linear) - #if os(iOS) || os(tvOS) - .introspect(.progressView(style: .linear), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.progressView(style: .linear), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .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, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy1) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.progressView(style: .linear), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(macOS) .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, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy2) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.progressView(style: .linear), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy2) #elseif os(macOS) .introspect(.progressView(style: .linear), on: .macOS(.v11, .v12, .v13, .v14), customize: spy2) #endif diff --git a/Tests/Tests/ViewTypes/ScrollViewTests.swift b/Tests/Tests/ViewTypes/ScrollViewTests.swift index 5170c42f9..e5befb54a 100644 --- a/Tests/Tests/ViewTypes/ScrollViewTests.swift +++ b/Tests/Tests/ViewTypes/ScrollViewTests.swift @@ -18,16 +18,16 @@ final class ScrollViewTests: XCTestCase { ScrollView(showsIndicators: false) { Text("Item 1") } - #if os(iOS) || os(tvOS) - .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .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, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor, customize: spy1) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), scope: .ancestor, customize: spy1) #elseif os(macOS) .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor, customize: spy1) #endif @@ -60,14 +60,14 @@ final class ScrollViewTests: XCTestCase { ScrollView(showsIndicators: false) { Text("Item 1") } - #if os(iOS) || os(tvOS) - .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(macOS) .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, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif @@ -96,8 +96,8 @@ final class ScrollViewTests: XCTestCase { ScrollView(showsIndicators: false) { Text("Item 1") } - #if os(iOS) || os(tvOS) - .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif @@ -107,8 +107,8 @@ final class ScrollViewTests: XCTestCase { ScrollView(showsIndicators: true) { Text("Item 1") - #if os(iOS) || os(tvOS) - .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor, customize: spy1) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), scope: .ancestor, customize: spy1) #elseif os(macOS) .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 334e1cf96..5e615519e 100644 --- a/Tests/Tests/ViewTypes/SearchFieldTests.swift +++ b/Tests/Tests/ViewTypes/SearchFieldTests.swift @@ -1,4 +1,4 @@ -#if os(iOS) || os(tvOS) +#if !os(macOS) import SwiftUI import SwiftUIIntrospect import XCTest @@ -22,8 +22,8 @@ final class SearchFieldTests: XCTestCase { .searchable(text: .constant("")) } .navigationViewStyle(.stack) - #if os(iOS) || os(tvOS) - .introspect(.searchField, on: .iOS(.v15, .v16, .v17), .tvOS(.v15, .v16, .v17), customize: spy) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.searchField, on: .iOS(.v15, .v16, .v17), .tvOS(.v15, .v16, .v17), .visionOS(.v1), customize: spy) #endif } } @@ -39,8 +39,8 @@ final class SearchFieldTests: XCTestCase { NavigationView { Text("Customized") .searchable(text: .constant("")) - #if os(iOS) || os(tvOS) - .introspect(.searchField, on: .iOS(.v15, .v16, .v17), .tvOS(.v15, .v16, .v17), scope: .ancestor, customize: spy) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.searchField, on: .iOS(.v15, .v16, .v17), .tvOS(.v15, .v16, .v17), .visionOS(.v1), scope: .ancestor, customize: spy) #endif } .navigationViewStyle(.stack) @@ -60,8 +60,8 @@ final class SearchFieldTests: XCTestCase { .searchable(text: .constant("")) } .navigationViewStyle(DoubleColumnNavigationViewStyle()) - #if os(iOS) || os(tvOS) - .introspect(.searchField, on: .iOS(.v15, .v16, .v17), .tvOS(.v15, .v16, .v17), customize: spy) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.searchField, on: .iOS(.v15, .v16, .v17), .tvOS(.v15, .v16, .v17), .visionOS(.v1), 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. @@ -83,8 +83,8 @@ final class SearchFieldTests: XCTestCase { NavigationView { Text("Customized") .searchable(text: .constant("")) - #if os(iOS) || os(tvOS) - .introspect(.searchField, on: .iOS(.v15, .v16, .v17), .tvOS(.v15, .v16, .v17), scope: .ancestor, customize: spy) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.searchField, on: .iOS(.v15, .v16, .v17), .tvOS(.v15, .v16, .v17), .visionOS(.v1), scope: .ancestor, customize: spy) #endif } .navigationViewStyle(DoubleColumnNavigationViewStyle()) diff --git a/Tests/Tests/ViewTypes/SecureFieldTests.swift b/Tests/Tests/ViewTypes/SecureFieldTests.swift index 1319cfdb2..042c47de0 100644 --- a/Tests/Tests/ViewTypes/SecureFieldTests.swift +++ b/Tests/Tests/ViewTypes/SecureFieldTests.swift @@ -17,24 +17,24 @@ final class SecureFieldTests: XCTestCase { VStack { SecureField("", text: .constant("Secure Field 0")) - #if os(iOS) || os(tvOS) - .introspect(.secureField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.secureField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .introspect(.secureField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif .cornerRadius(8) SecureField("", text: .constant("Secure Field 1")) - #if os(iOS) || os(tvOS) - .introspect(.secureField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.secureField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(macOS) .introspect(.secureField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) #endif .cornerRadius(8) SecureField("", text: .constant("Secure Field 2")) - #if os(iOS) || os(tvOS) - .introspect(.secureField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.secureField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy2) #elseif os(macOS) .introspect(.secureField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif @@ -60,22 +60,22 @@ final class SecureFieldTests: XCTestCase { List { SecureField("", text: .constant("Secure Field 0")) - #if os(iOS) || os(tvOS) - .introspect(.secureField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.secureField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .introspect(.secureField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif SecureField("", text: .constant("Secure Field 1")) - #if os(iOS) || os(tvOS) - .introspect(.secureField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.secureField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(macOS) .introspect(.secureField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) #endif SecureField("", text: .constant("Secure Field 2")) - #if os(iOS) || os(tvOS) - .introspect(.secureField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.secureField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy2) #elseif os(macOS) .introspect(.secureField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif diff --git a/Tests/Tests/ViewTypes/SheetTests.swift b/Tests/Tests/ViewTypes/SheetTests.swift index 857a8f751..dd744bd71 100644 --- a/Tests/Tests/ViewTypes/SheetTests.swift +++ b/Tests/Tests/ViewTypes/SheetTests.swift @@ -1,4 +1,4 @@ -#if os(iOS) || os(tvOS) +#if !os(macOS) import SwiftUI import SwiftUIIntrospect import XCTest @@ -58,6 +58,22 @@ final class SheetTests: XCTestCase { } } } + #elseif os(visionOS) + func testSheet() throws { + XCTAssertViewIntrospection(of: UIPresentationController.self) { spies in + let spy0 = spies[0] + + Text("Root") + .sheet(isPresented: .constant(true)) { + Text("Sheet") + .introspect( + .sheet, + on: .visionOS(.v1), + customize: spy0 + ) + } + } + } #endif } #endif diff --git a/Tests/Tests/ViewTypes/SignInWithAppleButtonTests.swift b/Tests/Tests/ViewTypes/SignInWithAppleButtonTests.swift index 3d4ece250..22b440cfc 100644 --- a/Tests/Tests/ViewTypes/SignInWithAppleButtonTests.swift +++ b/Tests/Tests/ViewTypes/SignInWithAppleButtonTests.swift @@ -22,21 +22,21 @@ final class SignInWithAppleButtonTests: XCTestCase { SignInWithAppleButton(.continue, onRequest: { _ in }, onCompletion: { _ in }) .introspect( .signInWithAppleButton, - on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .macOS(.v11, .v12, .v13, .v14), + on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .macOS(.v11, .v12, .v13, .v14), .visionOS(.v1), customize: spy0 ) SignInWithAppleButton(.signIn, onRequest: { _ in }, onCompletion: { _ in }) .introspect( .signInWithAppleButton, - on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .macOS(.v11, .v12, .v13, .v14), + on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .macOS(.v11, .v12, .v13, .v14), .visionOS(.v1), customize: spy1 ) SignInWithAppleButton(.signUp, onRequest: { _ in }, onCompletion: { _ in }) .introspect( .signInWithAppleButton, - on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .macOS(.v11, .v12, .v13, .v14), + on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .macOS(.v11, .v12, .v13, .v14), .visionOS(.v1), customize: spy2 ) } diff --git a/Tests/Tests/ViewTypes/SliderTests.swift b/Tests/Tests/ViewTypes/SliderTests.swift index 7c85709a3..db3200bbe 100644 --- a/Tests/Tests/ViewTypes/SliderTests.swift +++ b/Tests/Tests/ViewTypes/SliderTests.swift @@ -1,4 +1,4 @@ -#if !os(tvOS) +#if !os(tvOS) && !os(visionOS) import SwiftUI import SwiftUIIntrospect import XCTest diff --git a/Tests/Tests/ViewTypes/StepperTests.swift b/Tests/Tests/ViewTypes/StepperTests.swift index af5436f79..e130f1de6 100644 --- a/Tests/Tests/ViewTypes/StepperTests.swift +++ b/Tests/Tests/ViewTypes/StepperTests.swift @@ -1,4 +1,4 @@ -#if !os(tvOS) +#if !os(tvOS) && !os(visionOS) import SwiftUI import SwiftUIIntrospect import XCTest diff --git a/Tests/Tests/ViewTypes/TabViewTests.swift b/Tests/Tests/ViewTypes/TabViewTests.swift index 01851974f..1538f12db 100644 --- a/Tests/Tests/ViewTypes/TabViewTests.swift +++ b/Tests/Tests/ViewTypes/TabViewTests.swift @@ -1,4 +1,4 @@ -#if !LEGACY_MACOS_SDK +#if !os(visionOS) && !LEGACY_MACOS_SDK import SwiftUI import SwiftUIIntrospect import XCTest diff --git a/Tests/Tests/ViewTypes/TabViewWithPageStyleTests.swift b/Tests/Tests/ViewTypes/TabViewWithPageStyleTests.swift index 8d9fa0a22..93d0e160e 100644 --- a/Tests/Tests/ViewTypes/TabViewWithPageStyleTests.swift +++ b/Tests/Tests/ViewTypes/TabViewWithPageStyleTests.swift @@ -22,8 +22,8 @@ final class TabViewWithPageStyleTests: XCTestCase { Text("Page 2").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.blue) } .tabViewStyle(.page) - #if os(iOS) || os(tvOS) - .introspect(.tabView(style: .page), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.tabView(style: .page), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy) #endif } } @@ -38,8 +38,8 @@ final class TabViewWithPageStyleTests: XCTestCase { TabView { Text("Page 1").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red) - #if os(iOS) || os(tvOS) - .introspect(.tabView(style: .page), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), scope: .ancestor, customize: spy) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.tabView(style: .page), on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .visionOS(.v1), scope: .ancestor, customize: spy) #endif Text("Page 2").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.blue) } diff --git a/Tests/Tests/ViewTypes/TableTests.swift b/Tests/Tests/ViewTypes/TableTests.swift index 6a1f3230d..b30967c8e 100644 --- a/Tests/Tests/ViewTypes/TableTests.swift +++ b/Tests/Tests/ViewTypes/TableTests.swift @@ -1,4 +1,4 @@ -#if os(iOS) || os(macOS) +#if !os(tvOS) import SwiftUI import SwiftUIIntrospect import XCTest @@ -23,22 +23,22 @@ final class TableTests: XCTestCase { VStack { TipTable() - #if os(iOS) - .introspect(.table, on: .iOS(.v16, .v17), customize: spy0) + #if os(iOS) || os(visionOS) + .introspect(.table, on: .iOS(.v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .introspect(.table, on: .macOS(.v12, .v13, .v14), customize: spy0) #endif TipTable() - #if os(iOS) - .introspect(.table, on: .iOS(.v16, .v17), customize: spy1) + #if os(iOS) || os(visionOS) + .introspect(.table, on: .iOS(.v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(macOS) .introspect(.table, on: .macOS(.v12, .v13, .v14), customize: spy1) #endif TipTable() - #if os(iOS) - .introspect(.table, on: .iOS(.v16, .v17), customize: spy2) + #if os(iOS) || os(visionOS) + .introspect(.table, on: .iOS(.v16, .v17), .visionOS(.v1), customize: spy2) #elseif os(macOS) .introspect(.table, on: .macOS(.v12, .v13, .v14), customize: spy2) #endif @@ -59,24 +59,24 @@ final class TableTests: XCTestCase { VStack { TipTable() .tableStyle(.inset) - #if os(iOS) - .introspect(.table, on: .iOS(.v16, .v17), customize: spy0) + #if os(iOS) || os(visionOS) + .introspect(.table, on: .iOS(.v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .introspect(.table, on: .macOS(.v12, .v13, .v14), customize: spy0) #endif TipTable() .tableStyle(.inset) - #if os(iOS) - .introspect(.table, on: .iOS(.v16, .v17), customize: spy1) + #if os(iOS) || os(visionOS) + .introspect(.table, on: .iOS(.v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(macOS) .introspect(.table, on: .macOS(.v12, .v13, .v14), customize: spy1) #endif TipTable() .tableStyle(.inset) - #if os(iOS) - .introspect(.table, on: .iOS(.v16, .v17), customize: spy2) + #if os(iOS) || os(visionOS) + .introspect(.table, on: .iOS(.v16, .v17), .visionOS(.v1), customize: spy2) #elseif os(macOS) .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 91243fca3..6eca638df 100644 --- a/Tests/Tests/ViewTypes/TextEditorTests.swift +++ b/Tests/Tests/ViewTypes/TextEditorTests.swift @@ -23,24 +23,24 @@ final class TextEditorTests: XCTestCase { VStack { TextEditor(text: .constant("Text Field 0")) - #if os(iOS) - .introspect(.textEditor, on: .iOS(.v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(visionOS) + .introspect(.textEditor, on: .iOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .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, .v17), customize: spy1) + #if os(iOS) || os(visionOS) + .introspect(.textEditor, on: .iOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(macOS) .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, .v17), customize: spy2) + #if os(iOS) || os(visionOS) + .introspect(.textEditor, on: .iOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy2) #elseif os(macOS) .introspect(.textEditor, on: .macOS(.v11, .v12, .v13, .v14), customize: spy2) #endif diff --git a/Tests/Tests/ViewTypes/TextFieldTests.swift b/Tests/Tests/ViewTypes/TextFieldTests.swift index b68ce7e0c..0e65c99a8 100644 --- a/Tests/Tests/ViewTypes/TextFieldTests.swift +++ b/Tests/Tests/ViewTypes/TextFieldTests.swift @@ -17,24 +17,24 @@ final class TextFieldTests: XCTestCase { VStack { TextField("", text: .constant("Text Field 0")) - #if os(iOS) || os(tvOS) - .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .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, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(macOS) .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, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy2) #elseif os(macOS) .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif @@ -60,22 +60,22 @@ final class TextFieldTests: XCTestCase { List { TextField("", text: .constant("Text Field 0")) - #if os(iOS) || os(tvOS) - .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .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, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(macOS) .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, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy2) #elseif os(macOS) .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif diff --git a/Tests/Tests/ViewTypes/TextFieldWithVerticalAxisTests.swift b/Tests/Tests/ViewTypes/TextFieldWithVerticalAxisTests.swift index d6b008f2a..8289b86d9 100644 --- a/Tests/Tests/ViewTypes/TextFieldWithVerticalAxisTests.swift +++ b/Tests/Tests/ViewTypes/TextFieldWithVerticalAxisTests.swift @@ -5,7 +5,7 @@ import XCTest @available(iOS 16, tvOS 16, macOS 13, *) final class TextFieldWithVerticalAxisTests: XCTestCase { - #if canImport(UIKit) && os(iOS) + #if canImport(UIKit) && (os(iOS) || os(visionOS)) typealias PlatformTextField = UITextView #elseif canImport(UIKit) && os(tvOS) typealias PlatformTextField = UITextField @@ -25,8 +25,8 @@ final class TextFieldWithVerticalAxisTests: XCTestCase { VStack { TextField("", text: .constant("Text Field 1"), axis: .vertical) - #if os(iOS) - .introspect(.textField(axis: .vertical), on: .iOS(.v16, .v17), customize: spy0) + #if os(iOS) || os(visionOS) + .introspect(.textField(axis: .vertical), on: .iOS(.v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(tvOS) .introspect(.textField(axis: .vertical), on: .tvOS(.v16, .v17), customize: spy0) #elseif os(macOS) @@ -35,8 +35,8 @@ final class TextFieldWithVerticalAxisTests: XCTestCase { .cornerRadius(8) TextField("", text: .constant("Text Field 2"), axis: .vertical) - #if os(iOS) - .introspect(.textField(axis: .vertical), on: .iOS(.v16, .v17), customize: spy1) + #if os(iOS) || os(visionOS) + .introspect(.textField(axis: .vertical), on: .iOS(.v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(tvOS) .introspect(.textField(axis: .vertical), on: .tvOS(.v16, .v17), customize: spy1) #elseif os(macOS) @@ -45,8 +45,8 @@ final class TextFieldWithVerticalAxisTests: XCTestCase { .cornerRadius(8) TextField("", text: .constant("Text Field 3"), axis: .vertical) - #if os(iOS) - .introspect(.textField(axis: .vertical), on: .iOS(.v16, .v17), customize: spy2) + #if os(iOS) || os(visionOS) + .introspect(.textField(axis: .vertical), on: .iOS(.v16, .v17), .visionOS(.v1), customize: spy2) #elseif os(tvOS) .introspect(.textField(axis: .vertical), on: .tvOS(.v16, .v17), customize: spy2) #elseif os(macOS) diff --git a/Tests/Tests/ViewTypes/ToggleTests.swift b/Tests/Tests/ViewTypes/ToggleTests.swift index 9278124e6..544fcc520 100644 --- a/Tests/Tests/ViewTypes/ToggleTests.swift +++ b/Tests/Tests/ViewTypes/ToggleTests.swift @@ -18,22 +18,22 @@ final class ToggleTests: XCTestCase { VStack { Toggle("", isOn: .constant(true)) - #if os(iOS) - .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(visionOS) + .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .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, .v17), customize: spy1) + #if os(iOS) || os(visionOS) + .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(macOS) .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, .v17), customize: spy2) + #if os(iOS) || os(visionOS) + .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy2) #elseif os(macOS) .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif diff --git a/Tests/Tests/ViewTypes/ToggleWithButtonStyleTests.swift b/Tests/Tests/ViewTypes/ToggleWithButtonStyleTests.swift index d2f589615..b91d78579 100644 --- a/Tests/Tests/ViewTypes/ToggleWithButtonStyleTests.swift +++ b/Tests/Tests/ViewTypes/ToggleWithButtonStyleTests.swift @@ -1,4 +1,4 @@ -#if os(macOS) +#if !os(iOS) && !os(tvOS) && !os(visionOS) import SwiftUI import SwiftUIIntrospect import XCTest diff --git a/Tests/Tests/ViewTypes/ToggleWithCheckboxStyleTests.swift b/Tests/Tests/ViewTypes/ToggleWithCheckboxStyleTests.swift index cdf83a9c2..0497518d8 100644 --- a/Tests/Tests/ViewTypes/ToggleWithCheckboxStyleTests.swift +++ b/Tests/Tests/ViewTypes/ToggleWithCheckboxStyleTests.swift @@ -1,4 +1,4 @@ -#if os(macOS) +#if !os(iOS) && !os(tvOS) && !os(visionOS) import SwiftUI import SwiftUIIntrospect import XCTest diff --git a/Tests/Tests/ViewTypes/ToggleWithSwitchStyleTests.swift b/Tests/Tests/ViewTypes/ToggleWithSwitchStyleTests.swift index f27b35190..8836e850f 100644 --- a/Tests/Tests/ViewTypes/ToggleWithSwitchStyleTests.swift +++ b/Tests/Tests/ViewTypes/ToggleWithSwitchStyleTests.swift @@ -19,24 +19,24 @@ final class ToggleWithSwitchStyleTests: XCTestCase { VStack { Toggle("", isOn: .constant(true)) .toggleStyle(.switch) - #if os(iOS) - .introspect(.toggle(style: .switch), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(visionOS) + .introspect(.toggle(style: .switch), on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .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, .v17), customize: spy1) + #if os(iOS) || os(visionOS) + .introspect(.toggle(style: .switch), on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(macOS) .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, .v17), customize: spy2) + #if os(iOS) || os(visionOS) + .introspect(.toggle(style: .switch), on: .iOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy2) #elseif os(macOS) .introspect(.toggle(style: .switch), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif diff --git a/Tests/Tests/ViewTypes/VideoPlayerTests.swift b/Tests/Tests/ViewTypes/VideoPlayerTests.swift index 04c5df7fe..9658001e7 100644 --- a/Tests/Tests/ViewTypes/VideoPlayerTests.swift +++ b/Tests/Tests/ViewTypes/VideoPlayerTests.swift @@ -28,22 +28,22 @@ final class VideoPlayerTests: XCTestCase { VStack { VideoPlayer(player: AVPlayer(url: videoURL0)) - #if os(iOS) || os(tvOS) - .introspect(.videoPlayer, on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.videoPlayer, on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .introspect(.videoPlayer, on: .macOS(.v11, .v12, .v13, .v14), customize: spy0) #endif VideoPlayer(player: AVPlayer(url: videoURL1)) - #if os(iOS) || os(tvOS) - .introspect(.videoPlayer, on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy1) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.videoPlayer, on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(macOS) .introspect(.videoPlayer, on: .macOS(.v11, .v12, .v13, .v14), customize: spy1) #endif VideoPlayer(player: AVPlayer(url: videoURL2)) - #if os(iOS) || os(tvOS) - .introspect(.videoPlayer, on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), customize: spy2) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.videoPlayer, on: .iOS(.v14, .v15, .v16, .v17), .tvOS(.v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy2) #elseif os(macOS) .introspect(.videoPlayer, on: .macOS(.v11, .v12, .v13, .v14), customize: spy2) #endif diff --git a/Tests/Tests/ViewTypes/ViewControllerTests.swift b/Tests/Tests/ViewTypes/ViewControllerTests.swift index bdc987b8b..a0a7993b3 100644 --- a/Tests/Tests/ViewTypes/ViewControllerTests.swift +++ b/Tests/Tests/ViewTypes/ViewControllerTests.swift @@ -1,4 +1,4 @@ -#if os(iOS) || os(tvOS) +#if !os(macOS) import SwiftUI import SwiftUIIntrospect import XCTest @@ -15,7 +15,7 @@ final class ViewControllerTests: XCTestCase { Text("Root").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red) .introspect( .viewController, - on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), + on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy2 ) } @@ -26,17 +26,19 @@ final class ViewControllerTests: XCTestCase { } .introspect( .viewController, - on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), + on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1 ) } .introspect( .viewController, - on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), + on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0 ) } extraAssertions: { + #if !os(visionOS) XCTAssert($0[safe: 0] is UITabBarController) + #endif XCTAssert($0[safe: 1] is UINavigationController) XCTAssert(String(describing: $0[safe: 2]).contains("UIHostingController")) XCTAssert($0[safe: 1] === $0[safe: 2]?.parent) diff --git a/Tests/Tests/ViewTypes/ViewTests.swift b/Tests/Tests/ViewTypes/ViewTests.swift index 55fd1e8f8..1dcf065ea 100644 --- a/Tests/Tests/ViewTypes/ViewTests.swift +++ b/Tests/Tests/ViewTypes/ViewTests.swift @@ -11,22 +11,22 @@ final class ViewTests: XCTestCase { VStack(spacing: 10) { Image(systemName: "scribble").resizable().frame(height: 30) - #if os(iOS) || os(tvOS) - .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .introspect(.view, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif Text("Text").frame(height: 40) - #if os(iOS) || os(tvOS) - .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(macOS) .introspect(.view, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) #endif } .padding(10) - #if os(iOS) || os(tvOS) - .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy2) #elseif os(macOS) .introspect(.view, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif diff --git a/Tests/Tests/ViewTypes/WindowTests.swift b/Tests/Tests/ViewTypes/WindowTests.swift index 63cc32438..a6f43ab18 100644 --- a/Tests/Tests/ViewTypes/WindowTests.swift +++ b/Tests/Tests/ViewTypes/WindowTests.swift @@ -17,21 +17,21 @@ final class WindowTests: XCTestCase { VStack { Image(systemName: "scribble") - #if os(iOS) || os(tvOS) - .introspect(.window, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy0) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.window, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy0) #elseif os(macOS) .introspect(.window, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0) #endif Text("Text") - #if os(iOS) || os(tvOS) - .introspect(.window, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy1) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.window, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy1) #elseif os(macOS) .introspect(.window, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1) #endif } - #if os(iOS) || os(tvOS) - .introspect(.window, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), customize: spy2) + #if os(iOS) || os(tvOS) || os(visionOS) + .introspect(.window, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), .visionOS(.v1), customize: spy2) #elseif os(macOS) .introspect(.window, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2) #endif diff --git a/fastlane/Fastfile b/fastlane/Fastfile index e2d9d7752..57c0058a1 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -15,6 +15,9 @@ devices = { 16 => ["Apple TV (16.4)"], 17 => ["Apple TV (17.0)"], }, + "visionos" => { + 1 => ["Apple Vision Pro (1.0)"], + }, } lane :build do |options| From 6b4a5292f9dd18592a03ee768b8eff9a1073a720 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Thu, 17 Aug 2023 23:11:47 +0100 Subject: [PATCH 106/116] [CI] Make macOS and Xcode versions explicit (#332) --- .github/workflows/ci.yml | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index af22bee42..a0a3efe2f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: ci: name: ${{ matrix.platform[0] }} ${{ matrix.platform[1] }} - runs-on: ${{ matrix.os || 'macos-13' }} + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: @@ -50,16 +50,22 @@ jobs: install: true - platform: [ios, 14] runtime: iOS 14.5 - xcode: 14.2 + os: macos-13 + xcode: 14.3.1 install: true - platform: [ios, 15] runtime: iOS 15.5 + os: macos-13 + xcode: 15.0 install: true - platform: [ios, 16] runtime: iOS 16.4 + os: macos-13 xcode: 14.3.1 - platform: [ios, 17] runtime: iOS 17.0 + os: macos-13 + xcode: 15.0 - platform: [tvos, 13] runtime: tvOS 13.4 @@ -68,14 +74,22 @@ jobs: install: true - platform: [tvos, 14] runtime: tvOS 14.5 + os: macos-13 + xcode: 14.3.1 install: true - platform: [tvos, 15] runtime: tvOS 15.4 + os: macos-13 + xcode: 15.0 install: true - platform: [tvos, 16] runtime: tvOS 16.4 + os: macos-13 + xcode: 15.0 - platform: [tvos, 17] runtime: tvOS 17.0 + os: macos-13 + xcode: 15.0 - platform: [macos, 11] runtime: macOS 11 @@ -93,6 +107,8 @@ jobs: # FIXME: this currently hangs on CI # - platform: [visionos, 1] # runtime: visionOS 1.0-beta2 + # os: macos-13 + # xcode: 15.0 # install: true steps: - name: Git Checkout @@ -105,7 +121,7 @@ jobs: github.com/XcodesOrg/xcodes - name: Select Xcode version - run: sudo xcodes select ${{ matrix.xcode || '15.0' }} + run: sudo xcodes select ${{ matrix.xcode }} - if: ${{ matrix.install }} name: Install Required Runtime (${{ matrix.runtime }}) From 1136c9a348329887844de9353107c787becc675b Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 18 Aug 2023 00:56:02 +0100 Subject: [PATCH 107/116] Bump to 0.11.0 (#334) --- CHANGELOG.md | 2 ++ README.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85f150bc6..43c0d1222 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Changelog ## master +## [0.11.0] + - Added: visionOS support (#327) - Infrastructure: run CI tests on iOS & tvOS 17 (#323) diff --git a/README.md b/README.md index 2267af7cb..75c195ddb 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Install ```swift let package = Package( dependencies: [ - .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.10.0"), + .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.11.0"), ], targets: [ .target(name: <#Target Name#>, dependencies: [ From 2c6b476fbc84239716582b684f618e9d9fb991a6 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Wed, 23 Aug 2023 10:00:06 +0100 Subject: [PATCH 108/116] Add GitHub issue templates [skip ci] (#337) --- .github/ISSUE_TEMPLATE/bug_report.yml | 71 +++++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 9 ++++ 2 files changed, 80 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..827f8c14a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,71 @@ +name: Bug Report +description: Something isn't working as expected +labels: [bug] +body: +- type: markdown + attributes: + value: | + Thank you for contributing to SwiftUI Introspect! + + Before you submit your issue, please complete each text area below with the relevant details for your bug, and complete the steps in the checklist. +- type: textarea + attributes: + label: Description + description: | + A short description of the incorrect behavior. + + If you think this issue has been recently introduced and did not occur in an earlier version, please note that. If possible, include the last version that the behavior was correct in addition to your current version. + validations: + required: true +- type: checkboxes + attributes: + label: Checklist + options: + - label: I have read the [README](https://github.com/siteline/swiftui-introspect#swiftui-introspect) before submitting this report. + required: true + - label: This issue hasn't been addressed in an [existing GitHub issue](https://github.com/siteline/swiftui-introspect/issues) or [discussion](https://github.com/siteline/swiftui-introspect/discussions). + required: true +- type: textarea + attributes: + label: Expected behavior + description: Describe what you expected to happen. + validations: + required: false +- type: textarea + attributes: + label: Actual behavior + description: Describe or copy/paste the behavior you observe. + validations: + required: false +- type: textarea + attributes: + label: Steps to reproduce + description: | + Explanation of how to reproduce the incorrect behavior. + + This could include an attached project or link to code that is exhibiting the issue, and/or a screen recording. + placeholder: | + 1. ... + validations: + required: false +- type: input + attributes: + label: Version information + description: The version of SwiftUIIntrospect used to reproduce this issue. + placeholder: "'0.11.0' for example, or a commit hash" +- type: input + attributes: + label: Destination operating system + description: The OS running the SwiftUIIntrospect module. + placeholder: "'iOS 17' for example" +- type: input + attributes: + label: Xcode version information + description: The version of Xcode used to reproduce this issue. + placeholder: "The version displayed from 'Xcode 〉About Xcode'" +- type: textarea + attributes: + label: Swift Compiler version information + description: The version of Swift used to reproduce this issue. + placeholder: Output from 'xcrun swiftc --version' + render: shell diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..ed0d557fc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,9 @@ +blank_issues_enabled: false + +contact_links: + - name: Project Discussion + url: https://github.com/siteline/swiftui-introspect/discussions + about: Q&A, ideas, and more + - name: Documentation + url: https://github.com/siteline/swiftui-introspect#swiftui-introspect + about: Read SwiftUI Introspect's documentation From 7c6ab5c7f4d38ba99d58202db980b19e896f68f8 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Thu, 24 Aug 2023 12:53:20 +0100 Subject: [PATCH 109/116] Add SwiftUI Preview for Showcase (#338) --- Examples/Showcase/Showcase/App.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Examples/Showcase/Showcase/App.swift b/Examples/Showcase/Showcase/App.swift index d4ad38a6f..1a772f53b 100644 --- a/Examples/Showcase/Showcase/App.swift +++ b/Examples/Showcase/Showcase/App.swift @@ -23,3 +23,9 @@ struct App: SwiftUI.App { } } #endif + +#if swift(>=5.9) +#Preview { + AppView() +} +#endif From d9fbb6034890bc5f206397aa96ce0bbf9c35ecf4 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 25 Aug 2023 00:05:48 +0100 Subject: [PATCH 110/116] Fix `@_spi` errors (#339) --- CHANGELOG.md | 2 ++ Sources/Introspect.swift | 29 +++++++++++++---------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43c0d1222..561f686e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Changelog ## master +- Fixed: `@_spi` errors (#339) + ## [0.11.0] - Added: visionOS support (#327) diff --git a/Sources/Introspect.swift b/Sources/Introspect.swift index 272f0e899..58fcae894 100644 --- a/Sources/Introspect.swift +++ b/Sources/Introspect.swift @@ -108,6 +108,17 @@ public protocol PlatformEntity: AnyObject { func isDescendant(of other: Base) -> Bool } +extension PlatformEntity { + @_spi(Internals) + public var ancestor: Base? { nil } + + @_spi(Internals) + public var descendants: [Base] { [] } + + @_spi(Internals) + public func isDescendant(of other: Base) -> Bool { false } +} + extension PlatformEntity { @_spi(Internals) public var ancestors: some Sequence { @@ -196,24 +207,10 @@ extension PlatformViewController: PlatformEntity { #if canImport(UIKit) extension UIPresentationController: PlatformEntity { - @_spi(Internals) - public var ancestor: UIPresentationController? { nil } - - @_spi(Internals) - public var descendants: [UIPresentationController] { [] } - - @_spi(Internals) - public func isDescendant(of other: UIPresentationController) -> Bool { false } + public typealias Base = UIPresentationController } #elseif canImport(AppKit) extension NSWindow: PlatformEntity { - @_spi(Internals) - public var ancestor: NSWindow? { nil } - - @_spi(Internals) - public var descendants: [NSWindow] { [] } - - @_spi(Internals) - public func isDescendant(of other: NSWindow) -> Bool { false } + public typealias Base = NSWindow } #endif From 9da0f9b7bffe96a7c98a0128f1e214f62728a39a Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Fri, 25 Aug 2023 01:21:44 +0100 Subject: [PATCH 111/116] Bump to 0.11.1 (#340) --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 561f686e9..b478488fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Changelog ## master +## [0.11.1] + - Fixed: `@_spi` errors (#339) ## [0.11.0] From 07993a24a0143aa3185c8bfd80feaba2814e2940 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Sun, 27 Aug 2023 13:03:07 +0100 Subject: [PATCH 112/116] Update README.md [skip ci] --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 75c195ddb..1f3a280c5 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,8 @@ ScrollView { ... it will: -- Add `IntrospectionView` as an overlay of `TextField` -- Add `IntrospectionAnchorView` as the background of `TextField`. +- Add `IntrospectionView` as an overlay of `ScrollView` +- Add `IntrospectionAnchorView` as a background of `ScrollView`. - Traverse through all the subviews between both views until a `UIScrollView` instance (if any) is found. > **Warning** From 53333d669ea1946e09a384d86577b933ee81cf78 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 28 Aug 2023 18:50:15 +0100 Subject: [PATCH 113/116] Add `@Weak` property wrapper (#341) --- CHANGELOG.md | 2 + README.md | 63 ++++++++++++++++++++++----- Sources/Weak.swift | 14 ++++++ Tests/Tests.xcodeproj/project.pbxproj | 14 ++++++ Tests/Tests/WeakTests.swift | 56 ++++++++++++++++++++++++ 5 files changed, 137 insertions(+), 12 deletions(-) create mode 100644 Sources/Weak.swift create mode 100644 Tests/Tests/WeakTests.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index b478488fc..f9c414dcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Changelog ## master +- Added: `@Weak` property wrapper (#341) + ## [0.11.1] - Fixed: `@_spi` errors (#339) diff --git a/README.md b/README.md index 1f3a280c5..f11c0115d 100644 --- a/README.md +++ b/README.md @@ -199,14 +199,19 @@ TextField("Text Field", text: <#Binding#>) } ``` -Implement your own selector ---------------------------- +Advanced usage +-------------- + +### Implement your own introspectable type **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`: +In case SwiftUIIntrospect (unlikely) doesn't support the SwiftUI element that you're looking for, you can implement your own introspectable type. + +For example, here's how the library implements the introspectable `TextField` type: ```swift +import SwiftUI @_spi(Advanced) import SwiftUIIntrospect public struct TextFieldType: IntrospectableViewType {} @@ -246,14 +251,48 @@ extension macOSViewVersion { #endif ``` -Releasing ---------- +### Introspect on future platform versions + +By default, introspection applies per specific platform version. This is a sensible default for maximum predictability in regularly maintained codebases, but it's not always a good fit for e.g. library developers who may want to cover as many future platform versions as possible in order to provide the best chance for long-term future functionality of their library without regular maintenance. + +For such cases, SwiftUI Introspect offers range-based platform version predicates behind the Advanced SPI: + +```swift +import SwiftUI +@_spi(Advanced) import SwiftUIIntrospect + +struct ContentView: View { + var body: some View { + ScrollView { + // ... + } + .introspect(.scrollView, on: .iOS(.v13...)) { scrollView in + // ... + } + } +} +``` + +Bear in mind this should be used cautiosly, and with full knowledge that any future OS version might break the expected introspection types unless explicitly available. For instance, if in the example above hypothetically iOS 18 stops using UIScrollView under the hood, the customization closure will never be called on said platform. + +### Keep instances outside the customize closure + +Sometimes, you might need to keep your introspected instance around for longer than the customization closure lifetime. In such cases, `@State` is not a good option because it produces retain cycles. Instead, SwiftUI Introspect offers a `@Weak` property wrapper behind the Advanced SPI: + +```swift +import SwiftUI +@_spi(Advanced) import SwiftUIIntrospect -1. Update changelog with new version -2. PR as 'Bump to X.Y.Z' and merge it -3. Tag new version: +struct ContentView: View { + @Weak var scrollView: UIScrollView? - ```sh - $ git tag X.Y.Z - $ git push origin --tags - ``` + var body: some View { + ScrollView { + // ... + } + .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { scrollView in + self.scrollView = scrollView + } + } +} +``` diff --git a/Sources/Weak.swift b/Sources/Weak.swift new file mode 100644 index 000000000..f8ec57738 --- /dev/null +++ b/Sources/Weak.swift @@ -0,0 +1,14 @@ +@_spi(Advanced) +@propertyWrapper +public final class Weak { + private weak var _wrappedValue: T? + + public var wrappedValue: T? { + get { _wrappedValue } + set { _wrappedValue = newValue } + } + + public init(wrappedValue: T? = nil) { + self._wrappedValue = wrappedValue + } +} diff --git a/Tests/Tests.xcodeproj/project.pbxproj b/Tests/Tests.xcodeproj/project.pbxproj index 03bd41c5f..1924c3532 100644 --- a/Tests/Tests.xcodeproj/project.pbxproj +++ b/Tests/Tests.xcodeproj/project.pbxproj @@ -104,6 +104,12 @@ D58D83462A66C5EF00A203BE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D58D83452A66C5EF00A203BE /* Assets.xcassets */; }; D58D83492A66C5EF00A203BE /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D58D83482A66C5EF00A203BE /* Preview Assets.xcassets */; }; D58D83502A66C67A00A203BE /* TestCases.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58D834F2A66C67A00A203BE /* TestCases.swift */; }; + D591D1122A9CC2FF00AE05E8 /* WeakTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D591D1112A9CC2FF00AE05E8 /* WeakTests.swift */; }; + D591D1132A9CC2FF00AE05E8 /* WeakTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D591D1112A9CC2FF00AE05E8 /* WeakTests.swift */; }; + D591D1142A9CC30B00AE05E8 /* PageControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F26E012A561130001209E6 /* PageControlTests.swift */; }; + D591D1152A9CC30B00AE05E8 /* MapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AAF56E2A502EF000CAFFB6 /* MapTests.swift */; }; + D591D1162A9CC30B00AE05E8 /* ViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F26E032A56E74B001209E6 /* ViewControllerTests.swift */; }; + D591D1172A9CC30B00AE05E8 /* SecureFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57E66F92A6956EB0092F43E /* SecureFieldTests.swift */; }; D5983E7D2A66FD3F00C50953 /* TestCases.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58D834F2A66C67A00A203BE /* TestCases.swift */; }; D5AAF56F2A502EF000CAFFB6 /* MapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AAF56E2A502EF000CAFFB6 /* MapTests.swift */; }; D5AD0D912A114B98003D8DEC /* TextFieldWithVerticalAxisTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AD0D902A114B98003D8DEC /* TextFieldWithVerticalAxisTests.swift */; }; @@ -209,6 +215,7 @@ D58D83452A66C5EF00A203BE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; D58D83482A66C5EF00A203BE /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; D58D834F2A66C67A00A203BE /* TestCases.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestCases.swift; sourceTree = ""; }; + D591D1112A9CC2FF00AE05E8 /* WeakTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakTests.swift; sourceTree = ""; }; D5AAF56E2A502EF000CAFFB6 /* MapTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTests.swift; sourceTree = ""; }; D5AD0D902A114B98003D8DEC /* TextFieldWithVerticalAxisTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldWithVerticalAxisTests.swift; sourceTree = ""; }; D5ADFACB2A4A22AE009494FD /* SheetTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SheetTests.swift; sourceTree = ""; }; @@ -446,6 +453,7 @@ children = ( D5B67B852A0D3193007D5D9B /* ViewTypes */, D5F0BE6729C0DC4900AD95AB /* PlatformVersionTests.swift */, + D591D1112A9CC2FF00AE05E8 /* WeakTests.swift */, D58CE15729C621DD0081BFB0 /* TestUtils.swift */, ); path = Tests; @@ -697,10 +705,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D591D1172A9CC30B00AE05E8 /* SecureFieldTests.swift in Sources */, D5ADFAD42A4A4653009494FD /* FullScreenCoverTests.swift in Sources */, D50E2F5E2A2B9F6600BAFB03 /* ScrollViewTests.swift in Sources */, D50E2F5F2A2B9F6600BAFB03 /* NavigationStackTests.swift in Sources */, D50E2F602A2B9F6600BAFB03 /* DatePickerWithGraphicalStyleTests.swift in Sources */, + D591D1152A9CC30B00AE05E8 /* MapTests.swift in Sources */, + D591D1132A9CC2FF00AE05E8 /* WeakTests.swift in Sources */, D50E2F612A2B9F6600BAFB03 /* DatePickerWithCompactFieldStyleTests.swift in Sources */, D50E2F622A2B9F6600BAFB03 /* ToggleWithCheckboxStyleTests.swift in Sources */, D50E2F632A2B9F6600BAFB03 /* TabViewTests.swift in Sources */, @@ -734,6 +745,7 @@ D50E2F7B2A2B9F6600BAFB03 /* PlatformVersionTests.swift in Sources */, D50E2F7C2A2B9F6600BAFB03 /* TestUtils.swift in Sources */, D50E2F7D2A2B9F6600BAFB03 /* PickerWithSegmentedStyleTests.swift in Sources */, + D591D1142A9CC30B00AE05E8 /* PageControlTests.swift in Sources */, D50E2F7E2A2B9F6600BAFB03 /* TabViewWithPageStyleTests.swift in Sources */, D50E2F7F2A2B9F6600BAFB03 /* DatePickerWithFieldStyleTests.swift in Sources */, D50E2F802A2B9F6600BAFB03 /* TableTests.swift in Sources */, @@ -742,6 +754,7 @@ D5ADFAD62A4A4653009494FD /* VideoPlayerTests.swift in Sources */, D50E2F832A2B9F6600BAFB03 /* ListCellTests.swift in Sources */, D50E2F842A2B9F6600BAFB03 /* SearchFieldTests.swift in Sources */, + D591D1162A9CC30B00AE05E8 /* ViewControllerTests.swift in Sources */, D50E2F852A2B9F6600BAFB03 /* ViewTests.swift in Sources */, D50E2F862A2B9F6600BAFB03 /* ListWithGroupedStyleTests.swift in Sources */, D50E2F872A2B9F6600BAFB03 /* ProgressViewWithCircularStyleTests.swift in Sources */, @@ -804,6 +817,7 @@ D575069E2A27F80E00A628E4 /* ProgressViewWithLinearStyleTests.swift in Sources */, D57506862A27CA4100A628E4 /* ListWithBorderedStyleTests.swift in Sources */, D5F8D5ED2A1E7B490054E9AB /* NavigationViewWithStackStyleTests.swift in Sources */, + D591D1122A9CC2FF00AE05E8 /* WeakTests.swift in Sources */, D57506942A27EED200A628E4 /* DatePickerWithStepperFieldStyleTests.swift in Sources */, D5AD0D912A114B98003D8DEC /* TextFieldWithVerticalAxisTests.swift in Sources */, D58119D02A23A62C0081F853 /* SliderTests.swift in Sources */, diff --git a/Tests/Tests/WeakTests.swift b/Tests/Tests/WeakTests.swift new file mode 100644 index 000000000..a626b29d3 --- /dev/null +++ b/Tests/Tests/WeakTests.swift @@ -0,0 +1,56 @@ +@_spi(Advanced) import SwiftUIIntrospect +import XCTest + +final class WeakTests: XCTestCase { + final class Foo {} + + var strongFoo: Foo? = Foo() + + func testInit_nil() { + @Weak var weakFoo: Foo? + XCTAssertNil(weakFoo) + } + + func testInit_nonNil() { + @Weak var weakFoo: Foo? = strongFoo + XCTAssertIdentical(weakFoo, strongFoo) + } + + func testAssignment_nilToNil() { + @Weak var weakFoo: Foo? + weakFoo = nil + XCTAssertNil(weakFoo) + } + + func testAssignment_nilToNonNil() { + @Weak var weakFoo: Foo? + let otherFoo = Foo() + weakFoo = otherFoo + XCTAssertIdentical(weakFoo, otherFoo) + } + + func testAssignment_nonNilToNil() { + @Weak var weakFoo: Foo? = strongFoo + weakFoo = nil + XCTAssertNil(weakFoo) + } + + func testAssignment_nonNilToNonNil() { + @Weak var weakFoo: Foo? = strongFoo + let otherFoo = Foo() + weakFoo = otherFoo + XCTAssertIdentical(weakFoo, otherFoo) + } + + func testIndirectAssignment_nonNilToNil() { + @Weak var weakFoo: Foo? = strongFoo + strongFoo = nil + XCTAssertNil(weakFoo) + } + + func testIndirectAssignment_nonNilToNonNil() { + @Weak var weakFoo: Foo? = strongFoo + strongFoo = Foo() + XCTAssertNil(weakFoo) + } +} From 51dc4bd0dae12058f850b9efa2264e31aa297ff5 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 28 Aug 2023 19:36:27 +0100 Subject: [PATCH 114/116] Add community projects section to README [skip ci] (#342) --- CHANGELOG.md | 2 ++ README.md | 10 ++++++++++ Tests/Tests.xcodeproj/project.pbxproj | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9c414dcd..8dcf6e6e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ Changelog ## master - Added: `@Weak` property wrapper (#341) +- Documentation: added advanced usage section to README (#341) +- Documentation: added community projects section to README (#342) ## [0.11.1] diff --git a/README.md b/README.md index f11c0115d..58e32b346 100644 --- a/README.md +++ b/README.md @@ -296,3 +296,13 @@ struct ContentView: View { } } ``` + +Community projects +------------------ + +Here's a list of open source libraries powered by the SwiftUI Introspect library: + +- [CustomKeyboardKit](https://github.com/paescebu/CustomKeyboardKit) +- [NavigationTransitions](https://github.com/davdroman/swiftui-navigation-transitions) + +If you're working on a library built on SwiftUI Introspect or know of one, feel free to submit a PR adding it to the list. diff --git a/Tests/Tests.xcodeproj/project.pbxproj b/Tests/Tests.xcodeproj/project.pbxproj index 1924c3532..5d230e4ab 100644 --- a/Tests/Tests.xcodeproj/project.pbxproj +++ b/Tests/Tests.xcodeproj/project.pbxproj @@ -453,8 +453,8 @@ children = ( D5B67B852A0D3193007D5D9B /* ViewTypes */, D5F0BE6729C0DC4900AD95AB /* PlatformVersionTests.swift */, - D591D1112A9CC2FF00AE05E8 /* WeakTests.swift */, D58CE15729C621DD0081BFB0 /* TestUtils.swift */, + D591D1112A9CC2FF00AE05E8 /* WeakTests.swift */, ); path = Tests; sourceTree = ""; From 121c146fe591b1320238d054ae35c81ffa45f45a Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Mon, 28 Aug 2023 23:06:13 +0100 Subject: [PATCH 115/116] Bump to 0.12.0 (#344) --- CHANGELOG.md | 2 ++ README.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8dcf6e6e0..497fe687e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Changelog ## master +## [0.12.0] + - Added: `@Weak` property wrapper (#341) - Documentation: added advanced usage section to README (#341) - Documentation: added community projects section to README (#342) diff --git a/README.md b/README.md index 58e32b346..9d13485e7 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Install ```swift let package = Package( dependencies: [ - .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.11.0"), + .package(url: "https://github.com/siteline/swiftui-introspect", from: "0.12.0"), ], targets: [ .target(name: <#Target Name#>, dependencies: [ From 78b846452d678449f014d6f039c896610d494609 Mon Sep 17 00:00:00 2001 From: David Roman <2538074+davdroman@users.noreply.github.com> Date: Tue, 29 Aug 2023 03:53:59 +0100 Subject: [PATCH 116/116] [CI] Remove min iOS version constraint for UI Tests (#343) --- .github/workflows/ci.yml | 2 +- CHANGELOG.md | 2 + .../xcshareddata/xcschemes/Showcase.xcscheme | 2 +- README.md | 5 +- .../xcschemes/Introspect.xcscheme | 2 +- .../xcschemes/SwiftUIIntrospect.xcscheme | 2 +- Tests/Tests.xcodeproj/project.pbxproj | 68 +++++++++++++++++-- .../LegacySwiftUIIntrospectTests.xcscheme | 11 +-- .../xcschemes/SwiftUIIntrospectTests.xcscheme | 2 +- Tests/UITests/StatusBarStyleUITests.swift | 3 + fastlane/Fastfile | 1 - 11 files changed, 76 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0a3efe2f..e2f5c8c1f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -149,6 +149,6 @@ jobs: name: Run Tests (SwiftUIIntrospect) run: fastlane test platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }} scheme:SwiftUIIntrospectTests configuration:Debug - - if: ${{ matrix.platform[0] == 'ios' && matrix.platform[1] >= '14' && matrix.platform[1] <= '16' }} + - if: ${{ matrix.platform[0] == 'ios' && matrix.platform[1] <= '16' }} name: Run UI Tests (SwiftUIIntrospect) run: fastlane test platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }} scheme:SwiftUIIntrospectUITests configuration:Debug diff --git a/CHANGELOG.md b/CHANGELOG.md index 497fe687e..9d00ca4c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Changelog ## master +- Infrastructure: removed min iOS version constraint for UI Tests (#343) + ## [0.12.0] - Added: `@Weak` property wrapper (#341) diff --git a/Examples/Showcase/Showcase.xcodeproj/xcshareddata/xcschemes/Showcase.xcscheme b/Examples/Showcase/Showcase.xcodeproj/xcshareddata/xcschemes/Showcase.xcscheme index 109f41cd7..1533efdd2 100644 --- a/Examples/Showcase/Showcase.xcodeproj/xcshareddata/xcschemes/Showcase.xcscheme +++ b/Examples/Showcase/Showcase.xcodeproj/xcshareddata/xcschemes/Showcase.xcscheme @@ -1,6 +1,6 @@ **Warning** > diff --git a/SwiftUIIntrospect.xcworkspace/xcshareddata/xcschemes/Introspect.xcscheme b/SwiftUIIntrospect.xcworkspace/xcshareddata/xcschemes/Introspect.xcscheme index 11e22f5d3..d3295bf28 100644 --- a/SwiftUIIntrospect.xcworkspace/xcshareddata/xcschemes/Introspect.xcscheme +++ b/SwiftUIIntrospect.xcworkspace/xcshareddata/xcschemes/Introspect.xcscheme @@ -1,6 +1,6 @@ - - - - diff --git a/Tests/Tests.xcodeproj/xcshareddata/xcschemes/SwiftUIIntrospectTests.xcscheme b/Tests/Tests.xcodeproj/xcshareddata/xcschemes/SwiftUIIntrospectTests.xcscheme index 5368e40e0..ae540df31 100644 --- a/Tests/Tests.xcodeproj/xcshareddata/xcschemes/SwiftUIIntrospectTests.xcscheme +++ b/Tests/Tests.xcodeproj/xcshareddata/xcschemes/SwiftUIIntrospectTests.xcscheme @@ -1,6 +1,6 @@