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 836fb0b6..9cdfc21f 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 9cdfc21f..9e24ab8a 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 37c2961c..49cdd668 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 9ae53d6c..386e735f 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 8f22333c..a42c447a 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 5307afed..484ae55f 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 a0469b6f..ce315d7a 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 1ee04c95..2bd429a5 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 c09dd18a..00000000 --- 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 d58c9758..1995b16b 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 574ee13d..d39f203b 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 484ae55f..a14eb789 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 e7343a33..fd8e437e 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 386e735f..d4abd396 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 a42c447a..ff59be31 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 9e24ab8a..bf17d9f5 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 bf17d9f5..00000000 --- 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 00000000..1837e6e9 --- /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 d43323d2..4f0a9059 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 49cdd668..00000000 --- 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 00000000..dbea1ee8 --- /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 00000000..f57b87d6 --- /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 3bd6eed3..919434a6 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 00000000..88579ae0 --- /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 00000000..eb878970 --- /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 00000000..13613e3e --- /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 00000000..73c00596 --- /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 bf5f7fba..3cfe549d 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 00000000..73c00596 --- /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 00000000..ee95ab7e --- /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 ce315d7a..00000000 --- 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 2bd429a5..00000000 --- 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 5182b2c8..53e3d889 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 35a6f308..00000000 --- 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 1651105e..00000000 --- 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 14ab3211..9c257254 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 baa2bfc4..80c68beb 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 8a729eff..aebc9608 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 15086f80..11e22f5d 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 d2ace3d2..ae375ff0 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 c0701c6d..00000000 --- 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 7c52b636..00000000 --- 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 a14eb789..d999a7ef 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 fd8e437e..daeafd10 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 d4abd396..18d27f0e 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 9bafec63..00000000 --- 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 49e2bd08..00000000 --- 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 dad927ff..00000000 --- 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 687b697c..f7996b87 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 5e0761db..00000000 --- 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 ff59be31..22fa2afa 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 d39f203b..5d715970 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 08bdf02c..00000000 --- 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 bcf76256..00000000 --- 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 07d836dd..e292af6d 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 139f3a77..273a6b6f 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 f45d8f11..00000000 --- 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 1837e6e9..e9ee98d7 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 53e3d889..7cc35d68 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 18d27f0e..b48aa7b5 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 f7996b87..e4d38061 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 22fa2afa..c2f4aa17 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 41efcd5d..8a3da260 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 b48aa7b5..6399e757 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 e4d38061..3f59db94 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 c2f4aa17..c269810c 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 f57b87d6..21c20d3a 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 3cfe549d..24f9c3f6 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 00000000..5670498b --- /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 6399e757..0a797180 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 c269810c..f9bee710 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 0a797180..a9ba68d3 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 3f59db94..dc0de137 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 1995b16b..b2a856dc 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 8a3da260..932e6991 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 00000000..0341aea2 --- /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 00000000..e086031d --- /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 b2a856dc..bc42ddec 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 273a6b6f..00000000 --- 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 932e6991..f7e9dbf6 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 7cc35d68..a03922c6 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 d999a7ef..bb264805 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 daeafd10..bbf6f06d 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 f7e9dbf6..e3982161 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 a03922c6..a75dab37 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 bb264805..236abae9 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 bbf6f06d..4d8f1d34 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 236abae9..9d9765fe 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 4d8f1d34..a0e20d7b 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 e3982161..d53e373a 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 a75dab37..9b9eff46 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 a9ba68d3..56f2fcc0 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 f9bee710..c4b0b512 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 00000000..293791c8 --- /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 e9ee98d7..49e4f0e8 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 0341aea2..00000000 --- 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 e086031d..00000000 --- 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 9b9eff46..5344f47e 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 bc42ddec..7e710dad 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 d53e373a..03194f1b 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 49e4f0e8..fb6c3a37 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 c4b0b512..22a7417a 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 e292af6d..90d8c25f 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 03194f1b..9d1eacce 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 5d715970..0204e00c 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 7e710dad..c02e48d2 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 cbc75626..09e8b36e 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 56f2fcc0..b3393403 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 dc0de137..5a14e78c 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 22a7417a..64ecdf0f 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 9d1eacce..0a7025e6 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 5344f47e..ba45cbe2 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 0a7025e6..6bcf7f06 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 293791c8..9bd0fef3 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 fb6c3a37..77050d49 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 21c20d3a..08f01b9d 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 80c68beb..109f41cd 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 88579ae0..f9cecf9f 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 eb878970..00000000 --- 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 13613e3e..00000000 --- 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 73c00596..00000000 --- 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 24f9c3f6..dafb1518 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 5670498b..580bb9fb 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 73c00596..00000000 --- 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 9c257254..b3695494 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 aebc9608..825b5171 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 0204e00c..1e308616 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 00000000..4b1cf8b1 --- /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 c02e48d2..93d804f0 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 00000000..08012ba8 --- /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 00000000..4f413984 --- /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 00000000..29970071 --- /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 00000000..332ef5ff --- /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 00000000..a89c412f --- /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 00000000..09f883d8 --- /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 00000000..bee24769 --- /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 00000000..943cd75d --- /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 00000000..0f9e37ae --- /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 00000000..b6d9a97c --- /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 00000000..a5d04ac7 --- /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 00000000..7ef7eab1 --- /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 00000000..690748f3 --- /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 00000000..991ac172 --- /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 00000000..cf7a70f9 --- /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 00000000..a8b333da --- /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 00000000..a3bbdb1c --- /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 00000000..8f5e3de5 --- /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 00000000..4136a1af --- /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 00000000..b96fab04 --- /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 00000000..b40d282d --- /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 00000000..9a09fd6b --- /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 00000000..6820bf7b --- /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 00000000..b0b2b597 --- /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 00000000..7aa1028e --- /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 00000000..41231a01 --- /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 00000000..b6e1083d --- /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 00000000..b8e98c60 --- /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 00000000..e8f93d6c --- /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 00000000..aa97a11f --- /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 00000000..38f017bc --- /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 00000000..9a56d078 --- /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 00000000..edd6cf7b --- /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 00000000..0b7a06d2 --- /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 00000000..2c7d6e17 --- /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 00000000..d4d952a6 --- /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 00000000..1f16a7fc --- /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 00000000..0c904eee --- /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 00000000..4c6aefda --- /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 00000000..629d146c --- /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 00000000..d95d65b3 --- /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 00000000..579a1162 --- /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 00000000..27a0012c --- /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 00000000..b530bcd5 --- /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 00000000..bb2e01eb --- /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 00000000..f4ca1887 --- /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 00000000..c571e4e9 --- /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 00000000..cf3fa7c8 --- /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 00000000..a7008509 --- /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 00000000..a5363612 --- /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 00000000..919434a6 --- /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 00000000..18d98100 --- /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 00000000..5368e40e --- /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 00000000..a41c1330 --- /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 00000000..79a8cf60 --- /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 00000000..4bdbb063 --- /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 00000000..87d5d263 --- /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 00000000..e2ba2934 --- /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 00000000..9a8e3bb8 --- /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 00000000..a8824f67 --- /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 00000000..04c94125 --- /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 00000000..d7ce6895 --- /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 00000000..7285072d --- /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 00000000..bca2770a --- /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 00000000..6451e1a9 --- /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 00000000..b512f956 --- /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 00000000..f196abc8 --- /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 00000000..0aede8f8 --- /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 00000000..ee388fe8 --- /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 00000000..90f0389f --- /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 00000000..e7132b82 --- /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 00000000..87fb2675 --- /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 00000000..b112e95f --- /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 00000000..07cb69a8 --- /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 00000000..a37bb830 --- /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 00000000..2b35df7f --- /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 00000000..6e89763f --- /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 00000000..ae17cbac --- /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 00000000..ae9a97c0 --- /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 00000000..5915c6f3 --- /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 00000000..e990ea77 --- /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 00000000..6041a848 --- /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 00000000..682bf711 --- /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 00000000..b847aab9 --- /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 00000000..32214643 --- /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 00000000..211f8346 --- /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 00000000..c17e28aa --- /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 00000000..51ac0893 --- /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 00000000..7b55c03d --- /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 00000000..b89047d3 --- /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 00000000..b5126db4 --- /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 00000000..cd877fad --- /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 00000000..8ffb641a --- /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 00000000..9636d935 --- /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 00000000..855ebf3f --- /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 00000000..a1daddfb --- /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 00000000..51145970 --- /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 00000000..02cd7016 --- /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 00000000..a8e06508 --- /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 90d8c25f..0c35de1e 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 6bcf7f06..45cbfe48 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 a8e06508..8d3cf763 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 08012ba8..f78bec6c 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 29970071..28cacbab 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 a89c412f..0863186f 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 00000000..d6df9352 --- /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 f78bec6c..d0979fe3 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 00000000..ea691aa6 --- /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 28cacbab..09650986 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 09f883d8..9684a552 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 dafb1518..70032fcd 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 d0979fe3..953f999d 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 0b7a06d2..1ee82d7c 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 00000000..226f5788 --- /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 a5363612..5d95579c 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 00000000..98045023 --- /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 79a8cf60..1570ad9a 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 07cb69a8..77ff2589 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 2b35df7f..5902b789 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 b847aab9..b1883565 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 51145970..4cf9a3ac 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 02cd7016..050f4177 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 0c35de1e..987b4c2d 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 45cbfe48..ae5c2473 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 cf3fa7c8..d947ab79 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 ae5c2473..fc11223c 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 ea691aa6..742ed132 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 09650986..ea969090 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 b0b2b597..a7eec207 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 7aa1028e..a8917609 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 41231a01..e8e8d3af 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 b6e1083d..4507dddd 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 1f16a7fc..ae229110 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 fc11223c..f9c8d0fc 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 70032fcd..6d00095b 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 332ef5ff..194b5974 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 5cb94cf2..71810864 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 943cd75d..97f65471 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 0f9e37ae..bd4ac0c0 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 b6d9a97c..efc36cf7 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 a5d04ac7..85acc646 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 7ef7eab1..f8a328fe 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 690748f3..8d9236c3 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 991ac172..ae84ab17 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 cf7a70f9..f38bae28 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 a8b333da..09800f94 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 a3bbdb1c..74f54b51 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 8f5e3de5..396f65d3 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 4136a1af..f64ffc2b 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 b96fab04..7661b5e9 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 b40d282d..5d6f2c3d 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 9a09fd6b..2e33b2e5 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 6820bf7b..8a641278 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 a7eec207..bd669a0a 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 a8917609..0cc431f9 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 e8e8d3af..b31bf334 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 4507dddd..798a2d22 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 b8e98c60..9ff617f8 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 e8f93d6c..9a85f634 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 aa97a11f..b6de6d08 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 38f017bc..1a7f988c 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 edd6cf7b..91711857 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 1ee82d7c..13ade58a 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 2c7d6e17..6c5fd01f 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 d4d952a6..da2ea55c 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 ae229110..10012170 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 0c904eee..9bf1136e 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 4c6aefda..5534c4b1 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 629d146c..b451ae0f 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 d95d65b3..ae5c750c 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 579a1162..b0e29e3f 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 27a0012c..dfd76331 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 b530bcd5..cf77f860 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 bb2e01eb..1fcf049f 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 f4ca1887..494c62a5 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 c571e4e9..2415e8cd 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 a41c1330..0c736fa3 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 4bdbb063..acbb3922 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 87d5d263..0f550a2d 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 e2ba2934..2ba77759 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 9a8e3bb8..abdea4a4 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 a8824f67..fca60c79 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 04c94125..415692cc 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 d7ce6895..1439d255 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 7285072d..b7d34619 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 bca2770a..84a644e0 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 6451e1a9..abbfa398 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 b512f956..cb9b1f80 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 f196abc8..642f1344 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 0aede8f8..4a8c29a4 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 ee388fe8..ef09afa3 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 90f0389f..074a857b 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 e7132b82..f8767cca 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 87fb2675..0c783cf6 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 b112e95f..3199b9db 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 77ff2589..ddeff1d5 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 a37bb830..f32d4a06 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 5902b789..b40ac5dd 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 6e89763f..51635d84 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 ae17cbac..d8dc312a 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 ae9a97c0..903ab89b 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 5915c6f3..4efdd61c 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 e990ea77..b5d58baa 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 6041a848..b1751efd 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 682bf711..5170c42f 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 b1883565..334e1cf9 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 32214643..7c85709a 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 211f8346..af5436f7 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 c17e28aa..01851974 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 51ac0893..eac652c0 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 7b55c03d..6a1f3230 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 b89047d3..91243fca 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 b5126db4..b68ce7e0 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 cd877fad..d6b008f2 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 8ffb641a..9278124e 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 9636d935..d2f58961 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 855ebf3f..cdf83a9c 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 a1daddfb..f27b3519 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 4cf9a3ac..133029b4 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 8d3cf763..fd87e48c 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 b3393403..ca8ea4a9 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 64ecdf0f..dcb0bfcb 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 f9c8d0fc..e6d9da74 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 953f999d..cd699d7d 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 742ed132..3b93efa1 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 ea969090..681666c7 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 00000000..7c1fc017 --- /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 d6df9352..00000000 --- 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 77050d49..c308fa8c 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 cd699d7d..245280a6 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 e6d9da74..39c16be0 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 c308fa8c..f56e5d0a 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 00000000..030c0894 --- /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 f56e5d0a..312fc6f2 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 580bb9fb..ae85f5c1 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 71810864..108ba6f3 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 97f65471..d1970d54 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 bd4ac0c0..e9a48f42 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 efc36cf7..97276605 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 85acc646..b47f1930 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 f8a328fe..b292ce61 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 8d9236c3..911fc2c9 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 ae84ab17..6f3ced38 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 f38bae28..16cbd2e7 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 09800f94..3a35b76d 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 74f54b51..8badfb0e 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 396f65d3..f1f9e662 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 f64ffc2b..252e7549 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 7661b5e9..2ebdbeaa 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 5d6f2c3d..5e26727e 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 2e33b2e5..62269d35 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 8a641278..fede61ff 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 bd669a0a..5673b09d 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 0cc431f9..d4ecfad2 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 b31bf334..c21639ca 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 798a2d22..c83d357f 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 9ff617f8..dc804df7 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 9a85f634..20ecfecd 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 b6de6d08..587f2d41 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 1a7f988c..0f4bcae9 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 093f26c0..d9c2dd36 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 91711857..b58e79c9 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 13ade58a..8e381648 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 6c5fd01f..16747546 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 da2ea55c..61b584e9 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 10012170..5966130e 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 9bf1136e..62cefffb 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 5534c4b1..ea002aff 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 b451ae0f..b7c81c06 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 ae5c750c..ae6d0f11 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 b0e29e3f..983a8dfc 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 dfd76331..335c3c5d 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 cf77f860..0851d608 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 1fcf049f..11098bc4 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 494c62a5..b4978014 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 2415e8cd..19bc86fe 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 fd87e48c..8abed591 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 39c16be0..ea4cd5d8 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 8abed591..451683e9 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 312fc6f2..521efe83 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 ea4cd5d8..be65f821 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 245280a6..02c25536 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 be65f821..d62a6b96 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 1e308616..e5f66828 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 4b1cf8b1..25ea09e1 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 108ba6f3..36793a09 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 d1970d54..31f69024 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 e9a48f42..db199aea 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 97276605..6769cd2f 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 b47f1930..6aea42ec 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 b292ce61..ddfae0c6 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 911fc2c9..3cc3d9e8 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 6f3ced38..d87d4aba 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 16cbd2e7..4ba05620 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 3a35b76d..6f38a31d 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 8badfb0e..e5d0facc 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 f1f9e662..fe7aa088 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 252e7549..2c196d23 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 2ebdbeaa..459e264c 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 5e26727e..63e6a289 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 62269d35..ee08aa03 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 fede61ff..4d127dfc 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 5673b09d..72b0d564 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 d4ecfad2..b1f33021 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 c21639ca..b1355277 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 c83d357f..1fc7397a 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 dc804df7..f2c1f052 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 20ecfecd..3eee4563 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 587f2d41..5b3c702b 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 0f4bcae9..d237008f 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 d9c2dd36..5c667d06 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 b58e79c9..93ef0022 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 8e381648..0bc89d01 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 16747546..b8fa3455 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 61b584e9..8b1e3720 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 5966130e..bb886b40 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 62cefffb..30e028b7 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 ea002aff..134ebb22 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 b7c81c06..0991f104 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 ae6d0f11..5214f242 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 983a8dfc..d1472bed 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 335c3c5d..d61d29a3 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 0851d608..18bc80d6 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 11098bc4..e33281bc 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 b4978014..07d56e24 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 19bc86fe..e299dc37 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 d62a6b96..9918ef53 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 00000000..8e237648 --- /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 5d95579c..bd665b04 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 00000000..04c5df7f --- /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 451683e9..d9775bbe 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 00000000..4cfcbdd0 --- /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 bd665b04..b595b865 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 00000000..3d4ece25 --- /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 d9775bbe..d031f67c 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 9918ef53..9d8ec54e 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 6d00095b..2451325f 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 02c25536..cabb10b3 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 681666c7..6842003c 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 e299dc37..1a2c8e77 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 133029b4..55fd1e8f 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 9d8ec54e..b9785c96 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 2451325f..01b375b5 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 cabb10b3..47f8a712 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 00000000..0243b41c --- /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 00000000..cf8e19c2 --- /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 00000000..af769e52 --- /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 b595b865..eeb4ce5e 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 00000000..3528ad5e --- /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 00000000..71f667ab --- /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 00000000..857a8f75 --- /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 d031f67c..ddd2b742 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 b9785c96..37b533af 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 08f01b9d..171bccb9 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 f9cecf9f..803adc61 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 01b375b5..75d37033 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 47f8a712..e928c643 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 00000000..bff2767c --- /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 eeb4ce5e..0c191f36 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 00000000..63cc3243 --- /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 ddd2b742..5773dde9 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 93d804f0..a46f6ea1 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 00000000..37cf9973 --- /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 5773dde9..00000000 --- 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 37b533af..b2415aa3 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 ca8ea4a9..6ff3627a 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 b2415aa3..f8413e47 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 e928c643..19008a0e 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 4f413984..5424c602 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 f8413e47..03a1488a 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 a46f6ea1..b6b0627a 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 521efe83..b3b9f47e 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 00000000..419f146e --- /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 b3b9f47e..b492e0f1 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 b6b0627a..8d40e078 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 b492e0f1..fccaa0da 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 ddeff1d5..39caa25e 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 030c0894..c139fb9c 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 8d40e078..0f2265be 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 03a1488a..5a829a57 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 19008a0e..4c1be9a6 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 194b5974..ad085d1c 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 9684a552..5190340a 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 0c191f36..1c1de178 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 0c736fa3..00000000 --- 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 00000000..7a0bb953 --- /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 5a829a57..e4bef3ed 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 00000000..891d3b38 --- /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 1c1de178..193d1c51 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 00000000..ac05532b --- /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 0243b41c..143295d7 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 cf8e19c2..b4b9562e 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 af769e52..695e1da4 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 e4bef3ed..04134c69 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 0f2265be..be22b8ee 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 4c1be9a6..0379f59f 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 3b93efa1..6574054f 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 5190340a..fa3aeeda 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 04134c69..8c79a30a 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 be22b8ee..2fdc50e2 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 2fdc50e2..afa908fa 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 afa908fa..95ead20c 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 95ead20c..6fce3a76 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 6fce3a76..8736decc 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 8c79a30a..3684082a 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 8736decc..fece6509 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 00000000..aa5f4b6b --- /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 30e028b7..a300a5e8 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 193d1c51..746ca1a3 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 00000000..ad1b4b3d --- /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 eac652c0..8d9fa0a2 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 3684082a..9b43015f 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 fece6509..5516246d 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 00000000..ce1c1be8 --- /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 746ca1a3..3d8327ac 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 00000000..bdc987b8 --- /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 6842003c..9ad39976 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 1570ad9a..b2dcd2dd 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 9b43015f..37c4b866 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 5516246d..46654492 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 25ea09e1..23c8c66c 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 46654492..747230c5 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 37c4b866..074f2636 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 0379f59f..272f0e89 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 23c8c66c..1d3b33d5 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 074f2636..d18b9302 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 747230c5..99fbb19a 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 00000000..08de0be8 --- /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 9ad39976..fc230475 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 fccaa0da..7df222e6 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 00000000..5d7d67f2 --- /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 3d8327ac..a0f99289 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 00000000..2745a978 --- /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 00000000..0404986d --- /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 00000000..d99d199b --- /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 00000000..403bcc61 --- /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 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 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 00000000..73c00596 --- /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 00000000..1b13e8b1 --- /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 00000000..85e35354 --- /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 00000000..478b1ec9 --- /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 00000000..64285580 --- /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 00000000..0069c252 --- /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 987b4c2d..f2b53a67 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 d18b9302..ade6d7a2 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 99fbb19a..4798cde5 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 ade6d7a2..635d2a3f 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 4798cde5..14952d41 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 00000000..bae63059 --- /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 a0f99289..515dc8e6 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 00000000..1319cfdb --- /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 14952d41..9b297e22 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 635d2a3f..b698fadc 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 9b297e22..1c66b1fb 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 7df222e6..4b9419fc 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 f2b53a67..e2d9d775 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 4b9419fc..af22bee4 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 b698fadc..85f150bc 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 171bccb9..2b7edd2c 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 803adc61..d4ad38a6 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 75d37033..f00f5a70 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 1c66b1fb..2267af7c 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 ad085d1c..93552cf8 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 fa3aeeda..3d331363 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 36793a09..36354060 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 31f69024..2c876483 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 db199aea..f298f025 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 6769cd2f..120f886b 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 6aea42ec..ec1afcce 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 ddfae0c6..dfe1f20f 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 3cc3d9e8..047fd45a 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 d87d4aba..b5801db9 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 4ba05620..5957522f 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 6f38a31d..dec12943 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 143295d7..3dea536c 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 e5d0facc..a770131a 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 fe7aa088..daa0947d 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 2c196d23..294bc638 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 459e264c..e1213e73 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 63e6a289..8b5bd748 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 ee08aa03..1ae24014 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 4d127dfc..9a7a1eae 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 891d3b38..3344e03c 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 72b0d564..be8b5eac 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 b1f33021..69f82ab1 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 b1355277..f5fcbbce 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 1fc7397a..a2319e8c 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 aa5f4b6b..dff49dad 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 f2c1f052..17f8b05e 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 3eee4563..c6a83d9b 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 5b3c702b..4a826910 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 b4b9562e..bce74f35 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 d237008f..c88f9d02 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 5c667d06..cff92710 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 93ef0022..0bd33e62 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 0bc89d01..fb14f3e5 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 bae63059..29cb34c9 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 695e1da4..a751a9d8 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 4cfcbdd0..a11edaa2 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 b8fa3455..1e4b35ff 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 8b1e3720..96120351 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 bb886b40..c201ae49 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 a300a5e8..73080ed8 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 134ebb22..1627209e 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 0991f104..a49cbe70 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 5214f242..dd990725 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 d1472bed..8cf62f9b 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 d61d29a3..0bedff39 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 18bc80d6..f15dcb4c 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 e33281bc..afc93346 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 07d56e24..36c0ebcd 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 8e237648..d91038c4 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 1a2c8e77..ca2ec07b 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 ce1c1be8..0477064b 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 bff2767c..0cb2e2a4 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 515dc8e6..03bd41c5 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 b2dcd2dd..50f3b902 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 acbb3922..d07dcd96 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 0f550a2d..25375eb6 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 2ba77759..f105cd72 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 abdea4a4..6ac88b55 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 fca60c79..630ceb47 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 415692cc..c4a4fbc3 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 1439d255..b2005561 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 b7d34619..1f9df82a 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 84a644e0..b6b5d852 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 abbfa398..90df9208 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 3528ad5e..cb83e292 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 cb9b1f80..d6882abf 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 642f1344..1b8da8f0 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 4a8c29a4..357937c3 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 ef09afa3..16793db8 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 074a857b..23a539fa 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 f8767cca..86e1746a 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 0c783cf6..115a5a34 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 3199b9db..c45d456f 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 ac05532b..2199b4cd 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 39caa25e..33c8a775 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 f32d4a06..702cbfc6 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 b40ac5dd..a54edd7e 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 51635d84..eac7e45a 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 ad1b4b3d..261b3574 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 d8dc312a..c0c62962 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 903ab89b..54123e4e 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 4efdd61c..8c05558e 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 71f667ab..272fd6bc 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 b5d58baa..4234c913 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 b1751efd..9dac3147 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 5170c42f..e5befb54 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 334e1cf9..5e615519 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 1319cfdb..042c47de 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 857a8f75..dd744bd7 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 3d4ece25..22b440cf 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 7c85709a..db3200bb 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 af5436f7..e130f1de 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 01851974..1538f12d 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 8d9fa0a2..93d0e160 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 6a1f3230..b30967c8 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 91243fca..6eca638d 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 b68ce7e0..0e65c99a 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 d6b008f2..8289b86d 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 9278124e..544fcc52 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 d2f58961..b91d7857 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 cdf83a9c..0497518d 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 f27b3519..8836e850 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 04c5df7f..9658001e 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 bdc987b8..a0a7993b 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 55fd1e8f..1dcf065e 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 63cc3243..a6f43ab1 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 e2d9d775..57c0058a 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 af22bee4..a0a3efe2 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 85f150bc..43c0d122 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 2267af7c..75c195dd 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 00000000..827f8c14 --- /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 00000000..ed0d557f --- /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 d4ad38a6..1a772f53 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 43c0d122..561f686e 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 272f0e89..58fcae89 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 561f686e..b478488f 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 75c195dd..1f3a280c 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 b478488f..f9c414dc 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 1f3a280c..f11c0115 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 00000000..f8ec5773 --- /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 03bd41c5..1924c353 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 00000000..a626b29d --- /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 f9c414dc..8dcf6e6e 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 f11c0115..58e32b34 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 1924c353..5d230e4a 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 8dcf6e6e..497fe687 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 58e32b34..9d13485e 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 a0a3efe2..e2f5c8c1 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 497fe687..9d00ca4c 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 109f41cd..1533efdd 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 11e22f5d..d3295bf2 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 5368e40e..ae540df3 100644 --- a/Tests/Tests.xcodeproj/xcshareddata/xcschemes/SwiftUIIntrospectTests.xcscheme +++ b/Tests/Tests.xcodeproj/xcshareddata/xcschemes/SwiftUIIntrospectTests.xcscheme @@ -1,6 +1,6 @@