├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.yml
│ └── config.yml
└── workflows
│ ├── cd.yml
│ └── ci.yml
├── .gitignore
├── .spi.yml
├── Examples
├── .swiftpm
│ └── xcode
│ │ └── package.xcworkspace
│ │ └── contents.xcworkspacedata
├── Package.swift
└── Showcase
│ ├── Showcase.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Showcase.xcscheme
│ └── Showcase
│ ├── App.swift
│ ├── AppView.swift
│ ├── Helpers.swift
│ └── Showcase.entitlements
├── LICENSE
├── Package.swift
├── Package@swift-6.0.swift
├── README.md
├── Sources
├── Introspect.swift
├── IntrospectableViewType.swift
├── IntrospectionSelector.swift
├── IntrospectionView.swift
├── PlatformVersion.swift
├── PlatformView.swift
├── PlatformViewVersion.swift
├── Utils.swift
├── ViewTypes
│ ├── Button.swift
│ ├── ColorPicker.swift
│ ├── DatePicker.swift
│ ├── DatePickerWithCompactStyle.swift
│ ├── DatePickerWithFieldStyle.swift
│ ├── DatePickerWithGraphicalStyle.swift
│ ├── DatePickerWithStepperFieldStyle.swift
│ ├── DatePickerWithWheelStyle.swift
│ ├── Form.swift
│ ├── FormWithGroupedStyle.swift
│ ├── FullScreenCover.swift
│ ├── List.swift
│ ├── ListCell.swift
│ ├── ListWithBorderedStyle.swift
│ ├── ListWithGroupedStyle.swift
│ ├── ListWithInsetGroupedStyle.swift
│ ├── ListWithInsetStyle.swift
│ ├── ListWithSidebarStyle.swift
│ ├── Map.swift
│ ├── NavigationSplitView.swift
│ ├── NavigationStack.swift
│ ├── NavigationViewWithColumnsStyle.swift
│ ├── NavigationViewWithStackStyle.swift
│ ├── PageControl.swift
│ ├── PickerWithMenuStyle.swift
│ ├── PickerWithSegmentedStyle.swift
│ ├── PickerWithWheelStyle.swift
│ ├── Popover.swift
│ ├── ProgressViewWithCircularStyle.swift
│ ├── ProgressViewWithLinearStyle.swift
│ ├── ScrollView.swift
│ ├── SearchField.swift
│ ├── SecureField.swift
│ ├── Sheet.swift
│ ├── SignInWithAppleButton.swift
│ ├── Slider.swift
│ ├── Stepper.swift
│ ├── TabView.swift
│ ├── TabViewWithPageStyle.swift
│ ├── Table.swift
│ ├── TextEditor.swift
│ ├── TextField.swift
│ ├── TextFieldWithVerticalAxis.swift
│ ├── Toggle.swift
│ ├── ToggleWithButtonStyle.swift
│ ├── ToggleWithCheckboxStyle.swift
│ ├── ToggleWithSwitchStyle.swift
│ ├── VideoPlayer.swift
│ ├── View.swift
│ ├── ViewController.swift
│ └── Window.swift
└── Weak.swift
├── SwiftUIIntrospect.podspec
├── SwiftUIIntrospect.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ ├── IDEWorkspaceChecks.plist
│ ├── WorkspaceSettings.xcsettings
│ ├── swiftpm
│ └── Package.resolved
│ └── xcschemes
│ └── SwiftUIIntrospect.xcscheme
├── Tests
├── LegacyTestsHostApp
│ ├── LaunchScreen.storyboard
│ └── LegacyTestsHostApp.swift
├── Package.swift
├── Tests.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ ├── LegacySwiftUIIntrospectTests.xcscheme
│ │ ├── SwiftUIIntrospectTests.xcscheme
│ │ └── SwiftUIIntrospectUITests.xcscheme
├── Tests
│ ├── PlatformVersionTests.swift
│ ├── TestUtils.swift
│ ├── ViewTypes
│ │ ├── ButtonTests.swift
│ │ ├── ColorPickerTests.swift
│ │ ├── DatePickerTests.swift
│ │ ├── DatePickerWithCompactFieldStyleTests.swift
│ │ ├── DatePickerWithFieldStyleTests.swift
│ │ ├── DatePickerWithGraphicalStyleTests.swift
│ │ ├── DatePickerWithStepperFieldStyleTests.swift
│ │ ├── DatePickerWithWheelStyleTests.swift
│ │ ├── FormTests.swift
│ │ ├── FormWithGroupedStyleTests.swift
│ │ ├── FullScreenCoverTests.swift
│ │ ├── ListCellTests.swift
│ │ ├── ListTests.swift
│ │ ├── ListWithBorderedStyleTests.swift
│ │ ├── ListWithGroupedStyleTests.swift
│ │ ├── ListWithInsetGroupedStyleTests.swift
│ │ ├── ListWithInsetStyleTests.swift
│ │ ├── ListWithPlainStyleTests.swift
│ │ ├── ListWithSidebarStyleTests.swift
│ │ ├── MapTests.swift
│ │ ├── NavigationSplitViewTests.swift
│ │ ├── NavigationStackTests.swift
│ │ ├── NavigationViewWithColumnsStyleTests.swift
│ │ ├── NavigationViewWithStackStyleTests.swift
│ │ ├── PageControlTests.swift
│ │ ├── PickerWithMenuStyleTests.swift
│ │ ├── PickerWithSegmentedStyleTests.swift
│ │ ├── PickerWithWheelStyleTests.swift
│ │ ├── PopoverTests.swift
│ │ ├── ProgressViewWithCircularStyleTests.swift
│ │ ├── ProgressViewWithLinearStyleTests.swift
│ │ ├── ScrollViewTests.swift
│ │ ├── SearchFieldTests.swift
│ │ ├── SecureFieldTests.swift
│ │ ├── SheetTests.swift
│ │ ├── SliderTests.swift
│ │ ├── StepperTests.swift
│ │ ├── TabViewTests.swift
│ │ ├── TabViewWithPageStyleTests.swift
│ │ ├── TableTests.swift
│ │ ├── TextEditorTests.swift
│ │ ├── TextFieldTests.swift
│ │ ├── TextFieldWithVerticalAxisTests.swift
│ │ ├── ToggleTests.swift
│ │ ├── ToggleWithButtonStyleTests.swift
│ │ ├── ToggleWithCheckboxStyleTests.swift
│ │ ├── ToggleWithSwitchStyleTests.swift
│ │ ├── VideoPlayerTests.swift
│ │ ├── ViewControllerTests.swift
│ │ ├── ViewTests.swift
│ │ └── WindowTests.swift
│ └── WeakTests.swift
├── TestsHostApp
│ └── TestsHostApp.swift
├── UITests
│ ├── StatusBarStyleUITests.swift
│ ├── UITestCase.swift
│ ├── UITests.xctestplan
│ └── __Snapshots__
│ │ └── StatusBarStyleUITests
│ │ ├── test.ipad-ios-13-screenshot-1.png
│ │ ├── test.ipad-ios-14-screenshot-1.png
│ │ ├── test.ipad-ios-15-screenshot-1.png
│ │ ├── test.ipad-ios-16-screenshot-1.png
│ │ ├── test.iphone-ios-13-screenshot-1.png
│ │ ├── test.iphone-ios-14-screenshot-1.png
│ │ ├── test.iphone-ios-15-screenshot-1.png
│ │ └── test.iphone-ios-16-screenshot-1.png
└── UITestsHostApp
│ ├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── Info.plist
│ ├── LaunchScreen.storyboard
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ ├── StatusBarStyle
│ ├── HostingController.swift
│ ├── NavigationView.swift
│ └── RootView.swift
│ ├── TestCases.swift
│ └── UITestsHostApp.swift
└── fastlane
└── Fastfile
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: Bug Report
2 | description: Something isn't working as expected
3 | labels: [bug]
4 | body:
5 | - type: markdown
6 | attributes:
7 | value: |
8 | Thank you for contributing to SwiftUI Introspect!
9 |
10 | 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.
11 | - type: textarea
12 | attributes:
13 | label: Description
14 | description: |
15 | A short description of the incorrect behavior.
16 |
17 | 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.
18 | validations:
19 | required: true
20 | - type: checkboxes
21 | attributes:
22 | label: Checklist
23 | options:
24 | - label: I have read the [README](https://github.com/siteline/swiftui-introspect#swiftui-introspect) before submitting this report.
25 | required: true
26 | - 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).
27 | required: true
28 | - type: textarea
29 | attributes:
30 | label: Expected behavior
31 | description: Describe what you expected to happen.
32 | validations:
33 | required: false
34 | - type: textarea
35 | attributes:
36 | label: Actual behavior
37 | description: Describe or copy/paste the behavior you observe.
38 | validations:
39 | required: false
40 | - type: textarea
41 | attributes:
42 | label: Steps to reproduce
43 | description: |
44 | Explanation of how to reproduce the incorrect behavior.
45 |
46 | This could include an attached project or link to code that is exhibiting the issue, and/or a screen recording.
47 |
48 | Please make sure the code you are sharing is minimal and can be run independently of your project, otherwise it may be difficult to debug and will take longer to fix.
49 | placeholder: |
50 | 1. ...
51 | validations:
52 | required: false
53 | - type: input
54 | attributes:
55 | label: Version information
56 | description: The version of SwiftUIIntrospect used to reproduce this issue.
57 | placeholder: "'0.11.0' for example, or a commit hash"
58 | - type: input
59 | attributes:
60 | label: Destination operating system
61 | description: The OS running the SwiftUIIntrospect module.
62 | placeholder: "'iOS 17' for example"
63 | - type: input
64 | attributes:
65 | label: Xcode version information
66 | description: The version of Xcode used to reproduce this issue.
67 | placeholder: "The version displayed from 'Xcode 〉About Xcode'"
68 | - type: textarea
69 | attributes:
70 | label: Swift Compiler version information
71 | description: The version of Swift used to reproduce this issue.
72 | placeholder: Output from 'xcrun swiftc --version'
73 | render: shell
74 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 |
3 | contact_links:
4 | - name: Project Discussion
5 | url: https://github.com/siteline/swiftui-introspect/discussions
6 | about: Q&A, ideas, and more
7 | - name: Documentation
8 | url: https://github.com/siteline/swiftui-introspect#swiftui-introspect
9 | about: Read SwiftUI Introspect's documentation
10 |
--------------------------------------------------------------------------------
/.github/workflows/cd.yml:
--------------------------------------------------------------------------------
1 | name: CD
2 |
3 | on:
4 | push:
5 | tags:
6 | - "*"
7 |
8 | jobs:
9 | deploy:
10 | name: Deploy to CocoaPods Trunk
11 | runs-on: macos-14
12 | steps:
13 | - name: Git Checkout
14 | uses: actions/checkout@v4
15 | with:
16 | fetch-depth: 0 # required to be able to find Git tags
17 |
18 | - name: Set up pkgx environment
19 | uses: pkgxdev/setup@v1
20 | with:
21 | +: pod xcodes
22 |
23 | - name: Select Xcode version
24 | run: sudo xcodes select 15.4
25 |
26 | - name: Deploy to CocoaPods Trunk
27 | run: |
28 | set -eo pipefail
29 | export LIB_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`)
30 | pod lib lint SwiftUIIntrospect.podspec --allow-warnings
31 | pod trunk push SwiftUIIntrospect.podspec --allow-warnings
32 | env:
33 | COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /.swiftpm
4 | /Packages
5 | /*.xcodeproj
6 | xcuserdata/
7 | DerivedData/
8 | .netrc
9 |
10 | fastlane/report.xml
11 | fastlane/Preview.html
12 | fastlane/screenshots/**/*.png
13 | fastlane/test_output
14 |
--------------------------------------------------------------------------------
/.spi.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | builder:
3 | configs:
4 | - documentation_targets: [SwiftUIIntrospect]
5 | custom_documentation_parameters: [--include-extended-types]
6 |
--------------------------------------------------------------------------------
/Examples/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Examples/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.4
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "Examples",
7 | products: [],
8 | targets: []
9 | )
10 |
--------------------------------------------------------------------------------
/Examples/Showcase/Showcase.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Examples/Showcase/Showcase.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Examples/Showcase/Showcase.xcodeproj/xcshareddata/xcschemes/Showcase.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
45 |
51 |
52 |
53 |
54 |
60 |
62 |
68 |
69 |
70 |
71 |
73 |
74 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/Examples/Showcase/Showcase/App.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | #if os(iOS) || os(tvOS)
4 | @main
5 | final class AppDelegate: UIResponder, UIApplicationDelegate {
6 |
7 | var window: UIWindow?
8 |
9 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
10 | window = UIWindow(frame: UIScreen.main.bounds)
11 | window?.rootViewController = UIHostingController(rootView: AppView())
12 | window?.makeKeyAndVisible()
13 | return true
14 | }
15 | }
16 | #elseif os(macOS) || os(visionOS)
17 | @main
18 | struct App: SwiftUI.App {
19 | var body: some Scene {
20 | WindowGroup {
21 | AppView()
22 | }
23 | }
24 | }
25 | #endif
26 |
27 | #if swift(>=5.9)
28 | #Preview {
29 | AppView()
30 | }
31 | #endif
32 |
--------------------------------------------------------------------------------
/Examples/Showcase/Showcase/Helpers.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | extension View {
4 | /// Modify a view with a `ViewBuilder` closure.
5 | ///
6 | /// This represents a streamlining of the
7 | /// [`modifier`](https://developer.apple.com/documentation/swiftui/view/modifier(_:)) +
8 | /// [`ViewModifier`](https://developer.apple.com/documentation/swiftui/viewmodifier) pattern.
9 | ///
10 | /// - Note: Useful only when you don't need to reuse the closure.
11 | /// If you do, turn the closure into a proper modifier.
12 | public func modifier(
13 | @ViewBuilder _ modifier: (Self) -> ModifiedContent
14 | ) -> ModifiedContent {
15 | modifier(self)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Examples/Showcase/Showcase/Showcase.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.network.client
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2019 Timber Software
2 |
3 | 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:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | 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.
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.7
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "swiftui-introspect",
7 | platforms: [
8 | .iOS(.v13),
9 | .tvOS(.v13),
10 | .macOS(.v10_15),
11 | ],
12 | products: [
13 | .library(name: "SwiftUIIntrospect", targets: ["SwiftUIIntrospect"]),
14 | .library(name: "SwiftUIIntrospect-Static", type: .static, targets: ["SwiftUIIntrospect"]),
15 | .library(name: "SwiftUIIntrospect-Dynamic", type: .dynamic, targets: ["SwiftUIIntrospect"]),
16 | ],
17 | targets: [
18 | .target(
19 | name: "SwiftUIIntrospect",
20 | path: "Sources"
21 | ),
22 | ]
23 | )
24 |
--------------------------------------------------------------------------------
/Package@swift-6.0.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:6.0
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "swiftui-introspect",
7 | platforms: [
8 | .iOS(.v13),
9 | .tvOS(.v13),
10 | .macOS(.v10_15),
11 | ],
12 | products: [
13 | .library(name: "SwiftUIIntrospect", targets: ["SwiftUIIntrospect"]),
14 | .library(name: "SwiftUIIntrospect-Static", type: .static, targets: ["SwiftUIIntrospect"]),
15 | .library(name: "SwiftUIIntrospect-Dynamic", type: .dynamic, targets: ["SwiftUIIntrospect"]),
16 | ],
17 | targets: [
18 | .target(
19 | name: "SwiftUIIntrospect",
20 | path: "Sources"
21 | ),
22 | ],
23 | swiftLanguageVersions: [.v6]
24 | )
25 |
--------------------------------------------------------------------------------
/Sources/IntrospectableViewType.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | @MainActor
3 | public protocol IntrospectableViewType {
4 | /// The scope of introspection for this particular view type, i.e. where introspect
5 | /// should look to find the desired target view relative to the applied
6 | /// `.introspect(...)` modifier.
7 | ///
8 | /// While the scope can be overridden by the user in their `.introspect(...)` call,
9 | /// most of the time it's preferable to defer to the view type's own scope,
10 | /// as it guarantees introspection is working as intended by the vendor.
11 | ///
12 | /// Defaults to `.receiver` if left unimplemented, which is a sensible one in
13 | /// most cases if you're looking to implement your own view type.
14 | var scope: IntrospectionScope { get }
15 | }
16 |
17 | extension IntrospectableViewType {
18 | public var scope: IntrospectionScope { .receiver }
19 | }
20 | #endif
21 |
--------------------------------------------------------------------------------
/Sources/IntrospectionSelector.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | @_spi(Advanced)
3 |
4 | @MainActor
5 | public struct IntrospectionSelector {
6 | @_spi(Advanced)
7 | public static var `default`: Self { .from(Target.self, selector: { $0 }) }
8 |
9 | @_spi(Advanced)
10 | public static func from(_ entryType: Entry.Type, selector: @MainActor @escaping (Entry) -> Target?) -> Self {
11 | .init(
12 | receiverSelector: { controller in
13 | controller.as(Entry.Base.self)?.receiver(ofType: Entry.self).flatMap(selector)
14 | },
15 | ancestorSelector: { controller in
16 | controller.as(Entry.Base.self)?.ancestor(ofType: Entry.self).flatMap(selector)
17 | }
18 | )
19 | }
20 |
21 | private var receiverSelector: @MainActor (IntrospectionPlatformViewController) -> Target?
22 | private var ancestorSelector: @MainActor (IntrospectionPlatformViewController) -> Target?
23 |
24 | private init(
25 | receiverSelector: @MainActor @escaping (IntrospectionPlatformViewController) -> Target?,
26 | ancestorSelector: @MainActor @escaping (IntrospectionPlatformViewController) -> Target?
27 | ) {
28 | self.receiverSelector = receiverSelector
29 | self.ancestorSelector = ancestorSelector
30 | }
31 |
32 | @_spi(Advanced)
33 | public func withReceiverSelector(_ selector: @MainActor @escaping (PlatformViewController) -> Target?) -> Self {
34 | var copy = self
35 | copy.receiverSelector = selector
36 | return copy
37 | }
38 |
39 | @_spi(Advanced)
40 | public func withAncestorSelector(_ selector: @MainActor @escaping (PlatformViewController) -> Target?) -> Self {
41 | var copy = self
42 | copy.ancestorSelector = selector
43 | return copy
44 | }
45 |
46 | func callAsFunction(_ controller: IntrospectionPlatformViewController, _ scope: IntrospectionScope) -> Target? {
47 | if
48 | scope.contains(.receiver),
49 | let target = receiverSelector(controller)
50 | {
51 | return target
52 | }
53 | if
54 | scope.contains(.ancestor),
55 | let target = ancestorSelector(controller)
56 | {
57 | return target
58 | }
59 | return nil
60 | }
61 | }
62 |
63 | extension PlatformViewController {
64 | func `as`(_ baseType: Base.Type) -> (any PlatformEntity)? {
65 | if Base.self == PlatformView.self {
66 | #if canImport(UIKit)
67 | return viewIfLoaded
68 | #elseif canImport(AppKit)
69 | return isViewLoaded ? view : nil
70 | #endif
71 | } else if Base.self == PlatformViewController.self {
72 | return self
73 | }
74 | return nil
75 | }
76 | }
77 | #endif
78 |
--------------------------------------------------------------------------------
/Sources/PlatformView.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | #if canImport(UIKit)
5 | public typealias PlatformView = UIView
6 | #elseif canImport(AppKit)
7 | public typealias PlatformView = NSView
8 | #endif
9 |
10 | #if canImport(UIKit)
11 | public typealias PlatformViewController = UIViewController
12 | #elseif canImport(AppKit)
13 | public typealias PlatformViewController = NSViewController
14 | #endif
15 |
16 | #if canImport(UIKit)
17 | typealias _PlatformViewControllerRepresentable = UIViewControllerRepresentable
18 | #elseif canImport(AppKit)
19 | typealias _PlatformViewControllerRepresentable = NSViewControllerRepresentable
20 | #endif
21 |
22 | @MainActor
23 | protocol PlatformViewControllerRepresentable: _PlatformViewControllerRepresentable {
24 | #if canImport(UIKit)
25 | typealias ViewController = UIViewControllerType
26 | #elseif canImport(AppKit)
27 | typealias ViewController = NSViewControllerType
28 | #endif
29 |
30 | func makePlatformViewController(context: Context) -> ViewController
31 | func updatePlatformViewController(_ controller: ViewController, context: Context)
32 | static func dismantlePlatformViewController(_ controller: ViewController, coordinator: Coordinator)
33 | }
34 |
35 | extension PlatformViewControllerRepresentable {
36 | #if canImport(UIKit)
37 | func makeUIViewController(context: Context) -> ViewController {
38 | makePlatformViewController(context: context)
39 | }
40 | func updateUIViewController(_ controller: ViewController, context: Context) {
41 | updatePlatformViewController(controller, context: context)
42 | }
43 | static func dismantleUIViewController(_ controller: ViewController, coordinator: Coordinator) {
44 | dismantlePlatformViewController(controller, coordinator: coordinator)
45 | }
46 | #elseif canImport(AppKit)
47 | func makeNSViewController(context: Context) -> ViewController {
48 | makePlatformViewController(context: context)
49 | }
50 | func updateNSViewController(_ controller: ViewController, context: Context) {
51 | updatePlatformViewController(controller, context: context)
52 | }
53 | static func dismantleNSViewController(_ controller: ViewController, coordinator: Coordinator) {
54 | dismantlePlatformViewController(controller, coordinator: coordinator)
55 | }
56 | #endif
57 | }
58 | #endif
59 |
--------------------------------------------------------------------------------
/Sources/Utils.swift:
--------------------------------------------------------------------------------
1 | postfix operator ~
2 |
3 | postfix func ~ (lhs: LHS) -> T {
4 | lhs as! T
5 | }
6 |
7 | postfix func ~ (lhs: LHS?) -> T? {
8 | lhs as? T
9 | }
10 |
11 | func recursiveSequence(_ sequence: S, children: @escaping (S.Element) -> S) -> AnySequence {
12 | AnySequence {
13 | var mainIterator = sequence.makeIterator()
14 | // Current iterator, or `nil` if all sequences are exhausted:
15 | var iterator: AnyIterator?
16 |
17 | return AnyIterator {
18 | guard let iterator, let element = iterator.next() else {
19 | if let element = mainIterator.next() {
20 | iterator = recursiveSequence(children(element), children: children).makeIterator()
21 | return element
22 | }
23 | return nil
24 | }
25 | return element
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/Button.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `Button` type in SwiftUI.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// Not available.
9 | ///
10 | /// ### tvOS
11 | ///
12 | /// Not available.
13 | ///
14 | /// ### macOS
15 | ///
16 | /// ```swift
17 | /// struct ContentView: View {
18 | /// var body: some View {
19 | /// Button("Action", action: {})
20 | /// .introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13, .v14, .v15)) {
21 | /// print(type(of: $0)) // NSButton
22 | /// }
23 | /// }
24 | /// }
25 | /// ```
26 | ///
27 | /// ### visionOS
28 | ///
29 | /// Not available.
30 | public struct ButtonType: IntrospectableViewType {}
31 |
32 | #if !os(iOS) && !os(tvOS) && !os(visionOS)
33 | extension IntrospectableViewType where Self == ButtonType {
34 | public static var button: Self { .init() }
35 | }
36 |
37 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
38 | extension macOSViewVersion {
39 | public static let v10_15 = Self(for: .v10_15)
40 | public static let v11 = Self(for: .v11)
41 | public static let v12 = Self(for: .v12)
42 | public static let v13 = Self(for: .v13)
43 | public static let v14 = Self(for: .v14)
44 | public static let v15 = Self(for: .v15)
45 | }
46 | #endif
47 | #endif
48 | #endif
49 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/ColorPicker.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `ColorPicker` type in SwiftUI.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// ```swift
9 | /// struct ContentView: View {
10 | /// @State var color = Color.red
11 | ///
12 | /// var body: some View {
13 | /// ColorPicker("Pick a color", selection: $color)
14 | /// .introspect(.colorPicker, on: .iOS(.v14, .v15, .v16, .v17, .v18)) {
15 | /// print(type(of: $0)) // UIColorPicker
16 | /// }
17 | /// }
18 | /// }
19 | /// ```
20 | ///
21 | /// ### tvOS
22 | ///
23 | /// Not available.
24 | ///
25 | /// ### macOS
26 | ///
27 | /// ```swift
28 | /// struct ContentView: View {
29 | /// @State var color = Color.red
30 | ///
31 | /// var body: some View {
32 | /// ColorPicker("Pick a color", selection: $color)
33 | /// .introspect(.colorPicker, on: .macOS(.v11, .v12, .v13, .v14, .v15)) {
34 | /// print(type(of: $0)) // NSColorPicker
35 | /// }
36 | /// }
37 | /// }
38 | /// ```
39 | ///
40 | /// ### visionOS
41 | ///
42 | /// ```swift
43 | /// struct ContentView: View {
44 | /// @State var color = Color.red
45 | ///
46 | /// var body: some View {
47 | /// ColorPicker("Pick a color", selection: $color)
48 | /// .introspect(.colorPicker, on: .visionOS(.v1, .v2)) {
49 | /// print(type(of: $0)) // UIColorPicker
50 | /// }
51 | /// }
52 | /// }
53 | /// ```
54 | public struct ColorPickerType: IntrospectableViewType {}
55 |
56 | #if !os(tvOS)
57 | extension IntrospectableViewType where Self == ColorPickerType {
58 | public static var colorPicker: Self { .init() }
59 | }
60 |
61 | #if canImport(UIKit)
62 | @available(iOS 14, *)
63 | extension iOSViewVersion {
64 | @available(*, unavailable, message: "ColorPicker isn't available on iOS 13")
65 | public static let v13 = Self.unavailable()
66 | public static let v14 = Self(for: .v14)
67 | public static let v15 = Self(for: .v15)
68 | public static let v16 = Self(for: .v16)
69 | public static let v17 = Self(for: .v17)
70 | public static let v18 = Self(for: .v18)
71 | }
72 |
73 | @available(iOS 14, *)
74 | extension visionOSViewVersion {
75 | public static let v1 = Self(for: .v1)
76 | public static let v2 = Self(for: .v2)
77 | }
78 | #elseif canImport(AppKit)
79 | @available(macOS 11, *)
80 | extension macOSViewVersion {
81 | @available(*, unavailable, message: "ColorPicker isn't available on macOS 10.15")
82 | public static let v10_15 = Self.unavailable()
83 | public static let v11 = Self(for: .v11)
84 | public static let v12 = Self(for: .v12)
85 | public static let v13 = Self(for: .v13)
86 | public static let v14 = Self(for: .v14)
87 | public static let v15 = Self(for: .v15)
88 | }
89 | #endif
90 | #endif
91 | #endif
92 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/DatePicker.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `DatePicker` type in SwiftUI.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// ```swift
9 | /// struct ContentView: View {
10 | /// @State var date = Date()
11 | ///
12 | /// var body: some View {
13 | /// DatePicker("Pick a date", selection: $date)
14 | /// .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18)) {
15 | /// print(type(of: $0)) // UIDatePicker
16 | /// }
17 | /// }
18 | /// }
19 | /// ```
20 | ///
21 | /// ### tvOS
22 | ///
23 | /// Not available.
24 | ///
25 | /// ```swift
26 | /// struct ContentView: View {
27 | /// @State var date = Date()
28 | ///
29 | /// var body: some View {
30 | /// DatePicker("Pick a date", selection: $date)
31 | /// .introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13, .v14, .v15)) {
32 | /// print(type(of: $0)) // NSDatePicker
33 | /// }
34 | /// }
35 | /// }
36 | /// ```
37 | ///
38 | /// ### visionOS
39 | ///
40 | /// ```swift
41 | /// struct ContentView: View {
42 | /// @State var date = Date()
43 | ///
44 | /// var body: some View {
45 | /// DatePicker("Pick a date", selection: $date)
46 | /// .introspect(.datePicker, on: .visionOS(.v1, .v2)) {
47 | /// print(type(of: $0)) // UIDatePicker
48 | /// }
49 | /// }
50 | /// }
51 | /// ```
52 | public struct DatePickerType: IntrospectableViewType {}
53 |
54 | #if !os(tvOS)
55 | extension IntrospectableViewType where Self == DatePickerType {
56 | public static var datePicker: Self { .init() }
57 | }
58 |
59 | #if canImport(UIKit)
60 | extension iOSViewVersion {
61 | public static let v13 = Self(for: .v13)
62 | public static let v14 = Self(for: .v14)
63 | public static let v15 = Self(for: .v15)
64 | public static let v16 = Self(for: .v16)
65 | public static let v17 = Self(for: .v17)
66 | public static let v18 = Self(for: .v18)
67 | }
68 |
69 | extension visionOSViewVersion {
70 | public static let v1 = Self(for: .v1)
71 | public static let v2 = Self(for: .v2)
72 | }
73 | #elseif canImport(AppKit)
74 | extension macOSViewVersion {
75 | public static let v10_15 = Self(for: .v10_15)
76 | public static let v11 = Self(for: .v11)
77 | public static let v12 = Self(for: .v12)
78 | public static let v13 = Self(for: .v13)
79 | public static let v14 = Self(for: .v14)
80 | public static let v15 = Self(for: .v15)
81 | }
82 | #endif
83 | #endif
84 | #endif
85 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/DatePickerWithCompactStyle.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `DatePicker` type in SwiftUI, with `.compact` style.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// ```swift
9 | /// struct ContentView: View {
10 | /// @State var date = Date()
11 | ///
12 | /// var body: some View {
13 | /// DatePicker("Pick a date", selection: $date)
14 | /// .datePickerStyle(.compact)
15 | /// .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17, .v18)) {
16 | /// print(type(of: $0)) // UIDatePicker
17 | /// }
18 | /// }
19 | /// }
20 | /// ```
21 | ///
22 | /// ### tvOS
23 | ///
24 | /// Not available.
25 | ///
26 | /// ### macOS
27 | ///
28 | /// ```swift
29 | /// struct ContentView: View {
30 | /// @State var date = Date()
31 | ///
32 | /// var body: some View {
33 | /// DatePicker("Pick a date", selection: $date)
34 | /// .datePickerStyle(.compact)
35 | /// .introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13, .v14, .v15)) {
36 | /// print(type(of: $0)) // NSDatePicker
37 | /// }
38 | /// }
39 | /// }
40 | /// ```
41 | ///
42 | /// ### visionOS
43 | ///
44 | /// ```swift
45 | /// struct ContentView: View {
46 | /// @State var date = Date()
47 | ///
48 | /// var body: some View {
49 | /// DatePicker("Pick a date", selection: $date)
50 | /// .datePickerStyle(.compact)
51 | /// .introspect(.datePicker(style: .compact), on: .visionOS(.v1, .v2)) {
52 | /// print(type(of: $0)) // UIDatePicker
53 | /// }
54 | /// }
55 | /// }
56 | /// ```
57 | public struct DatePickerWithCompactStyleType: IntrospectableViewType {
58 | public enum Style: Sendable {
59 | case compact
60 | }
61 | }
62 |
63 | #if !os(tvOS)
64 | extension IntrospectableViewType where Self == DatePickerWithCompactStyleType {
65 | public static func datePicker(style: Self.Style) -> Self { .init() }
66 | }
67 |
68 | #if canImport(UIKit)
69 | extension iOSViewVersion {
70 | @available(*, unavailable, message: ".datePickerStyle(.compact) isn't available on iOS 13")
71 | public static let v13 = Self(for: .v13)
72 | public static let v14 = Self(for: .v14)
73 | public static let v15 = Self(for: .v15)
74 | public static let v16 = Self(for: .v16)
75 | public static let v17 = Self(for: .v17)
76 | public static let v18 = Self(for: .v18)
77 | }
78 |
79 | extension visionOSViewVersion {
80 | public static let v1 = Self(for: .v1)
81 | public static let v2 = Self(for: .v2)
82 | }
83 | #elseif canImport(AppKit) && !targetEnvironment(macCatalyst)
84 | extension macOSViewVersion {
85 | @available(*, unavailable, message: ".datePickerStyle(.compact) isn't available on macOS 10.15")
86 | public static let v10_15 = Self(for: .v10_15)
87 | public static let v10_15_4 = Self(for: .v10_15_4)
88 | public static let v11 = Self(for: .v11)
89 | public static let v12 = Self(for: .v12)
90 | public static let v13 = Self(for: .v13)
91 | public static let v14 = Self(for: .v14)
92 | public static let v15 = Self(for: .v15)
93 | }
94 | #endif
95 | #endif
96 | #endif
97 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/DatePickerWithFieldStyle.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `DatePicker` type in SwiftUI, with `.field` style.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// Not available.
9 | ///
10 | /// ### tvOS
11 | ///
12 | /// Not available.
13 | ///
14 | /// ### macOS
15 | ///
16 | /// ```swift
17 | /// struct ContentView: View {
18 | /// @State var date = Date()
19 | ///
20 | /// var body: some View {
21 | /// DatePicker("Pick a date", selection: $date)
22 | /// .datePickerStyle(.field)
23 | /// .introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13, .v14, .v15)) {
24 | /// print(type(of: $0)) // NSDatePicker
25 | /// }
26 | /// }
27 | /// }
28 | /// ```
29 | ///
30 | /// ### visionOS
31 | ///
32 | /// Not available.
33 | public struct DatePickerWithFieldStyleType: IntrospectableViewType {
34 | public enum Style: Sendable {
35 | case field
36 | }
37 | }
38 |
39 | #if !os(iOS) && !os(tvOS) && !os(visionOS)
40 | extension IntrospectableViewType where Self == DatePickerWithFieldStyleType {
41 | public static func datePicker(style: Self.Style) -> Self { .init() }
42 | }
43 |
44 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
45 | extension macOSViewVersion {
46 | public static let v10_15 = Self(for: .v10_15)
47 | public static let v11 = Self(for: .v11)
48 | public static let v12 = Self(for: .v12)
49 | public static let v13 = Self(for: .v13)
50 | public static let v14 = Self(for: .v14)
51 | public static let v15 = Self(for: .v15)
52 | }
53 | #endif
54 | #endif
55 | #endif
56 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/DatePickerWithGraphicalStyle.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `DatePicker` type in SwiftUI, with `.graphical` style.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// ```swift
9 | /// struct ContentView: View {
10 | /// @State var date = Date()
11 | ///
12 | /// var body: some View {
13 | /// DatePicker("Pick a date", selection: $date)
14 | /// .datePickerStyle(.graphical)
15 | /// .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17, .v18)) {
16 | /// print(type(of: $0)) // UIDatePicker
17 | /// }
18 | /// }
19 | /// }
20 | /// ```
21 | ///
22 | /// ### tvOS
23 | ///
24 | /// Not available.
25 | ///
26 | /// ### macOS
27 | ///
28 | /// ```swift
29 | /// struct ContentView: View {
30 | /// @State var date = Date()
31 | ///
32 | /// var body: some View {
33 | /// DatePicker("Pick a date", selection: $date)
34 | /// .datePickerStyle(.graphical)
35 | /// .introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13, .v14, .v15)) {
36 | /// print(type(of: $0)) // NSDatePicker
37 | /// }
38 | /// }
39 | /// }
40 | /// ```
41 | ///
42 | /// ### visionOS
43 | ///
44 | /// ```swift
45 | /// struct ContentView: View {
46 | /// @State var date = Date()
47 | ///
48 | /// var body: some View {
49 | /// DatePicker("Pick a date", selection: $date)
50 | /// .datePickerStyle(.graphical)
51 | /// .introspect(.datePicker(style: .graphical), on: .visionOS(.v1, .v2)) {
52 | /// print(type(of: $0)) // UIDatePicker
53 | /// }
54 | /// }
55 | /// }
56 | /// ```
57 | public struct DatePickerWithGraphicalStyleType: IntrospectableViewType {
58 | public enum Style: Sendable {
59 | case graphical
60 | }
61 | }
62 |
63 | #if !os(tvOS)
64 | extension IntrospectableViewType where Self == DatePickerWithGraphicalStyleType {
65 | public static func datePicker(style: Self.Style) -> Self { .init() }
66 | }
67 |
68 | #if canImport(UIKit)
69 | extension iOSViewVersion {
70 | @available(*, unavailable, message: ".datePickerStyle(.graphical) isn't available on iOS 13")
71 | public static let v13 = Self(for: .v13)
72 | public static let v14 = Self(for: .v14)
73 | public static let v15 = Self(for: .v15)
74 | public static let v16 = Self(for: .v16)
75 | public static let v17 = Self(for: .v17)
76 | public static let v18 = Self(for: .v18)
77 | }
78 |
79 | extension visionOSViewVersion {
80 | public static let v1 = Self(for: .v1)
81 | public static let v2 = Self(for: .v2)
82 | }
83 | #elseif canImport(AppKit) && !targetEnvironment(macCatalyst)
84 | extension macOSViewVersion {
85 | public static let v10_15 = Self(for: .v10_15)
86 | public static let v11 = Self(for: .v11)
87 | public static let v12 = Self(for: .v12)
88 | public static let v13 = Self(for: .v13)
89 | public static let v14 = Self(for: .v14)
90 | public static let v15 = Self(for: .v15)
91 | }
92 | #endif
93 | #endif
94 | #endif
95 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/DatePickerWithStepperFieldStyle.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `DatePicker` type in SwiftUI, with `.stepperField` style.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// Not available.
9 | ///
10 | /// ### tvOS
11 | ///
12 | /// Not available.
13 | ///
14 | /// ### macOS
15 | ///
16 | /// ```swift
17 | /// struct ContentView: View {
18 | /// @State var date = Date()
19 | ///
20 | /// var body: some View {
21 | /// DatePicker("Pick a date", selection: $date)
22 | /// .datePickerStyle(.stepperField)
23 | /// .introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13, .v14, .v15)) {
24 | /// print(type(of: $0)) // NSDatePicker
25 | /// }
26 | /// }
27 | /// }
28 | /// ```
29 | ///
30 | /// ### visionOS
31 | ///
32 | /// Not available.
33 | public struct DatePickerWithStepperFieldStyleType: IntrospectableViewType {
34 | public enum Style: Sendable {
35 | case stepperField
36 | }
37 | }
38 |
39 | #if !os(iOS) && !os(tvOS) && !os(visionOS)
40 | extension IntrospectableViewType where Self == DatePickerWithStepperFieldStyleType {
41 | public static func datePicker(style: Self.Style) -> Self { .init() }
42 | }
43 |
44 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
45 | extension macOSViewVersion {
46 | public static let v10_15 = Self(for: .v10_15)
47 | public static let v11 = Self(for: .v11)
48 | public static let v12 = Self(for: .v12)
49 | public static let v13 = Self(for: .v13)
50 | public static let v14 = Self(for: .v14)
51 | public static let v15 = Self(for: .v15)
52 | }
53 | #endif
54 | #endif
55 | #endif
56 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/DatePickerWithWheelStyle.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `DatePicker` type in SwiftUI, with `.wheel` style.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// ```swift
9 | /// struct ContentView: View {
10 | /// @State var date = Date()
11 | ///
12 | /// var body: some View {
13 | /// DatePicker("Pick a date", selection: $date)
14 | /// .datePickerStyle(.wheel)
15 | /// .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18)) {
16 | /// print(type(of: $0)) // UIDatePicker
17 | /// }
18 | /// }
19 | /// }
20 | /// ```
21 | ///
22 | /// ### tvOS
23 | ///
24 | /// Not available.
25 | ///
26 | /// ### macOS
27 | ///
28 | /// Not available.
29 | ///
30 | /// ### visionOS
31 | ///
32 | /// ```swift
33 | /// struct ContentView: View {
34 | /// @State var date = Date()
35 | ///
36 | /// var body: some View {
37 | /// DatePicker("Pick a date", selection: $date)
38 | /// .datePickerStyle(.wheel)
39 | /// .introspect(.datePicker(style: .wheel), on: .visionOS(.v1, .v2)) {
40 | /// print(type(of: $0)) // UIDatePicker
41 | /// }
42 | /// }
43 | /// }
44 | /// ```
45 | public struct DatePickerWithWheelStyleType: IntrospectableViewType {
46 | public enum Style: Sendable {
47 | case wheel
48 | }
49 | }
50 |
51 | #if !os(tvOS) && !os(macOS)
52 | extension IntrospectableViewType where Self == DatePickerWithWheelStyleType {
53 | public static func datePicker(style: Self.Style) -> Self { .init() }
54 | }
55 |
56 | #if canImport(UIKit)
57 | extension iOSViewVersion {
58 | public static let v13 = Self(for: .v13)
59 | public static let v14 = Self(for: .v14)
60 | public static let v15 = Self(for: .v15)
61 | public static let v16 = Self(for: .v16)
62 | public static let v17 = Self(for: .v17)
63 | public static let v18 = Self(for: .v18)
64 | }
65 |
66 | extension visionOSViewVersion {
67 | public static let v1 = Self(for: .v1)
68 | public static let v2 = Self(for: .v2)
69 | }
70 | #endif
71 | #endif
72 | #endif
73 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/Form.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `Form` type in SwiftUI.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// ```swift
9 | /// struct ContentView: View {
10 | /// var body: some View {
11 | /// Form {
12 | /// Text("Item 1")
13 | /// Text("Item 2")
14 | /// Text("Item 3")
15 | /// }
16 | /// .introspect(.form, on: .iOS(.v13, .v14, .v15)) {
17 | /// print(type(of: $0)) // UITableView
18 | /// }
19 | /// .introspect(.form, on: .iOS(.v16, .v17, .v18)) {
20 | /// print(type(of: $0)) // UICollectionView
21 | /// }
22 | /// }
23 | /// }
24 | /// ```
25 | ///
26 | /// ### tvOS
27 | ///
28 | /// ```swift
29 | /// struct ContentView: View {
30 | /// var body: some View {
31 | /// Form {
32 | /// Text("Item 1")
33 | /// Text("Item 2")
34 | /// Text("Item 3")
35 | /// }
36 | /// .introspect(.form, on: .tvOS(.v13, .v14, .v15, .v16, .v17, .v18)) {
37 | /// print(type(of: $0)) // UITableView
38 | /// }
39 | /// }
40 | /// }
41 | /// ```
42 | ///
43 | /// ### macOS
44 | ///
45 | /// Not available.
46 | ///
47 | /// ### visionOS
48 | ///
49 | /// ```swift
50 | /// struct ContentView: View {
51 | /// var body: some View {
52 | /// Form {
53 | /// Text("Item 1")
54 | /// Text("Item 2")
55 | /// Text("Item 3")
56 | /// }
57 | /// .introspect(.form, on: .visionOS(.v1, .v2)) {
58 | /// print(type(of: $0)) // UICollectionView
59 | /// }
60 | /// }
61 | /// }
62 | /// ```
63 | public struct FormType: IntrospectableViewType {}
64 |
65 | #if !os(macOS)
66 | extension IntrospectableViewType where Self == FormType {
67 | public static var form: Self { .init() }
68 | }
69 |
70 | #if canImport(UIKit)
71 | extension iOSViewVersion {
72 | public static let v13 = Self(for: .v13)
73 | public static let v14 = Self(for: .v14)
74 | public static let v15 = Self(for: .v15)
75 | }
76 |
77 | extension iOSViewVersion {
78 | public static let v16 = Self(for: .v16)
79 | public static let v17 = Self(for: .v17)
80 | public static let v18 = Self(for: .v18)
81 | }
82 |
83 | extension tvOSViewVersion {
84 | public static let v13 = Self(for: .v13)
85 | public static let v14 = Self(for: .v14)
86 | public static let v15 = Self(for: .v15)
87 | public static let v16 = Self(for: .v16)
88 | public static let v17 = Self(for: .v17)
89 | public static let v18 = Self(for: .v18)
90 | }
91 |
92 | extension visionOSViewVersion {
93 | public static let v1 = Self(for: .v1)
94 | public static let v2 = Self(for: .v2)
95 | }
96 | #endif
97 | #endif
98 | #endif
99 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/ListWithBorderedStyle.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `List` type in SwiftUI, with `.bordered` style.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// Not available.
9 | ///
10 | /// ### tvOS
11 | ///
12 | /// Not available.
13 | ///
14 | /// ### macOS
15 | ///
16 | /// ```swift
17 | /// struct ContentView: View {
18 | /// var body: some View {
19 | /// List {
20 | /// Text("Item 1")
21 | /// Text("Item 2")
22 | /// Text("Item 3")
23 | /// }
24 | /// .listStyle(.bordered)
25 | /// .introspect(.list(style: .bordered), on: .macOS(.v12, .v13, .v14, .v15)) {
26 | /// print(type(of: $0)) // NSTableView
27 | /// }
28 | /// }
29 | /// }
30 | /// ```
31 | ///
32 | /// ### visionOS
33 | ///
34 | /// Not available.
35 | public struct ListWithBorderedStyleType: IntrospectableViewType {
36 | public enum Style: Sendable {
37 | case bordered
38 | }
39 | }
40 |
41 | #if !os(iOS) && !os(tvOS) && !os(visionOS)
42 | extension IntrospectableViewType where Self == ListWithBorderedStyleType {
43 | public static func list(style: Self.Style) -> Self { .init() }
44 | }
45 |
46 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
47 | extension macOSViewVersion {
48 | @available(*, unavailable, message: ".listStyle(.insetGrouped) isn't available on macOS 10.15")
49 | public static let v10_15 = Self.unavailable()
50 | @available(*, unavailable, message: ".listStyle(.insetGrouped) isn't available on macOS 11")
51 | public static let v11 = Self.unavailable()
52 | public static let v12 = Self(for: .v12)
53 | public static let v13 = Self(for: .v13)
54 | public static let v14 = Self(for: .v14)
55 | public static let v15 = Self(for: .v15)
56 | }
57 | #endif
58 | #endif
59 | #endif
60 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/ListWithGroupedStyle.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `List` type in SwiftUI, with `.grouped` style.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// ```swift
9 | /// struct ContentView: View {
10 | /// var body: some View {
11 | /// List {
12 | /// Text("Item 1")
13 | /// Text("Item 2")
14 | /// Text("Item 3")
15 | /// }
16 | /// .listStyle(.grouped)
17 | /// .introspect(.list(style: .grouped), on: .iOS(.v13, .v14, .v15)) {
18 | /// print(type(of: $0)) // UITableView
19 | /// }
20 | /// .introspect(.list(style: .grouped), on: .iOS(.v16, .v17, .v18)) {
21 | /// print(type(of: $0)) // UICollectionView
22 | /// }
23 | /// }
24 | /// }
25 | /// ```
26 | ///
27 | /// ### tvOS
28 | ///
29 | /// ```swift
30 | /// struct ContentView: View {
31 | /// var body: some View {
32 | /// List {
33 | /// Text("Item 1")
34 | /// Text("Item 2")
35 | /// Text("Item 3")
36 | /// }
37 | /// .listStyle(.grouped)
38 | /// .introspect(.list(style: .grouped), on: .tvOS(.v13, .v14, .v15, .v16, .v17, .v18)) {
39 | /// print(type(of: $0)) // UITableView
40 | /// }
41 | /// }
42 | /// }
43 | /// ```
44 | ///
45 | /// ### macOS
46 | ///
47 | /// Not available.
48 | ///
49 | /// ### visionOS
50 | ///
51 | /// ```swift
52 | /// struct ContentView: View {
53 | /// var body: some View {
54 | /// List {
55 | /// Text("Item 1")
56 | /// Text("Item 2")
57 | /// Text("Item 3")
58 | /// }
59 | /// .listStyle(.grouped)
60 | /// .introspect(.list(style: .grouped), on: .visionOS(.v1, .v2)) {
61 | /// print(type(of: $0)) // UICollectionView
62 | /// }
63 | /// }
64 | /// }
65 | /// ```
66 | public struct ListWithGroupedStyleType: IntrospectableViewType {
67 | public enum Style: Sendable {
68 | case grouped
69 | }
70 | }
71 |
72 | #if !os(macOS)
73 | extension IntrospectableViewType where Self == ListWithGroupedStyleType {
74 | public static func list(style: Self.Style) -> Self { .init() }
75 | }
76 |
77 | #if canImport(UIKit)
78 | extension iOSViewVersion {
79 | public static let v13 = Self(for: .v13)
80 | public static let v14 = Self(for: .v14)
81 | public static let v15 = Self(for: .v15)
82 | }
83 |
84 | extension iOSViewVersion {
85 | public static let v16 = Self(for: .v16)
86 | public static let v17 = Self(for: .v17)
87 | public static let v18 = Self(for: .v18)
88 | }
89 |
90 | extension tvOSViewVersion {
91 | public static let v13 = Self(for: .v13)
92 | public static let v14 = Self(for: .v14)
93 | public static let v15 = Self(for: .v15)
94 | public static let v16 = Self(for: .v16)
95 | public static let v17 = Self(for: .v17)
96 | public static let v18 = Self(for: .v18)
97 | }
98 |
99 | extension visionOSViewVersion {
100 | public static let v1 = Self(for: .v1)
101 | public static let v2 = Self(for: .v2)
102 | }
103 | #endif
104 | #endif
105 | #endif
106 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/ListWithInsetGroupedStyle.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `List` type in SwiftUI, with `.insetGrouped` style.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// ```swift
9 | /// struct ContentView: View {
10 | /// var body: some View {
11 | /// List {
12 | /// Text("Item 1")
13 | /// Text("Item 2")
14 | /// Text("Item 3")
15 | /// }
16 | /// .listStyle(.insetGrouped)
17 | /// .introspect(.list(style: .insetGrouped), on: .iOS(.v14, .v15)) {
18 | /// print(type(of: $0)) // UITableView
19 | /// }
20 | /// .introspect(.list(style: .insetGrouped), on: .iOS(.v16, .v17, .v18)) {
21 | /// print(type(of: $0)) // UICollectionView
22 | /// }
23 | /// }
24 | /// }
25 | /// ```
26 | ///
27 | /// ### tvOS
28 | ///
29 | /// Not available.
30 | ///
31 | /// ### macOS
32 | ///
33 | /// Not available.
34 | ///
35 | /// ### visionOS
36 | ///
37 | /// ```swift
38 | /// struct ContentView: View {
39 | /// var body: some View {
40 | /// List {
41 | /// Text("Item 1")
42 | /// Text("Item 2")
43 | /// Text("Item 3")
44 | /// }
45 | /// .listStyle(.insetGrouped)
46 | /// .introspect(.list(style: .insetGrouped), on: .visionOS(.v1, .v2)) {
47 | /// print(type(of: $0)) // UICollectionView
48 | /// }
49 | /// }
50 | /// }
51 | /// ```
52 | public struct ListWithInsetGroupedStyleType: IntrospectableViewType {
53 | public enum Style: Sendable {
54 | case insetGrouped
55 | }
56 | }
57 |
58 | #if !os(tvOS) && !os(macOS)
59 | extension IntrospectableViewType where Self == ListWithInsetGroupedStyleType {
60 | public static func list(style: Self.Style) -> Self { .init() }
61 | }
62 |
63 | #if canImport(UIKit)
64 | extension iOSViewVersion {
65 | @available(*, unavailable, message: ".listStyle(.insetGrouped) isn't available on iOS 13")
66 | public static let v13 = Self(for: .v13)
67 | public static let v14 = Self(for: .v14)
68 | public static let v15 = Self(for: .v15)
69 | }
70 |
71 | extension iOSViewVersion {
72 | public static let v16 = Self(for: .v16)
73 | public static let v17 = Self(for: .v17)
74 | public static let v18 = Self(for: .v18)
75 | }
76 |
77 | extension visionOSViewVersion {
78 | public static let v1 = Self(for: .v1)
79 | public static let v2 = Self(for: .v2)
80 | }
81 | #endif
82 | #endif
83 | #endif
84 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/PageControl.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the page control type in SwiftUI.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// ```swift
9 | /// struct ContentView: View {
10 | /// var body: some View {
11 | /// TabView {
12 | /// Text("Page 1").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red)
13 | /// Text("Page 2").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.blue)
14 | /// }
15 | /// .tabViewStyle(.page(indexDisplayMode: .always))
16 | /// .introspect(.pageControl, on: .iOS(.v14, .v15, .v16, .v17, .v18)) {
17 | /// print(type(of: $0)) // UIPageControl
18 | /// }
19 | /// }
20 | /// }
21 | /// ```
22 | ///
23 | /// ### tvOS
24 | ///
25 | /// ```swift
26 | /// struct ContentView: View {
27 | /// var body: some View {
28 | /// TabView {
29 | /// Text("Page 1").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red)
30 | /// Text("Page 2").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.blue)
31 | /// }
32 | /// .tabViewStyle(.page(indexDisplayMode: .always))
33 | /// .introspect(.pageControl, on: .tvOS(.v14, .v15, .v16, .v17, .v18)) {
34 | /// print(type(of: $0)) // UIPageControl
35 | /// }
36 | /// }
37 | /// }
38 | /// ```
39 | ///
40 | /// ### macOS
41 | ///
42 | /// Not available.
43 | ///
44 | /// ### visionOS
45 | ///
46 | /// ```swift
47 | /// struct ContentView: View {
48 | /// var body: some View {
49 | /// TabView {
50 | /// Text("Page 1").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red)
51 | /// Text("Page 2").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.blue)
52 | /// }
53 | /// .tabViewStyle(.page(indexDisplayMode: .always))
54 | /// .introspect(.pageControl, on: .visionOS(.v1, .v2)) {
55 | /// print(type(of: $0)) // UIPageControl
56 | /// }
57 | /// }
58 | /// }
59 | /// ```
60 | public struct PageControlType: IntrospectableViewType {}
61 |
62 | extension IntrospectableViewType where Self == PageControlType {
63 | public static var pageControl: Self { .init() }
64 | }
65 |
66 | #if canImport(UIKit)
67 | extension iOSViewVersion {
68 | @available(*, unavailable, message: ".tabViewStyle(.page) isn't available on iOS 13")
69 | public static let v13 = Self(for: .v13)
70 | public static let v14 = Self(for: .v14)
71 | public static let v15 = Self(for: .v15)
72 | public static let v16 = Self(for: .v16)
73 | public static let v17 = Self(for: .v17)
74 | public static let v18 = Self(for: .v18)
75 | }
76 |
77 | extension tvOSViewVersion {
78 | @available(*, unavailable, message: ".tabViewStyle(.page) isn't available on tvOS 13")
79 | public static let v13 = Self(for: .v13)
80 | public static let v14 = Self(for: .v14)
81 | public static let v15 = Self(for: .v15)
82 | public static let v16 = Self(for: .v16)
83 | public static let v17 = Self(for: .v17)
84 | public static let v18 = Self(for: .v18)
85 | }
86 |
87 | extension visionOSViewVersion {
88 | public static let v1 = Self(for: .v1)
89 | public static let v2 = Self(for: .v2)
90 | }
91 | #endif
92 | #endif
93 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/PickerWithMenuStyle.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `Picker` type in SwiftUI, with `.menu` style.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// Not available.
9 | ///
10 | /// ### tvOS
11 | ///
12 | /// Not available.
13 | ///
14 | /// ### macOS
15 | ///
16 | /// ```swift
17 | /// struct ContentView: View {
18 | /// @State var selection = "1"
19 | ///
20 | /// var body: some View {
21 | /// Picker("Pick a number", selection: $selection) {
22 | /// Text("1").tag("1")
23 | /// Text("2").tag("2")
24 | /// Text("3").tag("3")
25 | /// }
26 | /// .pickerStyle(.menu)
27 | /// .introspect(.picker(style: .menu), on: .macOS(.v11, .v12, .v13, .v14, .v15)) {
28 | /// print(type(of: $0)) // NSPopUpButton
29 | /// }
30 | /// }
31 | /// }
32 | /// ```
33 | ///
34 | /// ### visionOS
35 | ///
36 | /// Not available.
37 | public struct PickerWithMenuStyleType: IntrospectableViewType {
38 | public enum Style: Sendable {
39 | case menu
40 | }
41 | }
42 |
43 | #if !os(iOS) && !os(tvOS) && !os(visionOS)
44 | extension IntrospectableViewType where Self == PickerWithMenuStyleType {
45 | public static func picker(style: Self.Style) -> Self { .init() }
46 | }
47 |
48 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
49 | extension macOSViewVersion {
50 | @available(*, unavailable, message: ".pickerStyle(.menu) isn't available on macOS 10.15")
51 | public static let v10_15 = Self.unavailable()
52 | public static let v11 = Self(for: .v11)
53 | public static let v12 = Self(for: .v12)
54 | public static let v13 = Self(for: .v13)
55 | public static let v14 = Self(for: .v14)
56 | public static let v15 = Self(for: .v15)
57 | }
58 | #endif
59 | #endif
60 | #endif
61 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/PickerWithWheelStyle.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `Picker` type in SwiftUI, with `.wheel` style.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// ```swift
9 | /// struct ContentView: View {
10 | /// @State var selection = "1"
11 | ///
12 | /// var body: some View {
13 | /// Picker("Pick a number", selection: $selection) {
14 | /// Text("1").tag("1")
15 | /// Text("2").tag("2")
16 | /// Text("3").tag("3")
17 | /// }
18 | /// .pickerStyle(.wheel)
19 | /// .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18)) {
20 | /// print(type(of: $0)) // UIPickerView
21 | /// }
22 | /// }
23 | /// }
24 | /// ```
25 | ///
26 | /// ### tvOS
27 | ///
28 | /// Not available.
29 | ///
30 | /// ### macOS
31 | ///
32 | /// Not available.
33 | ///
34 | /// ### visionOS
35 | ///
36 | /// ```swift
37 | /// struct ContentView: View {
38 | /// @State var selection = "1"
39 | ///
40 | /// var body: some View {
41 | /// Picker("Pick a number", selection: $selection) {
42 | /// Text("1").tag("1")
43 | /// Text("2").tag("2")
44 | /// Text("3").tag("3")
45 | /// }
46 | /// .pickerStyle(.wheel)
47 | /// .introspect(.picker(style: .wheel), on: .visionOS(.v1, .v2)) {
48 | /// print(type(of: $0)) // UIPickerView
49 | /// }
50 | /// }
51 | /// }
52 | /// ```
53 | public struct PickerWithWheelStyleType: IntrospectableViewType {
54 | public enum Style: Sendable {
55 | case wheel
56 | }
57 | }
58 |
59 | #if !os(tvOS) && !os(macOS)
60 | extension IntrospectableViewType where Self == PickerWithWheelStyleType {
61 | public static func picker(style: Self.Style) -> Self { .init() }
62 | }
63 |
64 | #if canImport(UIKit)
65 | extension iOSViewVersion {
66 | public static let v13 = Self(for: .v13)
67 | public static let v14 = Self(for: .v14)
68 | public static let v15 = Self(for: .v15)
69 | public static let v16 = Self(for: .v16)
70 | public static let v17 = Self(for: .v17)
71 | public static let v18 = Self(for: .v18)
72 | }
73 |
74 | extension visionOSViewVersion {
75 | public static let v1 = Self(for: .v1)
76 | public static let v2 = Self(for: .v2)
77 | }
78 | #endif
79 | #endif
80 | #endif
81 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/Popover.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of `.popover` in SwiftUI.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// ```swift
9 | /// struct ContentView: View {
10 | /// @State var isPresented = false
11 | ///
12 | /// var body: some View {
13 | /// Button("Present", action: { isPresented = true })
14 | /// .popover(isPresented: $isPresented) {
15 | /// Button("Dismiss", action: { isPresented = false })
16 | /// .introspect(.popover, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18)) {
17 | /// print(type(of: $0)) // UIPopoverPresentationController
18 | /// }
19 | /// }
20 | /// }
21 | /// }
22 | /// ```
23 | ///
24 | /// ### tvOS
25 | ///
26 | /// Not available.
27 | ///
28 | /// ### macOS
29 | ///
30 | /// Not available.
31 | ///
32 | /// ### visionOS
33 | ///
34 | /// ```swift
35 | /// struct ContentView: View {
36 | /// @State var isPresented = false
37 | ///
38 | /// var body: some View {
39 | /// Button("Present", action: { isPresented = true })
40 | /// .popover(isPresented: $isPresented) {
41 | /// Button("Dismiss", action: { isPresented = false })
42 | /// .introspect(.popover, on: .visionOS(.v1, .v2)) {
43 | /// print(type(of: $0)) // UIPopoverPresentationController
44 | /// }
45 | /// }
46 | /// }
47 | /// }
48 | /// ```
49 | public struct PopoverType: IntrospectableViewType {
50 | public var scope: IntrospectionScope { .ancestor }
51 | }
52 |
53 | #if !os(tvOS) && !os(macOS)
54 | extension IntrospectableViewType where Self == PopoverType {
55 | public static var popover: Self { .init() }
56 | }
57 |
58 | #if canImport(UIKit)
59 | extension iOSViewVersion {
60 | public static let v13 = Self(for: .v13, selector: selector)
61 | public static let v14 = Self(for: .v14, selector: selector)
62 | public static let v15 = Self(for: .v15, selector: selector)
63 | public static let v16 = Self(for: .v16, selector: selector)
64 | public static let v17 = Self(for: .v17, selector: selector)
65 | public static let v18 = Self(for: .v18, selector: selector)
66 |
67 | private static var selector: IntrospectionSelector {
68 | .from(UIViewController.self, selector: { $0.popoverPresentationController })
69 | }
70 | }
71 |
72 | extension visionOSViewVersion {
73 | public static let v1 = Self(for: .v1, selector: selector)
74 | public static let v2 = Self(for: .v2, selector: selector)
75 |
76 | private static var selector: IntrospectionSelector {
77 | .from(UIViewController.self, selector: { $0.popoverPresentationController })
78 | }
79 | }
80 | #endif
81 | #endif
82 | #endif
83 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/ScrollView.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `ScrollView` type in SwiftUI.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// ```swift
9 | /// struct ContentView: View {
10 | /// var body: some View {
11 | /// ScrollView {
12 | /// Text("Item")
13 | /// }
14 | /// .introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18)) {
15 | /// print(type(of: $0)) // UIScrollView
16 | /// }
17 | /// }
18 | /// }
19 | /// ```
20 | ///
21 | /// ### tvOS
22 | ///
23 | /// ```swift
24 | /// struct ContentView: View {
25 | /// var body: some View {
26 | /// ScrollView {
27 | /// Text("Item")
28 | /// }
29 | /// .introspect(.scrollView, on: .tvOS(.v13, .v14, .v15, .v16, .v17, .v18)) {
30 | /// print(type(of: $0)) // UIScrollView
31 | /// }
32 | /// }
33 | /// }
34 | /// ```
35 | ///
36 | /// ### macOS
37 | ///
38 | /// ```swift
39 | /// struct ContentView: View {
40 | /// var body: some View {
41 | /// ScrollView {
42 | /// Text("Item")
43 | /// }
44 | /// .introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14, .v15)) {
45 | /// print(type(of: $0)) // NSScrollView
46 | /// }
47 | /// }
48 | /// }
49 | /// ```
50 | ///
51 | /// ### visionOS
52 | ///
53 | /// ```swift
54 | /// struct ContentView: View {
55 | /// var body: some View {
56 | /// ScrollView {
57 | /// Text("Item")
58 | /// }
59 | /// .introspect(.scrollView, on: .visionOS(.v1, .v2)) {
60 | /// print(type(of: $0)) // UIScrollView
61 | /// }
62 | /// }
63 | /// }
64 | /// ```
65 | public struct ScrollViewType: IntrospectableViewType {}
66 |
67 | extension IntrospectableViewType where Self == ScrollViewType {
68 | public static var scrollView: Self { .init() }
69 | }
70 |
71 | #if canImport(UIKit)
72 | extension iOSViewVersion {
73 | public static let v13 = Self(for: .v13)
74 | public static let v14 = Self(for: .v14)
75 | public static let v15 = Self(for: .v15)
76 | public static let v16 = Self(for: .v16)
77 | public static let v17 = Self(for: .v17)
78 | public static let v18 = Self(for: .v18)
79 | }
80 |
81 | extension tvOSViewVersion {
82 | public static let v13 = Self(for: .v13)
83 | public static let v14 = Self(for: .v14)
84 | public static let v15 = Self(for: .v15)
85 | public static let v16 = Self(for: .v16)
86 | public static let v17 = Self(for: .v17)
87 | public static let v18 = Self(for: .v18)
88 | }
89 |
90 | extension visionOSViewVersion {
91 | public static let v1 = Self(for: .v1)
92 | public static let v2 = Self(for: .v2)
93 | }
94 | #elseif canImport(AppKit)
95 | extension macOSViewVersion {
96 | public static let v10_15 = Self(for: .v10_15)
97 | public static let v11 = Self(for: .v11)
98 | public static let v12 = Self(for: .v12)
99 | public static let v13 = Self(for: .v13)
100 | public static let v14 = Self(for: .v14)
101 | public static let v15 = Self(for: .v15)
102 | }
103 | #endif
104 | #endif
105 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/SignInWithAppleButton.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `SignInWithAppleButton` type in SwiftUI.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// ```swift
9 | /// struct ContentView: View {
10 | /// var body: some View {
11 | /// SignInWithAppleButton(.signIn) { request in
12 | /// request.requestedScopes = [.fullName, .email]
13 | /// } onCompletion: { result in
14 | /// // do something with result
15 | /// }
16 | /// .introspect(.signInWithAppleButton, on: .iOS(.v14, .v15, .v16, .v17, .v18)) {
17 | /// print(type(of: $0)) // ASAuthorizationAppleIDButton
18 | /// }
19 | /// }
20 | /// }
21 | /// ```
22 | ///
23 | /// ### tvOS
24 | ///
25 | /// ```swift
26 | /// struct ContentView: View {
27 | /// var body: some View {
28 | /// SignInWithAppleButton(.signIn) { request in
29 | /// request.requestedScopes = [.fullName, .email]
30 | /// } onCompletion: { result in
31 | /// // do something with result
32 | /// }
33 | /// .introspect(.signInWithAppleButton, on: .tvOS(.v14, .v15, .v16, .v17, .v18)) {
34 | /// print(type(of: $0)) // ASAuthorizationAppleIDButton
35 | /// }
36 | /// }
37 | /// }
38 | /// ```
39 | ///
40 | /// ### macOS
41 | ///
42 | /// ```swift
43 | /// struct ContentView: View {
44 | /// var body: some View {
45 | /// SignInWithAppleButton(.signIn) { request in
46 | /// request.requestedScopes = [.fullName, .email]
47 | /// } onCompletion: { result in
48 | /// // do something with result
49 | /// }
50 | /// .introspect(.signInWithAppleButton, on: .macOS(.v11, .v12, .v13, .v14),) {
51 | /// print(type(of: $0)) // ASAuthorizationAppleIDButton
52 | /// }
53 | /// }
54 | /// }
55 | /// ```
56 | ///
57 | /// ### visionOS
58 | ///
59 | /// ```swift
60 | /// struct ContentView: View {
61 | /// var body: some View {
62 | /// SignInWithAppleButton(.signIn) { request in
63 | /// request.requestedScopes = [.fullName, .email]
64 | /// } onCompletion: { result in
65 | /// // do something with result
66 | /// }
67 | /// .introspect(.signInWithAppleButton, on: .visionOS(.v1, .v2)) {
68 | /// print(type(of: $0)) // ASAuthorizationAppleIDButton
69 | /// }
70 | /// }
71 | /// }
72 | /// ```
73 | public struct SignInWithAppleButtonType: IntrospectableViewType {}
74 |
75 | extension IntrospectableViewType where Self == SignInWithAppleButtonType {
76 | @available(
77 | *,
78 | unavailable,
79 | message: """
80 | Due to a mysterious bug on Apple's part that may cause a complete
81 | app hang, the unfortunate decision has been made to remove support
82 | for `SignInWithAppleButton` introspection.
83 |
84 | We apologize for this inconvenience.
85 |
86 | More details can be found at https://github.com/siteline/swiftui-introspect/issues/400
87 | """
88 | )
89 | public static var signInWithAppleButton: Self { .init() }
90 | }
91 | #endif
92 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/Slider.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `Slider` type in SwiftUI.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// ```swift
9 | /// struct ContentView: View {
10 | /// @State var selection = 0.5
11 | ///
12 | /// var body: some View {
13 | /// Slider(value: $selection, in: 0...1)
14 | /// .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18)) {
15 | /// print(type(of: $0)) // UISlider
16 | /// }
17 | /// }
18 | /// }
19 | /// ```
20 | ///
21 | /// ### tvOS
22 | ///
23 | /// Not available.
24 | ///
25 | /// ### macOS
26 | ///
27 | /// ```swift
28 | /// struct ContentView: View {
29 | /// @State var selection = 0.5
30 | ///
31 | /// var body: some View {
32 | /// Slider(value: $selection, in: 0...1)
33 | /// .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13, .v14, .v15)) {
34 | /// print(type(of: $0)) // NSSlider
35 | /// }
36 | /// }
37 | /// }
38 | /// ```
39 | ///
40 | /// ### visionOS
41 | ///
42 | /// Not available.
43 | public struct SliderType: IntrospectableViewType {}
44 |
45 | #if !os(tvOS) && !os(visionOS)
46 | extension IntrospectableViewType where Self == SliderType {
47 | public static var slider: Self { .init() }
48 | }
49 |
50 | #if canImport(UIKit)
51 | extension iOSViewVersion {
52 | public static let v13 = Self(for: .v13)
53 | public static let v14 = Self(for: .v14)
54 | public static let v15 = Self(for: .v15)
55 | public static let v16 = Self(for: .v16)
56 | public static let v17 = Self(for: .v17)
57 | public static let v18 = Self(for: .v18)
58 | }
59 | #elseif canImport(AppKit)
60 | extension macOSViewVersion {
61 | public static let v10_15 = Self(for: .v10_15)
62 | public static let v11 = Self(for: .v11)
63 | public static let v12 = Self(for: .v12)
64 | public static let v13 = Self(for: .v13)
65 | public static let v14 = Self(for: .v14)
66 | public static let v15 = Self(for: .v15)
67 | }
68 | #endif
69 | #endif
70 | #endif
71 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/Stepper.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `Stepper` type in SwiftUI.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// ```swift
9 | /// struct ContentView: View {
10 | /// @State var selection = 5
11 | ///
12 | /// var body: some View {
13 | /// Stepper("Select a number", value: $selection, in: 0...10)
14 | /// .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18)) {
15 | /// print(type(of: $0)) // UIStepper
16 | /// }
17 | /// }
18 | /// }
19 | /// ```
20 | ///
21 | /// ### tvOS
22 | ///
23 | /// Not available.
24 | ///
25 | /// ### macOS
26 | ///
27 | /// ```swift
28 | /// struct ContentView: View {
29 | /// @State var selection = 5
30 | ///
31 | /// var body: some View {
32 | /// Stepper("Select a number", value: $selection, in: 0...10)
33 | /// .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13, .v14, .v15)) {
34 | /// print(type(of: $0)) // NSStepper
35 | /// }
36 | /// }
37 | /// }
38 | /// ```
39 | ///
40 | /// ### visionOS
41 | ///
42 | /// Not available.
43 | public struct StepperType: IntrospectableViewType {}
44 |
45 | #if !os(tvOS) && !os(visionOS)
46 | extension IntrospectableViewType where Self == StepperType {
47 | public static var stepper: Self { .init() }
48 | }
49 |
50 | #if canImport(UIKit)
51 | extension iOSViewVersion {
52 | public static let v13 = Self(for: .v13)
53 | public static let v14 = Self(for: .v14)
54 | public static let v15 = Self(for: .v15)
55 | public static let v16 = Self(for: .v16)
56 | public static let v17 = Self(for: .v17)
57 | public static let v18 = Self(for: .v18)
58 | }
59 | #elseif canImport(AppKit)
60 | extension macOSViewVersion {
61 | public static let v10_15 = Self(for: .v10_15)
62 | public static let v11 = Self(for: .v11)
63 | public static let v12 = Self(for: .v12)
64 | public static let v13 = Self(for: .v13)
65 | public static let v14 = Self(for: .v14)
66 | public static let v15 = Self(for: .v15)
67 | }
68 | #endif
69 | #endif
70 | #endif
71 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/TextEditor.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `TextEditor` type in SwiftUI.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// ```swift
9 | /// struct ContentView: View {
10 | /// @State var text = "Lorem ipsum"
11 | ///
12 | /// var body: some View {
13 | /// TextEditor(text: $text)
14 | /// .introspect(.textEditor, on: .iOS(.v14, .v15, .v16, .v17, .v18)) {
15 | /// print(type(of: $0)) // UITextView
16 | /// }
17 | /// }
18 | /// }
19 | /// ```
20 | ///
21 | /// ### tvOS
22 | ///
23 | /// Not available.
24 | ///
25 | /// ### macOS
26 | ///
27 | /// ```swift
28 | /// struct ContentView: View {
29 | /// @State var text = "Lorem ipsum"
30 | ///
31 | /// var body: some View {
32 | /// TextEditor(text: $text)
33 | /// .introspect(.textEditor, on: .macOS(.v11, .v12, .v13, .v14, .v15)) {
34 | /// print(type(of: $0)) // NSTextView
35 | /// }
36 | /// }
37 | /// }
38 | /// ```
39 | ///
40 | /// ### visionOS
41 | ///
42 | /// ```swift
43 | /// struct ContentView: View {
44 | /// @State var text = "Lorem ipsum"
45 | ///
46 | /// var body: some View {
47 | /// TextEditor(text: $text)
48 | /// .introspect(.textEditor, on: .visionOS(.v1, .v2)) {
49 | /// print(type(of: $0)) // UITextView
50 | /// }
51 | /// }
52 | /// }
53 | /// ```
54 | public struct TextEditorType: IntrospectableViewType {}
55 |
56 | #if !os(tvOS)
57 | extension IntrospectableViewType where Self == TextEditorType {
58 | public static var textEditor: Self { .init() }
59 | }
60 |
61 | #if canImport(UIKit)
62 | extension iOSViewVersion {
63 | @available(*, unavailable, message: "TextEditor isn't available on iOS 13")
64 | public static let v13 = Self.unavailable()
65 | public static let v14 = Self(for: .v14)
66 | public static let v15 = Self(for: .v15)
67 | public static let v16 = Self(for: .v16)
68 | public static let v17 = Self(for: .v17)
69 | public static let v18 = Self(for: .v18)
70 | }
71 |
72 | extension visionOSViewVersion {
73 | public static let v1 = Self(for: .v1)
74 | public static let v2 = Self(for: .v2)
75 | }
76 | #elseif canImport(AppKit)
77 | extension macOSViewVersion {
78 | @available(*, unavailable, message: "TextEditor isn't available on macOS 10.15")
79 | public static let v10_15 = Self.unavailable()
80 | public static let v11 = Self(for: .v11)
81 | public static let v12 = Self(for: .v12)
82 | public static let v13 = Self(for: .v13)
83 | public static let v14 = Self(for: .v14)
84 | public static let v15 = Self(for: .v15)
85 | }
86 | #endif
87 | #endif
88 | #endif
89 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/TextField.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `TextField` type in SwiftUI.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// ```swift
9 | /// struct ContentView: View {
10 | /// @State var text = "Lorem ipsum"
11 | ///
12 | /// var body: some View {
13 | /// TextField("Text Field", text: $text)
14 | /// .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18)) {
15 | /// print(type(of: $0)) // UITextField
16 | /// }
17 | /// }
18 | /// }
19 | /// ```
20 | ///
21 | /// ### tvOS
22 | ///
23 | /// ```swift
24 | /// struct ContentView: View {
25 | /// @State var text = "Lorem ipsum"
26 | ///
27 | /// var body: some View {
28 | /// TextField("Text Field", text: $text)
29 | /// .introspect(.textField, on: .tvOS(.v13, .v14, .v15, .v16, .v17, .v18)) {
30 | /// print(type(of: $0)) // UITextField
31 | /// }
32 | /// }
33 | /// }
34 | /// ```
35 | ///
36 | /// ### macOS
37 | ///
38 | /// ```swift
39 | /// struct ContentView: View {
40 | /// @State var text = "Lorem ipsum"
41 | ///
42 | /// var body: some View {
43 | /// TextField("Text Field", text: $text)
44 | /// .introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14, .v15)) {
45 | /// print(type(of: $0)) // NSTextField
46 | /// }
47 | /// }
48 | /// }
49 | /// ```
50 | ///
51 | /// ### visionOS
52 | ///
53 | /// ```swift
54 | /// struct ContentView: View {
55 | /// @State var text = "Lorem ipsum"
56 | ///
57 | /// var body: some View {
58 | /// TextField("Text Field", text: $text)
59 | /// .introspect(.textField, on: .visionOS(.v1, .v2)) {
60 | /// print(type(of: $0)) // UITextField
61 | /// }
62 | /// }
63 | /// }
64 | /// ```
65 | public struct TextFieldType: IntrospectableViewType {}
66 |
67 | extension IntrospectableViewType where Self == TextFieldType {
68 | public static var textField: Self { .init() }
69 | }
70 |
71 | #if canImport(UIKit)
72 | extension iOSViewVersion {
73 | public static let v13 = Self(for: .v13)
74 | public static let v14 = Self(for: .v14)
75 | public static let v15 = Self(for: .v15)
76 | public static let v16 = Self(for: .v16)
77 | public static let v17 = Self(for: .v17)
78 | public static let v18 = Self(for: .v18)
79 | }
80 |
81 | extension tvOSViewVersion {
82 | public static let v13 = Self(for: .v13)
83 | public static let v14 = Self(for: .v14)
84 | public static let v15 = Self(for: .v15)
85 | public static let v16 = Self(for: .v16)
86 | public static let v17 = Self(for: .v17)
87 | public static let v18 = Self(for: .v18)
88 | }
89 |
90 | extension visionOSViewVersion {
91 | public static let v1 = Self(for: .v1)
92 | public static let v2 = Self(for: .v2)
93 | }
94 | #elseif canImport(AppKit)
95 | extension macOSViewVersion {
96 | public static let v10_15 = Self(for: .v10_15)
97 | public static let v11 = Self(for: .v11)
98 | public static let v12 = Self(for: .v12)
99 | public static let v13 = Self(for: .v13)
100 | public static let v14 = Self(for: .v14)
101 | public static let v15 = Self(for: .v15)
102 | }
103 | #endif
104 | #endif
105 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/Toggle.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `Toggle` type in SwiftUI.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// ```swift
9 | /// struct ContentView: View {
10 | /// @State var isOn = false
11 | ///
12 | /// var body: some View {
13 | /// Toggle("Toggle", isOn: $isOn)
14 | /// .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18)) {
15 | /// print(type(of: $0)) // UISwitch
16 | /// }
17 | /// }
18 | /// }
19 | /// ```
20 | ///
21 | /// ### tvOS
22 | ///
23 | /// Not available.
24 | ///
25 | /// ### macOS
26 | ///
27 | /// ```swift
28 | /// struct ContentView: View {
29 | /// @State var isOn = false
30 | ///
31 | /// var body: some View {
32 | /// Toggle("Toggle", isOn: $isOn)
33 | /// .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13, .v14, .v15)) {
34 | /// print(type(of: $0)) // NSButton
35 | /// }
36 | /// }
37 | /// }
38 | /// ```
39 | ///
40 | /// ### visionOS
41 | ///
42 | /// Not available.
43 | public struct ToggleType: IntrospectableViewType {}
44 |
45 | #if !os(tvOS) && !os(visionOS)
46 | extension IntrospectableViewType where Self == ToggleType {
47 | public static var toggle: Self { .init() }
48 | }
49 |
50 | #if canImport(UIKit)
51 | extension iOSViewVersion {
52 | public static let v13 = Self(for: .v13)
53 | public static let v14 = Self(for: .v14)
54 | public static let v15 = Self(for: .v15)
55 | public static let v16 = Self(for: .v16)
56 | public static let v17 = Self(for: .v17)
57 | public static let v18 = Self(for: .v18)
58 | }
59 | #elseif canImport(AppKit)
60 | extension macOSViewVersion {
61 | public static let v10_15 = Self(for: .v10_15)
62 | public static let v11 = Self(for: .v11)
63 | public static let v12 = Self(for: .v12)
64 | public static let v13 = Self(for: .v13)
65 | public static let v14 = Self(for: .v14)
66 | public static let v15 = Self(for: .v15)
67 | }
68 | #endif
69 | #endif
70 | #endif
71 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/ToggleWithButtonStyle.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `Toggle` type in SwiftUI, with `.button` style.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// Not available.
9 | ///
10 | /// ### tvOS
11 | ///
12 | /// Not available.
13 | ///
14 | /// ### macOS
15 | ///
16 | /// ```swift
17 | /// struct ContentView: View {
18 | /// @State var isOn = false
19 | ///
20 | /// var body: some View {
21 | /// Toggle("Toggle", isOn: $isOn)
22 | /// .toggleStyle(.button)
23 | /// .introspect(.toggle(style: .button), on: .macOS(.v12, .v13, .v14, .v15)) {
24 | /// print(type(of: $0)) // NSButton
25 | /// }
26 | /// }
27 | /// }
28 | /// ```
29 | ///
30 | /// ### visionOS
31 | ///
32 | /// Not available.
33 | public struct ToggleWithButtonStyleType: IntrospectableViewType {
34 | public enum Style: Sendable {
35 | case button
36 | }
37 | }
38 |
39 | #if !os(iOS) && !os(tvOS) && !os(visionOS)
40 | extension IntrospectableViewType where Self == ToggleWithButtonStyleType {
41 | public static func toggle(style: Self.Style) -> Self { .init() }
42 | }
43 |
44 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
45 | extension macOSViewVersion {
46 | @available(*, unavailable, message: ".toggleStyle(.button) isn't available on macOS 10.15")
47 | public static let v10_15 = Self.unavailable()
48 | @available(*, unavailable, message: ".toggleStyle(.button) isn't available on macOS 11")
49 | public static let v11 = Self.unavailable()
50 | public static let v12 = Self(for: .v12)
51 | public static let v13 = Self(for: .v13)
52 | public static let v14 = Self(for: .v14)
53 | public static let v15 = Self(for: .v15)
54 | }
55 | #endif
56 | #endif
57 | #endif
58 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/ToggleWithCheckboxStyle.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `Toggle` type in SwiftUI, with `.checkbox` style.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// Not available.
9 | ///
10 | /// ### tvOS
11 | ///
12 | /// Not available.
13 | ///
14 | /// ### macOS
15 | ///
16 | /// ```swift
17 | /// struct ContentView: View {
18 | /// @State var isOn = false
19 | ///
20 | /// var body: some View {
21 | /// Toggle("Checkbox", isOn: $isOn)
22 | /// .toggleStyle(.checkbox)
23 | /// .introspect(.toggle(style: .checkbox), on: .macOS(.v10_15, .v11, .v12, .v13, .v14, .v15)) {
24 | /// print(type(of: $0)) // NSButton
25 | /// }
26 | /// }
27 | /// }
28 | /// ```
29 | ///
30 | /// ### visionOS
31 | ///
32 | /// Not available.
33 | public struct ToggleWithCheckboxStyleType: IntrospectableViewType {
34 | public enum Style: Sendable {
35 | case checkbox
36 | }
37 | }
38 |
39 | #if !os(iOS) && !os(tvOS) && !os(visionOS)
40 | extension IntrospectableViewType where Self == ToggleWithCheckboxStyleType {
41 | public static func toggle(style: Self.Style) -> Self { .init() }
42 | }
43 |
44 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
45 | extension macOSViewVersion {
46 | public static let v10_15 = Self(for: .v10_15)
47 | public static let v11 = Self(for: .v11)
48 | public static let v12 = Self(for: .v12)
49 | public static let v13 = Self(for: .v13)
50 | public static let v14 = Self(for: .v14)
51 | public static let v15 = Self(for: .v15)
52 | }
53 | #endif
54 | #endif
55 | #endif
56 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/ToggleWithSwitchStyle.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of the `Toggle` type in SwiftUI, with `.switch` style.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// ```swift
9 | /// struct ContentView: View {
10 | /// @State var isOn = false
11 | ///
12 | /// var body: some View {
13 | /// Toggle("Switch", isOn: $isOn)
14 | /// .toggleStyle(.switch)
15 | /// .introspect(.toggle(style: .switch), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18)) {
16 | /// print(type(of: $0)) // UISwitch
17 | /// }
18 | /// }
19 | /// }
20 | /// ```
21 | ///
22 | /// ### tvOS
23 | ///
24 | /// Not available.
25 | ///
26 | /// ### macOS
27 | ///
28 | /// ```swift
29 | /// struct ContentView: View {
30 | /// @State var isOn = false
31 | ///
32 | /// var body: some View {
33 | /// Toggle("Switch", isOn: $isOn)
34 | /// .toggleStyle(.switch)
35 | /// .introspect(.toggle(style: .switch), on: .macOS(.v10_15, .v11, .v12, .v13, .v14, .v15)) {
36 | /// print(type(of: $0)) // NSSwitch
37 | /// }
38 | /// }
39 | /// }
40 | /// ```
41 | ///
42 | /// ### visionOS
43 | ///
44 | /// Not available.
45 | public struct ToggleWithSwitchStyleType: IntrospectableViewType {
46 | public enum Style: Sendable {
47 | case `switch`
48 | }
49 | }
50 |
51 | #if !os(tvOS) && !os(visionOS)
52 | extension IntrospectableViewType where Self == ToggleWithSwitchStyleType {
53 | public static func toggle(style: Self.Style) -> Self { .init() }
54 | }
55 |
56 | #if canImport(UIKit)
57 | extension iOSViewVersion {
58 | public static let v13 = Self(for: .v13)
59 | public static let v14 = Self(for: .v14)
60 | public static let v15 = Self(for: .v15)
61 | public static let v16 = Self(for: .v16)
62 | public static let v17 = Self(for: .v17)
63 | public static let v18 = Self(for: .v18)
64 | }
65 | #elseif canImport(AppKit)
66 | extension macOSViewVersion {
67 | public static let v10_15 = Self(for: .v10_15)
68 | public static let v11 = Self(for: .v11)
69 | public static let v12 = Self(for: .v12)
70 | public static let v13 = Self(for: .v13)
71 | public static let v14 = Self(for: .v14)
72 | public static let v15 = Self(for: .v15)
73 | }
74 | #endif
75 | #endif
76 | #endif
77 |
--------------------------------------------------------------------------------
/Sources/ViewTypes/View.swift:
--------------------------------------------------------------------------------
1 | #if !os(watchOS)
2 | import SwiftUI
3 |
4 | /// An abstract representation of a generic SwiftUI view type.
5 | ///
6 | /// ### iOS
7 | ///
8 | /// ```swift
9 | /// struct ContentView: View {
10 | /// var body: some View {
11 | /// HStack {
12 | /// Image(systemName: "scribble")
13 | /// Text("Some text")
14 | /// }
15 | /// .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18)) {
16 | /// print(type(of: $0)) // some subclass of UIView
17 | /// }
18 | /// }
19 | /// }
20 | /// ```
21 | ///
22 | /// ### tvOS
23 | ///
24 | /// ```swift
25 | /// struct ContentView: View {
26 | /// var body: some View {
27 | /// HStack {
28 | /// Image(systemName: "scribble")
29 | /// Text("Some text")
30 | /// }
31 | /// .introspect(.view, on: .tvOS(.v13, .v14, .v15, .v16, .v17, .v18)) {
32 | /// print(type(of: $0)) // some subclass of UIView
33 | /// }
34 | /// }
35 | /// }
36 | /// ```
37 | ///
38 | /// ### macOS
39 | ///
40 | /// ```swift
41 | /// struct ContentView: View {
42 | /// var body: some View {
43 | /// HStack {
44 | /// Image(systemName: "scribble")
45 | /// Text("Some text")
46 | /// }
47 | /// .introspect(.view, on: .macOS(.v10_15, .v11, .v12, .v13, .v14, .v15)) {
48 | /// print(type(of: $0)) // some subclass of NSView
49 | /// }
50 | /// }
51 | /// }
52 | /// ```
53 | ///
54 | /// ### visionOS
55 | ///
56 | /// ```swift
57 | /// struct ContentView: View {
58 | /// var body: some View {
59 | /// HStack {
60 | /// Image(systemName: "scribble")
61 | /// Text("Some text")
62 | /// }
63 | /// .introspect(.view, on: .visionOS(.v1, .v2)) {
64 | /// print(type(of: $0)) // some subclass of UIView
65 | /// }
66 | /// }
67 | /// }
68 | /// ```
69 | public struct ViewType: IntrospectableViewType {}
70 |
71 | extension IntrospectableViewType where Self == ViewType {
72 | public static var view: Self { .init() }
73 | }
74 |
75 | #if canImport(UIKit)
76 | extension iOSViewVersion {
77 | public static let v13 = Self(for: .v13)
78 | public static let v14 = Self(for: .v14)
79 | public static let v15 = Self(for: .v15)
80 | public static let v16 = Self(for: .v16)
81 | public static let v17 = Self(for: .v17)
82 | public static let v18 = Self(for: .v18)
83 | }
84 |
85 | extension tvOSViewVersion {
86 | public static let v13 = Self(for: .v13)
87 | public static let v14 = Self(for: .v14)
88 | public static let v15 = Self(for: .v15)
89 | public static let v16 = Self(for: .v16)
90 | public static let v17 = Self(for: .v17)
91 | public static let v18 = Self(for: .v18)
92 | }
93 |
94 | extension visionOSViewVersion {
95 | public static let v1 = Self(for: .v1)
96 | public static let v2 = Self(for: .v2)
97 | }
98 | #elseif canImport(AppKit)
99 | extension macOSViewVersion {
100 | public static let v10_15 = Self(for: .v10_15)
101 | public static let v11 = Self(for: .v11)
102 | public static let v12 = Self(for: .v12)
103 | public static let v13 = Self(for: .v13)
104 | public static let v14 = Self(for: .v14)
105 | public static let v15 = Self(for: .v15)
106 | }
107 | #endif
108 | #endif
109 |
--------------------------------------------------------------------------------
/Sources/Weak.swift:
--------------------------------------------------------------------------------
1 | @_spi(Advanced)
2 | @propertyWrapper
3 | public final class Weak {
4 | private weak var _wrappedValue: T?
5 |
6 | public var wrappedValue: T? {
7 | get { _wrappedValue }
8 | set { _wrappedValue = newValue }
9 | }
10 |
11 | public init(wrappedValue: T? = nil) {
12 | self._wrappedValue = wrappedValue
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/SwiftUIIntrospect.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 | spec.name = 'SwiftUIIntrospect'
3 | spec.version = ENV['LIB_VERSION']
4 | spec.license = { type: 'MIT' }
5 | spec.homepage = 'https://github.com/siteline/swiftui-introspect'
6 | spec.author = 'David Roman'
7 | spec.summary = 'Introspect underlying UIKit/AppKit components from SwiftUI.'
8 | spec.source = {
9 | git: 'https://github.com/siteline/swiftui-introspect.git',
10 | tag: spec.version
11 | }
12 |
13 | spec.source_files = 'Sources/**/*.swift'
14 |
15 | spec.swift_version = '5.7'
16 | spec.ios.deployment_target = '13.0'
17 | spec.tvos.deployment_target = '13.0'
18 | spec.osx.deployment_target = '10.15'
19 | spec.visionos.deployment_target = '1.0'
20 | end
21 |
--------------------------------------------------------------------------------
/SwiftUIIntrospect.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/SwiftUIIntrospect.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SwiftUIIntrospect.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SwiftUIIntrospect.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "pins" : [
3 | {
4 | "identity" : "swift-snapshot-testing",
5 | "kind" : "remoteSourceControl",
6 | "location" : "https://github.com/pointfreeco/swift-snapshot-testing.git",
7 | "state" : {
8 | "revision" : "dc46eeb3928a75390651fac6c1ef7f93ad59a73b",
9 | "version" : "1.11.1"
10 | }
11 | }
12 | ],
13 | "version" : 2
14 | }
15 |
--------------------------------------------------------------------------------
/SwiftUIIntrospect.xcworkspace/xcshareddata/xcschemes/SwiftUIIntrospect.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
57 |
58 |
59 |
60 |
62 |
63 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/Tests/LegacyTestsHostApp/LegacyTestsHostApp.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | @main
4 | final class AppDelegate: UIResponder, UIApplicationDelegate {
5 |
6 | var window: UIWindow?
7 |
8 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
9 | window = UIWindow(frame: UIScreen.main.bounds)
10 | window?.rootViewController = UIHostingController(rootView: EmptyView())
11 | window?.makeKeyAndVisible()
12 | return true
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Tests/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.5
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "Tests",
7 | products: [],
8 | targets: []
9 | )
10 |
--------------------------------------------------------------------------------
/Tests/Tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Tests/Tests.xcodeproj/xcshareddata/xcschemes/LegacySwiftUIIntrospectTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
17 |
23 |
24 |
25 |
26 |
27 |
37 |
39 |
45 |
46 |
47 |
48 |
54 |
55 |
61 |
62 |
63 |
64 |
66 |
67 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/Tests/Tests.xcodeproj/xcshareddata/xcschemes/SwiftUIIntrospectUITests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
34 |
35 |
36 |
37 |
47 |
49 |
55 |
56 |
57 |
58 |
64 |
66 |
72 |
73 |
74 |
75 |
77 |
78 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/ButtonTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(iOS) && !os(tvOS) && !os(visionOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @MainActor
7 | final class ButtonTests: XCTestCase {
8 | #if canImport(AppKit)
9 | typealias PlatformButton = NSButton
10 | #endif
11 |
12 | func testButton() {
13 | XCTAssertViewIntrospection(of: PlatformButton.self) { spies in
14 | let spy0 = spies[0]
15 | let spy1 = spies[1]
16 | let spy2 = spies[2]
17 | let spy3 = spies[3]
18 |
19 | VStack {
20 | Button("Button 0", action: {})
21 | .buttonStyle(.bordered)
22 | #if os(macOS)
23 | .introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
24 | #endif
25 |
26 | Button("Button 1", action: {})
27 | .buttonStyle(.borderless)
28 | #if os(macOS)
29 | .introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
30 | #endif
31 |
32 | Button("Button 2", action: {})
33 | .buttonStyle(.link)
34 | #if os(macOS)
35 | .introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
36 | #endif
37 |
38 | Button("Button 3", action: {})
39 | #if os(macOS)
40 | .introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy3)
41 | #endif
42 | }
43 | } extraAssertions: {
44 | #if canImport(AppKit)
45 | XCTAssert(Set($0.map(ObjectIdentifier.init)).count == 4)
46 | #endif
47 | }
48 | }
49 | }
50 | #endif
51 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/ColorPickerTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(tvOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @available(iOS 14, macOS 11, *)
7 | @MainActor
8 | final class ColorPickerTests: XCTestCase {
9 | #if canImport(UIKit)
10 | typealias PlatformColor = UIColor
11 | typealias PlatformColorPicker = UIColorWell
12 | #elseif canImport(AppKit)
13 | typealias PlatformColor = NSColor
14 | typealias PlatformColorPicker = NSColorWell
15 | #endif
16 |
17 | func testColorPicker() throws {
18 | guard #available(iOS 14, macOS 11, *) else {
19 | throw XCTSkip()
20 | }
21 |
22 | XCTAssertViewIntrospection(of: PlatformColorPicker.self) { spies in
23 | let spy0 = spies[0]
24 | let spy1 = spies[1]
25 | let spy2 = spies[2]
26 |
27 | VStack {
28 | ColorPicker("", selection: .constant(PlatformColor.red.cgColor))
29 | #if os(iOS) || os(visionOS)
30 | .introspect(.colorPicker, on: .iOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy0)
31 | #elseif os(macOS)
32 | .introspect(.colorPicker, on: .macOS(.v11, .v12, .v13, .v14), customize: spy0)
33 | #endif
34 |
35 | ColorPicker("", selection: .constant(PlatformColor.green.cgColor))
36 | #if os(iOS) || os(visionOS)
37 | .introspect(.colorPicker, on: .iOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy1)
38 | #elseif os(macOS)
39 | .introspect(.colorPicker, on: .macOS(.v11, .v12, .v13, .v14), customize: spy1)
40 | #endif
41 |
42 | ColorPicker("", selection: .constant(PlatformColor.blue.cgColor))
43 | #if os(iOS) || os(visionOS)
44 | .introspect(.colorPicker, on: .iOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy2)
45 | #elseif os(macOS)
46 | .introspect(.colorPicker, on: .macOS(.v11, .v12, .v13, .v14), customize: spy2)
47 | #endif
48 | }
49 | } extraAssertions: {
50 | #if canImport(UIKit)
51 | XCTAssertEqual($0[safe: 0]?.selectedColor, .red)
52 | XCTAssertEqual($0[safe: 1]?.selectedColor, .green)
53 | XCTAssertEqual($0[safe: 2]?.selectedColor, .blue)
54 | #elseif canImport(AppKit)
55 | XCTAssertEqual($0[safe: 0]?.color, .red)
56 | XCTAssertEqual($0[safe: 1]?.color, .green)
57 | XCTAssertEqual($0[safe: 2]?.color, .blue)
58 | #endif
59 | }
60 | }
61 | }
62 | #endif
63 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/DatePickerTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(tvOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @MainActor
7 | final class DatePickerTests: XCTestCase {
8 | #if canImport(UIKit)
9 | typealias PlatformDatePicker = UIDatePicker
10 | #elseif canImport(AppKit)
11 | typealias PlatformDatePicker = NSDatePicker
12 | #endif
13 |
14 | func testDatePicker() {
15 | let date0 = Date(timeIntervalSince1970: 0)
16 | let date1 = Date(timeIntervalSince1970: 5)
17 | let date2 = Date(timeIntervalSince1970: 10)
18 |
19 | XCTAssertViewIntrospection(of: PlatformDatePicker.self) { spies in
20 | let spy0 = spies[0]
21 | let spy1 = spies[1]
22 | let spy2 = spies[2]
23 |
24 | VStack {
25 | DatePicker("", selection: .constant(date0))
26 | #if os(iOS) || os(visionOS)
27 | .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy0)
28 | #elseif os(macOS)
29 | .introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
30 | #endif
31 | .cornerRadius(8)
32 |
33 | DatePicker("", selection: .constant(date1))
34 | #if os(iOS) || os(visionOS)
35 | .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy1)
36 | #elseif os(macOS)
37 | .introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
38 | #endif
39 | .cornerRadius(8)
40 |
41 | DatePicker("", selection: .constant(date2))
42 | #if os(iOS) || os(visionOS)
43 | .introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy2)
44 | #elseif os(macOS)
45 | .introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
46 | #endif
47 | }
48 | } extraAssertions: {
49 | #if canImport(UIKit)
50 | XCTAssertEqual($0[safe: 0]?.date, date0)
51 | XCTAssertEqual($0[safe: 1]?.date, date1)
52 | XCTAssertEqual($0[safe: 2]?.date, date2)
53 | #elseif canImport(AppKit)
54 | XCTAssertEqual($0[safe: 0]?.dateValue, date0)
55 | XCTAssertEqual($0[safe: 1]?.dateValue, date1)
56 | XCTAssertEqual($0[safe: 2]?.dateValue, date2)
57 | #endif
58 | }
59 | }
60 | }
61 | #endif
62 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/DatePickerWithCompactFieldStyleTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(tvOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @available(iOS 14, macOS 10.15.4, *)
7 | @MainActor
8 | final class DatePickerWithCompactStyleTests: XCTestCase {
9 | #if canImport(UIKit)
10 | typealias PlatformDatePickerWithCompactStyle = UIDatePicker
11 | #elseif canImport(AppKit)
12 | typealias PlatformDatePickerWithCompactStyle = NSDatePicker
13 | #endif
14 |
15 | func testDatePickerWithCompactStyle() throws {
16 | guard #available(iOS 14, macOS 10.15.4, *) else {
17 | throw XCTSkip()
18 | }
19 |
20 | let date0 = Date(timeIntervalSince1970: 0)
21 | let date1 = Date(timeIntervalSince1970: 5)
22 | let date2 = Date(timeIntervalSince1970: 10)
23 |
24 | XCTAssertViewIntrospection(of: PlatformDatePickerWithCompactStyle.self) { spies in
25 | let spy0 = spies[0]
26 | let spy1 = spies[1]
27 | let spy2 = spies[2]
28 |
29 | VStack {
30 | DatePicker("", selection: .constant(date0))
31 | .datePickerStyle(.compact)
32 | #if os(iOS) || os(visionOS)
33 | .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy0)
34 | #elseif os(macOS)
35 | .introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13, .v14), customize: spy0)
36 | #endif
37 | .cornerRadius(8)
38 |
39 | DatePicker("", selection: .constant(date1))
40 | .datePickerStyle(.compact)
41 | #if os(iOS) || os(visionOS)
42 | .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy1)
43 | #elseif os(macOS)
44 | .introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13, .v14), customize: spy1)
45 | #endif
46 | .cornerRadius(8)
47 |
48 | DatePicker("", selection: .constant(date2))
49 | .datePickerStyle(.compact)
50 | #if os(iOS) || os(visionOS)
51 | .introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy2)
52 | #elseif os(macOS)
53 | .introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13, .v14), customize: spy2)
54 | #endif
55 | }
56 | } extraAssertions: {
57 | #if canImport(UIKit)
58 | XCTAssertEqual($0[safe: 0]?.date, date0)
59 | XCTAssertEqual($0[safe: 1]?.date, date1)
60 | XCTAssertEqual($0[safe: 2]?.date, date2)
61 | #elseif canImport(AppKit)
62 | XCTAssertEqual($0[safe: 0]?.dateValue, date0)
63 | XCTAssertEqual($0[safe: 1]?.dateValue, date1)
64 | XCTAssertEqual($0[safe: 2]?.dateValue, date2)
65 | #endif
66 | }
67 | }
68 | }
69 | #endif
70 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/DatePickerWithFieldStyleTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(iOS) && !os(tvOS) && !os(visionOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @MainActor
7 | final class DatePickerWithFieldStyleTests: XCTestCase {
8 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
9 | typealias PlatformDatePickerWithFieldStyle = NSDatePicker
10 | #endif
11 |
12 | func testDatePickerWithFieldStyle() {
13 | let date0 = Date(timeIntervalSince1970: 0)
14 | let date1 = Date(timeIntervalSince1970: 5)
15 | let date2 = Date(timeIntervalSince1970: 10)
16 |
17 | XCTAssertViewIntrospection(of: PlatformDatePickerWithFieldStyle.self) { spies in
18 | let spy0 = spies[0]
19 | let spy1 = spies[1]
20 | let spy2 = spies[2]
21 |
22 | VStack {
23 | DatePicker("", selection: .constant(date0))
24 | .datePickerStyle(.field)
25 | #if os(macOS)
26 | .introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
27 | #endif
28 | .cornerRadius(8)
29 |
30 | DatePicker("", selection: .constant(date1))
31 | .datePickerStyle(.field)
32 | #if os(macOS)
33 | .introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
34 | #endif
35 | .cornerRadius(8)
36 |
37 | DatePicker("", selection: .constant(date2))
38 | .datePickerStyle(.field)
39 | #if os(macOS)
40 | .introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
41 | #endif
42 | }
43 | } extraAssertions: {
44 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
45 | XCTAssertEqual($0[safe: 0]?.dateValue, date0)
46 | XCTAssertEqual($0[safe: 1]?.dateValue, date1)
47 | XCTAssertEqual($0[safe: 2]?.dateValue, date2)
48 | #endif
49 | }
50 | }
51 | }
52 | #endif
53 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/DatePickerWithGraphicalStyleTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(tvOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @available(iOS 14, *)
7 | @MainActor
8 | final class DatePickerWithGraphicalStyleTests: XCTestCase {
9 | #if canImport(UIKit)
10 | typealias PlatformDatePickerWithGraphicalStyle = UIDatePicker
11 | #elseif canImport(AppKit)
12 | typealias PlatformDatePickerWithGraphicalStyle = NSDatePicker
13 | #endif
14 |
15 | func testDatePickerWithGraphicalStyle() throws {
16 | guard #available(iOS 14, *) else {
17 | throw XCTSkip()
18 | }
19 |
20 | let date0 = Date(timeIntervalSince1970: 0)
21 | let date1 = Date(timeIntervalSince1970: 3600 * 24 * 1)
22 | let date2 = Date(timeIntervalSince1970: 3600 * 24 * 2)
23 |
24 | XCTAssertViewIntrospection(of: PlatformDatePickerWithGraphicalStyle.self) { spies in
25 | let spy0 = spies[0]
26 | let spy1 = spies[1]
27 | let spy2 = spies[2]
28 |
29 | VStack {
30 | DatePicker("", selection: .constant(date0))
31 | .datePickerStyle(.graphical)
32 | #if os(iOS) || os(visionOS)
33 | .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy0)
34 | #elseif os(macOS)
35 | .introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
36 | #endif
37 | .cornerRadius(8)
38 |
39 | DatePicker("", selection: .constant(date1))
40 | .datePickerStyle(.graphical)
41 | #if os(iOS) || os(visionOS)
42 | .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy1)
43 | #elseif os(macOS)
44 | .introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
45 | #endif
46 | .cornerRadius(8)
47 |
48 | DatePicker("", selection: .constant(date2))
49 | .datePickerStyle(.graphical)
50 | #if os(iOS) || os(visionOS)
51 | .introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy2)
52 | #elseif os(macOS)
53 | .introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
54 | #endif
55 | }
56 | } extraAssertions: {
57 | #if canImport(UIKit)
58 | XCTAssertEqual($0[safe: 0]?.date, date0)
59 | XCTAssertEqual($0[safe: 1]?.date, date1)
60 | XCTAssertEqual($0[safe: 2]?.date, date2)
61 | #elseif canImport(AppKit)
62 | XCTAssertEqual($0[safe: 0]?.dateValue, date0)
63 | XCTAssertEqual($0[safe: 1]?.dateValue, date1)
64 | XCTAssertEqual($0[safe: 2]?.dateValue, date2)
65 | #endif
66 | }
67 | }
68 | }
69 | #endif
70 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/DatePickerWithStepperFieldStyleTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(iOS) && !os(tvOS) && !os(visionOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @MainActor
7 | final class DatePickerWithStepperFieldStyleTests: XCTestCase {
8 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
9 | typealias PlatformDatePickerWithStepperFieldStyle = NSDatePicker
10 | #endif
11 |
12 | func testDatePickerWithStepperFieldStyle() {
13 | let date0 = Date(timeIntervalSince1970: 0)
14 | let date1 = Date(timeIntervalSince1970: 5)
15 | let date2 = Date(timeIntervalSince1970: 10)
16 |
17 | XCTAssertViewIntrospection(of: PlatformDatePickerWithStepperFieldStyle.self) { spies in
18 | let spy0 = spies[0]
19 | let spy1 = spies[1]
20 | let spy2 = spies[2]
21 |
22 | VStack {
23 | DatePicker("", selection: .constant(date0))
24 | .datePickerStyle(.stepperField)
25 | #if os(macOS)
26 | .introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
27 | #endif
28 | .cornerRadius(8)
29 |
30 | DatePicker("", selection: .constant(date1))
31 | .datePickerStyle(.stepperField)
32 | #if os(macOS)
33 | .introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
34 | #endif
35 | .cornerRadius(8)
36 |
37 | DatePicker("", selection: .constant(date2))
38 | .datePickerStyle(.stepperField)
39 | #if os(macOS)
40 | .introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
41 | #endif
42 | }
43 | } extraAssertions: {
44 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
45 | XCTAssertEqual($0[safe: 0]?.dateValue, date0)
46 | XCTAssertEqual($0[safe: 1]?.dateValue, date1)
47 | XCTAssertEqual($0[safe: 2]?.dateValue, date2)
48 | #endif
49 | }
50 | }
51 | }
52 | #endif
53 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/DatePickerWithWheelStyleTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(tvOS) && !os(macOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @MainActor
7 | final class DatePickerWithWheelStyleTests: XCTestCase {
8 | #if canImport(UIKit)
9 | typealias PlatformDatePickerWithWheelStyle = UIDatePicker
10 | #endif
11 |
12 | func testDatePickerWithWheelStyle() {
13 | let date0 = Date(timeIntervalSince1970: 0)
14 | let date1 = Date(timeIntervalSince1970: 5)
15 | let date2 = Date(timeIntervalSince1970: 10)
16 |
17 | XCTAssertViewIntrospection(of: PlatformDatePickerWithWheelStyle.self) { spies in
18 | let spy0 = spies[0]
19 | let spy1 = spies[1]
20 | let spy2 = spies[2]
21 |
22 | VStack {
23 | DatePicker("", selection: .constant(date0))
24 | .datePickerStyle(.wheel)
25 | #if os(iOS) || os(visionOS)
26 | .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy0)
27 | #endif
28 | .cornerRadius(8)
29 |
30 | DatePicker("", selection: .constant(date1))
31 | .datePickerStyle(.wheel)
32 | #if os(iOS) || os(visionOS)
33 | .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy1)
34 | #endif
35 | .cornerRadius(8)
36 |
37 | DatePicker("", selection: .constant(date2))
38 | .datePickerStyle(.wheel)
39 | #if os(iOS) || os(visionOS)
40 | .introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy2)
41 | #endif
42 | }
43 | } extraAssertions: {
44 | #if canImport(UIKit)
45 | XCTAssertEqual($0[safe: 0]?.date, date0)
46 | XCTAssertEqual($0[safe: 1]?.date, date1)
47 | XCTAssertEqual($0[safe: 2]?.date, date2)
48 | #endif
49 | }
50 | }
51 | }
52 | #endif
53 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/FormTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(macOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @MainActor
7 | final class FormTests: XCTestCase {
8 | #if canImport(UIKit)
9 | typealias PlatformForm = UIScrollView // covers both UITableView and UICollectionView
10 | #elseif canImport(AppKit)
11 | typealias PlatformForm = NSScrollView
12 | #endif
13 |
14 | func testForm() throws {
15 | XCTAssertViewIntrospection(of: PlatformForm.self) { spies in
16 | let spy0 = spies[0]
17 | let spy1 = spies[1]
18 |
19 | HStack {
20 | Form {
21 | Text("Item 1")
22 | }
23 | #if os(iOS) || os(tvOS) || os(visionOS)
24 | .introspect(.form, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18)) { spy0($0) }
25 | .introspect(.form, on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2)) { spy0($0) }
26 | #endif
27 |
28 | Form {
29 | Text("Item 1")
30 | #if os(iOS) || os(tvOS) || os(visionOS)
31 | .introspect(.form, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), scope: .ancestor) { spy1($0) }
32 | .introspect(.form, on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2), scope: .ancestor) { spy1($0) }
33 | #endif
34 | }
35 | }
36 | } extraAssertions: {
37 | XCTAssert($0[safe: 0] !== $0[safe: 1])
38 | }
39 | }
40 | }
41 | #endif
42 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/FormWithGroupedStyleTests.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import SwiftUIIntrospect
3 | import XCTest
4 |
5 | @available(iOS 16, tvOS 16, macOS 13, *)
6 | @MainActor
7 | final class FormWithGroupedStyleTests: XCTestCase {
8 | #if canImport(UIKit)
9 | typealias PlatformFormWithGroupedStyle = UIScrollView // covers both UITableView and UICollectionView
10 | #elseif canImport(AppKit)
11 | typealias PlatformFormWithGroupedStyle = NSScrollView
12 | #endif
13 |
14 | func testFormWithGroupedStyle() throws {
15 | guard #available(iOS 16, tvOS 16, macOS 13, *) else {
16 | throw XCTSkip()
17 | }
18 |
19 | XCTAssertViewIntrospection(of: PlatformFormWithGroupedStyle.self) { spies in
20 | let spy0 = spies[0]
21 | let spy1 = spies[1]
22 |
23 | HStack {
24 | Form {
25 | Text("Item 1")
26 | }
27 | .formStyle(.grouped)
28 | #if os(iOS) || os(tvOS) || os(visionOS)
29 | .introspect(.form(style: .grouped), on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2)) { spy0($0) }
30 | .introspect(.form(style: .grouped), on: .tvOS(.v16, .v17, .v18)) { spy0($0) }
31 | #elseif os(macOS)
32 | .introspect(.form(style: .grouped), on: .macOS(.v13, .v14)) { spy0($0) }
33 | #endif
34 |
35 | Form {
36 | Text("Item 1")
37 | #if os(iOS) || os(tvOS) || os(visionOS)
38 | .introspect(.form(style: .grouped), on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2), scope: .ancestor) { spy1($0) }
39 | .introspect(.form(style: .grouped), on: .tvOS(.v16, .v17, .v18), scope: .ancestor) { spy1($0) }
40 | #elseif os(macOS)
41 | .introspect(.form(style: .grouped), on: .macOS(.v13, .v14), scope: .ancestor) { spy1($0) }
42 | #endif
43 | }
44 | .formStyle(.grouped)
45 | }
46 | } extraAssertions: {
47 | XCTAssert($0[safe: 0] !== $0[safe: 1])
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/FullScreenCoverTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(macOS) && !targetEnvironment(macCatalyst)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @available(iOS 14, tvOS 14, *)
7 | @MainActor
8 | final class FullScreenCoverTests: XCTestCase {
9 | func testPresentationAsFullScreenCover() throws {
10 | guard #available(iOS 14, tvOS 14, *) else {
11 | throw XCTSkip()
12 | }
13 |
14 | XCTAssertViewIntrospection(of: UIPresentationController.self) { spies in
15 | let spy0 = spies[0]
16 |
17 | Text("Root")
18 | .fullScreenCover(isPresented: .constant(true)) {
19 | Text("Content")
20 | #if os(iOS) || os(tvOS) || os(visionOS)
21 | .introspect(
22 | .fullScreenCover,
23 | on: .iOS(.v14, .v15, .v16, .v17, .v18), .tvOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2),
24 | customize: spy0
25 | )
26 | #endif
27 | }
28 | }
29 | }
30 | }
31 | #endif
32 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/ListCellTests.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import SwiftUIIntrospect
3 | import XCTest
4 |
5 | @MainActor
6 | final class ListCellTests: XCTestCase {
7 | #if canImport(UIKit)
8 | typealias PlatformListCell = UIView // covers both UITableViewCell and UICollectionViewCell
9 | #elseif canImport(AppKit)
10 | typealias PlatformListCell = NSTableCellView
11 | #endif
12 |
13 | func testListCell() {
14 | XCTAssertViewIntrospection(of: PlatformListCell.self) { spies in
15 | let spy = spies[0]
16 |
17 | List {
18 | Text("Item 1")
19 | #if os(iOS) || os(tvOS) || os(visionOS)
20 | .introspect(.listCell, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18)) { spy($0) }
21 | .introspect(.listCell, on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2)) { spy($0) }
22 | #elseif os(macOS)
23 | .introspect(.listCell, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy($0) }
24 | #endif
25 | }
26 | }
27 | }
28 |
29 | func testMaskedListCell() {
30 | XCTAssertViewIntrospection(of: PlatformListCell.self) { spies in
31 | let spy = spies[0]
32 |
33 | List {
34 | Text("Item 1")
35 | #if os(iOS) || os(tvOS) || os(visionOS)
36 | .introspect(.listCell, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18)) { spy($0) }
37 | .introspect(.listCell, on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2)) { spy($0) }
38 | #elseif os(macOS)
39 | .introspect(.listCell, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy($0) }
40 | #endif
41 | .clipped()
42 | .clipShape(RoundedRectangle(cornerRadius: 20.0))
43 | .cornerRadius(2.0)
44 | }
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/ListWithBorderedStyleTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(iOS) && !os(tvOS) && !os(visionOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @available(macOS 12, *)
7 | @MainActor
8 | final class ListWithBorderedStyleTests: XCTestCase {
9 | #if canImport(AppKit)
10 | typealias PlatformListWithBorderedStyle = NSTableView
11 | #endif
12 |
13 | func testListWithBorderedStyle() throws {
14 | guard #available(macOS 12, *) else {
15 | throw XCTSkip()
16 | }
17 |
18 | XCTAssertViewIntrospection(of: PlatformListWithBorderedStyle.self) { spies in
19 | let spy0 = spies[0]
20 | let spy1 = spies[1]
21 |
22 | HStack {
23 | List {
24 | Text("Item 1")
25 | }
26 | .listStyle(.bordered)
27 | #if os(macOS)
28 | .introspect(.list(style: .bordered), on: .macOS(.v12, .v13, .v14)) { spy0($0) }
29 | #endif
30 |
31 | List {
32 | Text("Item 1")
33 | #if os(macOS)
34 | .introspect(.list(style: .bordered), on: .macOS(.v12, .v13, .v14), scope: .ancestor) { spy1($0) }
35 | #endif
36 | }
37 | .listStyle(.bordered)
38 | }
39 | } extraAssertions: {
40 | XCTAssert($0[safe: 0] !== $0[safe: 1])
41 | }
42 | }
43 | }
44 | #endif
45 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/ListWithGroupedStyleTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(macOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @MainActor
7 | final class ListWithGroupedStyleTests: XCTestCase {
8 | #if canImport(UIKit)
9 | typealias PlatformListWithGroupedStyle = UIScrollView // covers both UITableView and UICollectionView
10 | #endif
11 |
12 | func testListWithGroupedStyle() {
13 | XCTAssertViewIntrospection(of: PlatformListWithGroupedStyle.self) { spies in
14 | let spy0 = spies[0]
15 | let spy1 = spies[1]
16 |
17 | HStack {
18 | List {
19 | Text("Item 1")
20 | }
21 | .listStyle(.grouped)
22 | #if os(iOS) || os(tvOS) || os(visionOS)
23 | .introspect(.list(style: .grouped), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18)) { spy0($0) }
24 | .introspect(.list(style: .grouped), on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2)) { spy0($0) }
25 | #endif
26 |
27 | List {
28 | Text("Item 1")
29 | #if os(iOS) || os(tvOS) || os(visionOS)
30 | .introspect(.list(style: .grouped), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), scope: .ancestor) { spy1($0) }
31 | .introspect(.list(style: .grouped), on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2), scope: .ancestor) { spy1($0) }
32 | #endif
33 | }
34 | .listStyle(.grouped)
35 | }
36 | } extraAssertions: {
37 | XCTAssert($0[safe: 0] !== $0[safe: 1])
38 | }
39 | }
40 | }
41 | #endif
42 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/ListWithInsetGroupedStyleTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(tvOS) && !os(macOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @available(iOS 14, *)
7 | @MainActor
8 | final class ListWithInsetGroupedStyleTests: XCTestCase {
9 | #if canImport(UIKit)
10 | typealias PlatformListWithInsetGroupedStyle = UIScrollView // covers both UITableView and UICollectionView
11 | #endif
12 |
13 | func testListWithInsetGroupedStyle() throws {
14 | guard #available(iOS 14, *) else {
15 | throw XCTSkip()
16 | }
17 |
18 | XCTAssertViewIntrospection(of: PlatformListWithInsetGroupedStyle.self) { spies in
19 | let spy0 = spies[0]
20 | let spy1 = spies[1]
21 |
22 | HStack {
23 | List {
24 | Text("Item 1")
25 | }
26 | .listStyle(.insetGrouped)
27 | #if os(iOS) || os(visionOS)
28 | .introspect(.list(style: .insetGrouped), on: .iOS(.v14, .v15)) { spy0($0) }
29 | .introspect(.list(style: .insetGrouped), on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2)) { spy0($0) }
30 | #endif
31 |
32 | List {
33 | Text("Item 1")
34 | #if os(iOS) || os(visionOS)
35 | .introspect(.list(style: .insetGrouped), on: .iOS(.v14, .v15), scope: .ancestor) { spy1($0) }
36 | .introspect(.list(style: .insetGrouped), on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2), scope: .ancestor) { spy1($0) }
37 | #endif
38 | }
39 | .listStyle(.insetGrouped)
40 | }
41 | } extraAssertions: {
42 | XCTAssert($0[safe: 0] !== $0[safe: 1])
43 | }
44 | }
45 | }
46 | #endif
47 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/ListWithInsetStyleTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(tvOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @available(iOS 14, macOS 11, *)
7 | @MainActor
8 | final class ListWithInsetStyleTests: XCTestCase {
9 | #if canImport(UIKit)
10 | typealias PlatformListWithInsetStyle = UIScrollView // covers both UITableView and UICollectionView
11 | #elseif canImport(AppKit)
12 | typealias PlatformListWithInsetStyle = NSTableView
13 | #endif
14 |
15 | func testListWithInsetStyle() throws {
16 | guard #available(iOS 14, macOS 11, *) else {
17 | throw XCTSkip()
18 | }
19 |
20 | XCTAssertViewIntrospection(of: PlatformListWithInsetStyle.self) { spies in
21 | let spy0 = spies[0]
22 | let spy1 = spies[1]
23 |
24 | HStack {
25 | List {
26 | Text("Item 1")
27 | }
28 | .listStyle(.inset)
29 | #if os(iOS) || os(visionOS)
30 | .introspect(.list(style: .inset), on: .iOS(.v14, .v15)) { spy0($0) }
31 | .introspect(.list(style: .inset), on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2)) { spy0($0) }
32 | #elseif os(macOS)
33 | .introspect(.list(style: .inset), on: .macOS(.v11, .v12, .v13, .v14)) { spy0($0) }
34 | #endif
35 |
36 | List {
37 | Text("Item 1")
38 | #if os(iOS) || os(visionOS)
39 | .introspect(.list(style: .inset), on: .iOS(.v14, .v15), scope: .ancestor) { spy1($0) }
40 | .introspect(.list(style: .inset), on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2), scope: .ancestor) { spy1($0) }
41 | #elseif os(macOS)
42 | .introspect(.list(style: .inset), on: .macOS(.v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) }
43 | #endif
44 | }
45 | .listStyle(.inset)
46 | }
47 | } extraAssertions: {
48 | XCTAssert($0[safe: 0] !== $0[safe: 1])
49 | }
50 | }
51 | }
52 | #endif
53 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/ListWithPlainStyleTests.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import SwiftUIIntrospect
3 | import XCTest
4 |
5 | @MainActor
6 | final class ListWithPlainStyleTests: XCTestCase {
7 | #if canImport(UIKit)
8 | typealias PlatformListWithPlainStyle = UIScrollView // covers both UITableView and UICollectionView
9 | #elseif canImport(AppKit)
10 | typealias PlatformListWithPlainStyle = NSTableView
11 | #endif
12 |
13 | func testListWithPlainStyle() {
14 | XCTAssertViewIntrospection(of: PlatformListWithPlainStyle.self) { spies in
15 | let spy0 = spies[0]
16 | let spy1 = spies[1]
17 |
18 | HStack {
19 | List {
20 | Text("Item 1")
21 | }
22 | .listStyle(.plain)
23 | #if os(iOS) || os(tvOS) || os(visionOS)
24 | .introspect(.list(style: .plain), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18)) { spy0($0) }
25 | .introspect(.list(style: .plain), on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2)) { spy0($0) }
26 | #elseif os(macOS)
27 | .introspect(.list(style: .plain), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy0($0) }
28 | #endif
29 |
30 | List {
31 | Text("Item 1")
32 | #if os(iOS) || os(tvOS) || os(visionOS)
33 | .introspect(.list(style: .plain), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), scope: .ancestor) { spy1($0) }
34 | .introspect(.list(style: .plain), on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2), scope: .ancestor) { spy1($0) }
35 | #elseif os(macOS)
36 | .introspect(.list(style: .plain), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) }
37 | #endif
38 | }
39 | .listStyle(.plain)
40 | }
41 | } extraAssertions: {
42 | XCTAssert($0[safe: 0] !== $0[safe: 1])
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/ListWithSidebarStyleTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(tvOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @available(iOS 14, macOS 10.15, *)
7 | @MainActor
8 | final class ListWithSidebarStyleTests: XCTestCase {
9 | #if canImport(UIKit)
10 | typealias PlatformListWithSidebarStyle = UIScrollView // covers both UITableView and UICollectionView
11 | #elseif canImport(AppKit)
12 | typealias PlatformListWithSidebarStyle = NSTableView
13 | #endif
14 |
15 | func testListWithSidebarStyle() throws {
16 | guard #available(iOS 14, macOS 10.15, *) else {
17 | throw XCTSkip()
18 | }
19 |
20 | XCTAssertViewIntrospection(of: PlatformListWithSidebarStyle.self) { spies in
21 | let spy0 = spies[0]
22 | let spy1 = spies[1]
23 |
24 | HStack {
25 | List {
26 | Text("Item 1")
27 | }
28 | .listStyle(.sidebar)
29 | #if os(iOS) || os(visionOS)
30 | .introspect(.list(style: .sidebar), on: .iOS(.v14, .v15)) { spy0($0) }
31 | .introspect(.list(style: .sidebar), on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2)) { spy0($0) }
32 | #elseif os(macOS)
33 | .introspect(.list(style: .sidebar), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy0($0) }
34 | #endif
35 |
36 | List {
37 | Text("Item 1")
38 | #if os(iOS) || os(visionOS)
39 | .introspect(.list(style: .sidebar), on: .iOS(.v14, .v15), scope: .ancestor) { spy1($0) }
40 | .introspect(.list(style: .sidebar), on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2), scope: .ancestor) { spy1($0) }
41 | #elseif os(macOS)
42 | .introspect(.list(style: .sidebar), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) }
43 | #endif
44 | }
45 | .listStyle(.sidebar)
46 | }
47 | } extraAssertions: {
48 | XCTAssert($0[safe: 0] !== $0[safe: 1])
49 | }
50 | }
51 | }
52 | #endif
53 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/MapTests.swift:
--------------------------------------------------------------------------------
1 | #if canImport(MapKit)
2 | import MapKit
3 | import SwiftUI
4 | import SwiftUIIntrospect
5 | import XCTest
6 |
7 | @available(iOS 14, tvOS 14, macOS 11, *)
8 | @MainActor
9 | final class MapTests: XCTestCase {
10 | typealias PlatformMap = MKMapView
11 |
12 | func testMap() throws {
13 | guard #available(iOS 14, tvOS 14, macOS 11, *) else {
14 | throw XCTSkip()
15 | }
16 |
17 | XCTAssertViewIntrospection(of: PlatformMap.self) { spies in
18 | let spy0 = spies[0]
19 | let spy1 = spies[1]
20 | let spy2 = spies[2]
21 |
22 | let region = Binding.constant(MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 51.507222, longitude: -0.1275), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)))
23 |
24 | VStack {
25 | Map(coordinateRegion: region)
26 | .introspect(
27 | .map,
28 | on: .iOS(.v14, .v15, .v16, .v17, .v18), .tvOS(.v14, .v15, .v16, .v17, .v18), .macOS(.v11, .v12, .v13, .v14), .visionOS(.v1, .v2),
29 | customize: spy0
30 | )
31 |
32 | Map(coordinateRegion: region)
33 | .introspect(
34 | .map,
35 | on: .iOS(.v14, .v15, .v16, .v17, .v18), .tvOS(.v14, .v15, .v16, .v17, .v18), .macOS(.v11, .v12, .v13, .v14), .visionOS(.v1, .v2),
36 | customize: spy1
37 | )
38 |
39 | Map(coordinateRegion: region)
40 | .introspect(
41 | .map,
42 | on: .iOS(.v14, .v15, .v16, .v17, .v18), .tvOS(.v14, .v15, .v16, .v17, .v18), .macOS(.v11, .v12, .v13, .v14), .visionOS(.v1, .v2),
43 | customize: spy2
44 | )
45 | }
46 | } extraAssertions: {
47 | XCTAssertNotIdentical($0[safe: 0], $0[safe: 1])
48 | XCTAssertNotIdentical($0[safe: 0], $0[safe: 2])
49 | XCTAssertNotIdentical($0[safe: 1], $0[safe: 2])
50 | }
51 | }
52 | }
53 | #endif
54 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/NavigationSplitViewTests.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import SwiftUIIntrospect
3 | import XCTest
4 |
5 | @available(iOS 16, tvOS 16, macOS 13, *)
6 | @MainActor
7 | final class NavigationSplitViewTests: XCTestCase {
8 | #if canImport(UIKit) && (os(iOS) || os(visionOS))
9 | typealias PlatformNavigationSplitView = UISplitViewController
10 | #elseif canImport(UIKit) && os(tvOS)
11 | typealias PlatformNavigationSplitView = UINavigationController
12 | #elseif canImport(AppKit)
13 | typealias PlatformNavigationSplitView = NSSplitView
14 | #endif
15 |
16 | func testNavigationSplitView() throws {
17 | guard #available(iOS 16, tvOS 16, macOS 13, *) else {
18 | throw XCTSkip()
19 | }
20 | guard #unavailable(tvOS 18) else {
21 | throw XCTSkip()
22 | }
23 |
24 | XCTAssertViewIntrospection(of: PlatformNavigationSplitView.self) { spies in
25 | let spy = spies[0]
26 |
27 | NavigationSplitView {
28 | ZStack {
29 | Color.red
30 | Text("Root")
31 | }
32 | } detail: {
33 | ZStack {
34 | Color.blue
35 | Text("Detail")
36 | }
37 | }
38 | #if os(iOS) || os(visionOS)
39 | .introspect(.navigationSplitView, on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy)
40 | #elseif os(tvOS)
41 | .introspect(.navigationSplitView, on: .tvOS(.v16, .v17), customize: spy)
42 | #elseif os(macOS)
43 | .introspect(.navigationSplitView, on: .macOS(.v13, .v14), customize: spy)
44 | #endif
45 | }
46 | }
47 |
48 | func testNavigationSplitViewAsAncestor() throws {
49 | guard #available(iOS 16, tvOS 16, macOS 13, *) else {
50 | throw XCTSkip()
51 | }
52 | guard #unavailable(tvOS 18) else {
53 | throw XCTSkip()
54 | }
55 |
56 | XCTAssertViewIntrospection(of: PlatformNavigationSplitView.self) { spies in
57 | let spy = spies[0]
58 |
59 | // 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.
60 | NavigationSplitView(columnVisibility: .constant(.all)) {
61 | ZStack {
62 | Color.red
63 | Text("Sidebar")
64 | #if os(iOS) || os(visionOS)
65 | .introspect(.navigationSplitView, on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2), scope: .ancestor, customize: spy)
66 | #elseif os(tvOS)
67 | .introspect(.navigationSplitView, on: .tvOS(.v16, .v17), scope: .ancestor, customize: spy)
68 | #elseif os(macOS)
69 | .introspect(.navigationSplitView, on: .macOS(.v13, .v14), scope: .ancestor, customize: spy)
70 | #endif
71 | }
72 | } detail: {
73 | ZStack {
74 | Color.blue
75 | Text("Detail")
76 | }
77 | }
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/NavigationStackTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(macOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @available(iOS 16, tvOS 16, *)
7 | @MainActor
8 | final class NavigationStackTests: XCTestCase {
9 | #if canImport(UIKit)
10 | typealias PlatformNavigationStack = UINavigationController
11 | #endif
12 |
13 | func testNavigationStack() throws {
14 | guard #available(iOS 16, tvOS 16, *) else {
15 | throw XCTSkip()
16 | }
17 |
18 | XCTAssertViewIntrospection(of: PlatformNavigationStack.self) { spies in
19 | let spy = spies[0]
20 |
21 | NavigationStack {
22 | ZStack {
23 | Color.red
24 | Text("Something")
25 | }
26 | }
27 | #if os(iOS) || os(tvOS) || os(visionOS)
28 | .introspect(.navigationStack, on: .iOS(.v16, .v17, .v18), .tvOS(.v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy)
29 | #endif
30 | }
31 | }
32 |
33 | func testNavigationStackAsAncestor() throws {
34 | guard #available(iOS 16, tvOS 16, *) else {
35 | throw XCTSkip()
36 | }
37 |
38 | XCTAssertViewIntrospection(of: PlatformNavigationStack.self) { spies in
39 | let spy = spies[0]
40 |
41 | NavigationStack {
42 | ZStack {
43 | Color.red
44 | Text("Something")
45 | #if os(iOS) || os(tvOS) || os(visionOS)
46 | .introspect(.navigationStack, on: .iOS(.v16, .v17, .v18), .tvOS(.v16, .v17, .v18), .visionOS(.v1, .v2), scope: .ancestor, customize: spy)
47 | #endif
48 | }
49 | }
50 | }
51 | }
52 | }
53 | #endif
54 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/NavigationViewWithColumnsStyleTests.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import SwiftUIIntrospect
3 | import XCTest
4 |
5 | @MainActor
6 | final class NavigationViewWithColumnsStyleTests: XCTestCase {
7 | #if canImport(UIKit) && (os(iOS) || os(visionOS))
8 | typealias PlatformNavigationViewWithColumnsStyle = UISplitViewController
9 | #elseif canImport(UIKit) && os(tvOS)
10 | typealias PlatformNavigationViewWithColumnsStyle = UINavigationController
11 | #elseif canImport(AppKit)
12 | typealias PlatformNavigationViewWithColumnsStyle = NSSplitView
13 | #endif
14 |
15 | func testNavigationViewWithColumnsStyle() {
16 | XCTAssertViewIntrospection(of: PlatformNavigationViewWithColumnsStyle.self) { spies in
17 | let spy = spies[0]
18 |
19 | NavigationView {
20 | ZStack {
21 | Color.red
22 | Text("Something")
23 | }
24 | }
25 | .navigationViewStyle(DoubleColumnNavigationViewStyle())
26 | #if os(iOS) || os(visionOS)
27 | .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy)
28 | #elseif os(tvOS)
29 | .introspect(.navigationView(style: .columns), on: .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), customize: spy)
30 | #elseif os(macOS)
31 | .introspect(.navigationView(style: .columns), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy)
32 | #endif
33 | }
34 | }
35 |
36 | func testNavigationViewWithColumnsStyleAsAncestor() {
37 | XCTAssertViewIntrospection(of: PlatformNavigationViewWithColumnsStyle.self) { spies in
38 | let spy = spies[0]
39 |
40 | NavigationView {
41 | ZStack {
42 | Color.red
43 | Text("Something")
44 | #if os(iOS) || os(visionOS)
45 | .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), scope: .ancestor, customize: spy)
46 | #elseif os(tvOS)
47 | .introspect(.navigationView(style: .columns), on: .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), scope: .ancestor, customize: spy)
48 | #elseif os(macOS)
49 | .introspect(.navigationView(style: .columns), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor, customize: spy)
50 | #endif
51 | }
52 | }
53 | .navigationViewStyle(DoubleColumnNavigationViewStyle())
54 | #if os(iOS)
55 | // 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.
56 | .introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18)) {
57 | $0.preferredDisplayMode = .oneOverSecondary
58 | }
59 | #endif
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/NavigationViewWithStackStyleTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(macOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @MainActor
7 | final class NavigationViewWithStackStyleTests: XCTestCase {
8 | #if canImport(UIKit)
9 | typealias PlatformNavigationViewWithStackStyle = UINavigationController
10 | #endif
11 |
12 | func testNavigationViewWithStackStyle() {
13 | XCTAssertViewIntrospection(of: PlatformNavigationViewWithStackStyle.self) { spies in
14 | let spy = spies[0]
15 |
16 | NavigationView {
17 | ZStack {
18 | Color.red
19 | Text("Something")
20 | }
21 | }
22 | .navigationViewStyle(.stack)
23 | #if os(iOS) || os(tvOS) || os(visionOS)
24 | .introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy)
25 | #endif
26 | }
27 | }
28 |
29 | func testNavigationViewWithStackStyleAsAncestor() {
30 | XCTAssertViewIntrospection(of: PlatformNavigationViewWithStackStyle.self) { spies in
31 | let spy = spies[0]
32 |
33 | NavigationView {
34 | ZStack {
35 | Color.red
36 | Text("Something")
37 | #if os(iOS) || os(tvOS) || os(visionOS)
38 | .introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), scope: .ancestor, customize: spy)
39 | #endif
40 | }
41 | }
42 | .navigationViewStyle(.stack)
43 | }
44 | }
45 | }
46 | #endif
47 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/PageControlTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(macOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @available(iOS 14, tvOS 14, *)
7 | @MainActor
8 | final class PageControlTests: XCTestCase {
9 | #if canImport(UIKit)
10 | typealias PlatformPageControl = UIPageControl
11 | #endif
12 |
13 | func testPageControl() throws {
14 | guard #available(iOS 14, tvOS 14, *) else {
15 | throw XCTSkip()
16 | }
17 |
18 | XCTAssertViewIntrospection(of: PlatformPageControl.self) { spies in
19 | let spy = spies[0]
20 |
21 | TabView {
22 | Text("Page 1").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red)
23 | Text("Page 2").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.blue)
24 | }
25 | .tabViewStyle(.page(indexDisplayMode: .always))
26 | #if os(iOS) || os(tvOS) || os(visionOS)
27 | .introspect(.pageControl, on: .iOS(.v14, .v15, .v16, .v17, .v18), .tvOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy)
28 | #endif
29 | }
30 | }
31 | }
32 | #endif
33 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/PickerWithMenuStyleTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(iOS) && !os(tvOS) && !os(visionOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @MainActor
7 | final class PickerWithMenuStyleTests: XCTestCase {
8 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
9 | typealias PlatformPickerWithMenuStyle = NSPopUpButton
10 | #endif
11 |
12 | func testPickerWithMenuStyle() {
13 | XCTAssertViewIntrospection(of: PlatformPickerWithMenuStyle.self) { spies in
14 | let spy0 = spies[0]
15 | let spy1 = spies[1]
16 | let spy2 = spies[2]
17 |
18 | VStack {
19 | Picker("Pick", selection: .constant("1")) {
20 | Text("1").tag("1")
21 | }
22 | .pickerStyle(.menu)
23 | #if os(macOS)
24 | .introspect(.picker(style: .menu), on: .macOS(.v11, .v12, .v13, .v14), customize: spy0)
25 | #endif
26 | .cornerRadius(8)
27 |
28 | Picker("Pick", selection: .constant("1")) {
29 | Text("1").tag("1")
30 | Text("2").tag("2")
31 | }
32 | .pickerStyle(.menu)
33 | #if os(macOS)
34 | .introspect(.picker(style: .menu), on: .macOS(.v11, .v12, .v13, .v14), customize: spy1)
35 | #endif
36 | .cornerRadius(8)
37 |
38 | Picker("Pick", selection: .constant("1")) {
39 | Text("1").tag("1")
40 | Text("2").tag("2")
41 | Text("3").tag("3")
42 | }
43 | .pickerStyle(.menu)
44 | #if os(macOS)
45 | .introspect(.picker(style: .menu), on: .macOS(.v11, .v12, .v13, .v14), customize: spy2)
46 | #endif
47 | }
48 | } extraAssertions: {
49 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
50 | XCTAssertEqual($0[safe: 0]?.numberOfItems, 1)
51 | XCTAssertEqual($0[safe: 1]?.numberOfItems, 2)
52 | XCTAssertEqual($0[safe: 2]?.numberOfItems, 3)
53 | #endif
54 | }
55 | }
56 | }
57 | #endif
58 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/PickerWithSegmentedStyleTests.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import SwiftUIIntrospect
3 | import XCTest
4 |
5 | @MainActor
6 | final class PickerWithSegmentedStyleTests: XCTestCase {
7 | #if canImport(UIKit)
8 | typealias PlatformPickerWithSegmentedStyle = UISegmentedControl
9 | #elseif canImport(AppKit)
10 | typealias PlatformPickerWithSegmentedStyle = NSSegmentedControl
11 | #endif
12 |
13 | func testPickerWithSegmentedStyle() {
14 | XCTAssertViewIntrospection(of: PlatformPickerWithSegmentedStyle.self) { spies in
15 | let spy0 = spies[0]
16 | let spy1 = spies[1]
17 | let spy2 = spies[2]
18 |
19 | VStack {
20 | Picker("Pick", selection: .constant("1")) {
21 | Text("1").tag("1")
22 | }
23 | .pickerStyle(.segmented)
24 | #if os(iOS) || os(tvOS) || os(visionOS)
25 | .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy0)
26 | #elseif os(macOS)
27 | .introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
28 | #endif
29 | .cornerRadius(8)
30 |
31 | Picker("Pick", selection: .constant("1")) {
32 | Text("1").tag("1")
33 | Text("2").tag("2")
34 | }
35 | .pickerStyle(.segmented)
36 | #if os(iOS) || os(tvOS) || os(visionOS)
37 | .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy1)
38 | #elseif os(macOS)
39 | .introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
40 | #endif
41 | .cornerRadius(8)
42 |
43 | Picker("Pick", selection: .constant("1")) {
44 | Text("1").tag("1")
45 | Text("2").tag("2")
46 | Text("3").tag("3")
47 | }
48 | .pickerStyle(.segmented)
49 | #if os(iOS) || os(tvOS) || os(visionOS)
50 | .introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy2)
51 | #elseif os(macOS)
52 | .introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
53 | #endif
54 | }
55 | } extraAssertions: {
56 | #if canImport(UIKit)
57 | XCTAssertEqual($0[safe: 0]?.numberOfSegments, 1)
58 | XCTAssertEqual($0[safe: 1]?.numberOfSegments, 2)
59 | XCTAssertEqual($0[safe: 2]?.numberOfSegments, 3)
60 | #elseif canImport(AppKit)
61 | XCTAssertEqual($0[safe: 0]?.segmentCount, 1)
62 | XCTAssertEqual($0[safe: 1]?.segmentCount, 2)
63 | XCTAssertEqual($0[safe: 2]?.segmentCount, 3)
64 | #endif
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/PickerWithWheelStyleTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(tvOS) && !os(macOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @MainActor
7 | final class PickerWithWheelStyleTests: XCTestCase {
8 | #if canImport(UIKit)
9 | typealias PlatformPickerWithWheelStyle = UIPickerView
10 | #endif
11 |
12 | func testPickerWithWheelStyle() {
13 | XCTAssertViewIntrospection(of: PlatformPickerWithWheelStyle.self) { spies in
14 | let spy0 = spies[0]
15 | let spy1 = spies[1]
16 | let spy2 = spies[2]
17 |
18 | VStack {
19 | Picker("Pick", selection: .constant("1")) {
20 | Text("1").tag("1")
21 | }
22 | .pickerStyle(.wheel)
23 | #if os(iOS) || os(visionOS)
24 | .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy0)
25 | #endif
26 | .cornerRadius(8)
27 |
28 | Picker("Pick", selection: .constant("1")) {
29 | Text("1").tag("1")
30 | Text("2").tag("2")
31 | }
32 | .pickerStyle(.wheel)
33 | #if os(iOS) || os(visionOS)
34 | .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy1)
35 | #endif
36 | .cornerRadius(8)
37 |
38 | Picker("Pick", selection: .constant("1")) {
39 | Text("1").tag("1")
40 | Text("2").tag("2")
41 | Text("3").tag("3")
42 | }
43 | .pickerStyle(.wheel)
44 | #if os(iOS) || os(visionOS)
45 | .introspect(.picker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy2)
46 | #endif
47 | }
48 | } extraAssertions: {
49 | #if canImport(UIKit)
50 | XCTAssertEqual($0[safe: 0]?.numberOfRows(inComponent: 0), 1)
51 | XCTAssertEqual($0[safe: 1]?.numberOfRows(inComponent: 0), 2)
52 | XCTAssertEqual($0[safe: 2]?.numberOfRows(inComponent: 0), 3)
53 | #endif
54 | }
55 | }
56 | }
57 | #endif
58 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/PopoverTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(tvOS) && !os(macOS) && !targetEnvironment(macCatalyst)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @MainActor
7 | final class PopoverTests: XCTestCase {
8 | func testPopover() throws {
9 | XCTAssertViewIntrospection(of: UIPopoverPresentationController.self) { spies in
10 | let spy0 = spies[0]
11 |
12 | Text("Root")
13 | .popover(isPresented: .constant(true)) {
14 | Text("Popover")
15 | .introspect(
16 | .popover,
17 | on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2),
18 | customize: spy0
19 | )
20 | }
21 | }
22 | }
23 | }
24 | #endif
25 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/ProgressViewWithCircularStyleTests.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import SwiftUIIntrospect
3 | import XCTest
4 |
5 | @MainActor
6 | final class ProgressViewWithCircularStyleTests: XCTestCase {
7 | #if canImport(UIKit)
8 | typealias PlatformProgressViewWithCircularStyle = UIActivityIndicatorView
9 | #elseif canImport(AppKit)
10 | typealias PlatformProgressViewWithCircularStyle = NSProgressIndicator
11 | #endif
12 |
13 | func testProgressViewWithCircularStyle() throws {
14 | guard #available(iOS 14, tvOS 14, macOS 11, *) else {
15 | throw XCTSkip()
16 | }
17 |
18 | XCTAssertViewIntrospection(of: PlatformProgressViewWithCircularStyle.self) { spies in
19 | let spy0 = spies[0]
20 | let spy1 = spies[1]
21 | let spy2 = spies[2]
22 |
23 | VStack {
24 | ProgressView(value: 0.25)
25 | .progressViewStyle(.circular)
26 | #if os(iOS) || os(tvOS) || os(visionOS)
27 | .introspect(.progressView(style: .circular), on: .iOS(.v14, .v15, .v16, .v17, .v18), .tvOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy0)
28 | #elseif os(macOS)
29 | .introspect(.progressView(style: .circular), on: .macOS(.v11, .v12, .v13, .v14), customize: spy0)
30 | #endif
31 |
32 | ProgressView(value: 0.5)
33 | .progressViewStyle(.circular)
34 | #if os(iOS) || os(tvOS) || os(visionOS)
35 | .introspect(.progressView(style: .circular), on: .iOS(.v14, .v15, .v16, .v17, .v18), .tvOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy1)
36 | #elseif os(macOS)
37 | .introspect(.progressView(style: .circular), on: .macOS(.v11, .v12, .v13, .v14), customize: spy1)
38 | #endif
39 |
40 | ProgressView(value: 0.75)
41 | .progressViewStyle(.circular)
42 | #if os(iOS) || os(tvOS) || os(visionOS)
43 | .introspect(.progressView(style: .circular), on: .iOS(.v14, .v15, .v16, .v17, .v18), .tvOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy2)
44 | #elseif os(macOS)
45 | .introspect(.progressView(style: .circular), on: .macOS(.v11, .v12, .v13, .v14), customize: spy2)
46 | #endif
47 | }
48 | } extraAssertions: {
49 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
50 | XCTAssertEqual($0[safe: 0]?.doubleValue, 0.25)
51 | XCTAssertEqual($0[safe: 1]?.doubleValue, 0.5)
52 | XCTAssertEqual($0[safe: 2]?.doubleValue, 0.75)
53 | #endif
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/ProgressViewWithLinearStyleTests.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import SwiftUIIntrospect
3 | import XCTest
4 |
5 | @MainActor
6 | final class ProgressViewWithLinearStyleTests: XCTestCase {
7 | #if canImport(UIKit)
8 | typealias PlatformProgressViewWithLinearStyle = UIProgressView
9 | #elseif canImport(AppKit)
10 | typealias PlatformProgressViewWithLinearStyle = NSProgressIndicator
11 | #endif
12 |
13 | func testProgressViewWithLinearStyle() throws {
14 | guard #available(iOS 14, tvOS 14, macOS 11, *) else {
15 | throw XCTSkip()
16 | }
17 |
18 | XCTAssertViewIntrospection(of: PlatformProgressViewWithLinearStyle.self) { spies in
19 | let spy0 = spies[0]
20 | let spy1 = spies[1]
21 | let spy2 = spies[2]
22 |
23 | VStack {
24 | ProgressView(value: 0.25)
25 | .progressViewStyle(.linear)
26 | #if os(iOS) || os(tvOS) || os(visionOS)
27 | .introspect(.progressView(style: .linear), on: .iOS(.v14, .v15, .v16, .v17, .v18), .tvOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy0)
28 | #elseif os(macOS)
29 | .introspect(.progressView(style: .linear), on: .macOS(.v11, .v12, .v13, .v14), customize: spy0)
30 | #endif
31 |
32 | ProgressView(value: 0.5)
33 | .progressViewStyle(.linear)
34 | #if os(iOS) || os(tvOS) || os(visionOS)
35 | .introspect(.progressView(style: .linear), on: .iOS(.v14, .v15, .v16, .v17, .v18), .tvOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy1)
36 | #elseif os(macOS)
37 | .introspect(.progressView(style: .linear), on: .macOS(.v11, .v12, .v13, .v14), customize: spy1)
38 | #endif
39 |
40 | ProgressView(value: 0.75)
41 | .progressViewStyle(.linear)
42 | #if os(iOS) || os(tvOS) || os(visionOS)
43 | .introspect(.progressView(style: .linear), on: .iOS(.v14, .v15, .v16, .v17, .v18), .tvOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy2)
44 | #elseif os(macOS)
45 | .introspect(.progressView(style: .linear), on: .macOS(.v11, .v12, .v13, .v14), customize: spy2)
46 | #endif
47 | }
48 | } extraAssertions: {
49 | #if canImport(UIKit)
50 | XCTAssertEqual($0[safe: 0]?.progress, 0.25)
51 | XCTAssertEqual($0[safe: 1]?.progress, 0.5)
52 | XCTAssertEqual($0[safe: 2]?.progress, 0.75)
53 | #elseif canImport(AppKit) && !targetEnvironment(macCatalyst)
54 | XCTAssertEqual($0[safe: 0]?.doubleValue, 0.25)
55 | XCTAssertEqual($0[safe: 1]?.doubleValue, 0.5)
56 | XCTAssertEqual($0[safe: 2]?.doubleValue, 0.75)
57 | #endif
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/SheetTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(macOS) && !targetEnvironment(macCatalyst)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @MainActor
7 | final class SheetTests: XCTestCase {
8 | #if os(iOS)
9 | func testSheet() throws {
10 | XCTAssertViewIntrospection(of: UIPresentationController.self) { spies in
11 | let spy0 = spies[0]
12 |
13 | Text("Root")
14 | .sheet(isPresented: .constant(true)) {
15 | Text("Sheet")
16 | .introspect(
17 | .sheet,
18 | on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18),
19 | customize: spy0
20 | )
21 | }
22 | }
23 | }
24 |
25 | func testSheetAsSheetPresentationController() throws {
26 | guard #available(iOS 15, tvOS 15, *) else {
27 | throw XCTSkip()
28 | }
29 |
30 | XCTAssertViewIntrospection(of: UISheetPresentationController.self) { spies in
31 | let spy0 = spies[0]
32 |
33 | Text("Root")
34 | .sheet(isPresented: .constant(true)) {
35 | Text("Sheet")
36 | .introspect(
37 | .sheet,
38 | on: .iOS(.v15, .v16, .v17, .v18),
39 | customize: spy0
40 | )
41 | }
42 | }
43 | }
44 | #elseif os(tvOS)
45 | func testSheet() throws {
46 | XCTAssertViewIntrospection(of: UIPresentationController.self) { spies in
47 | let spy0 = spies[0]
48 |
49 | Text("Root")
50 | .sheet(isPresented: .constant(true)) {
51 | Text("Content")
52 | .introspect(
53 | .sheet,
54 | on: .tvOS(.v13, .v14, .v15, .v16, .v17, .v18),
55 | customize: spy0
56 | )
57 | }
58 | }
59 | }
60 | #elseif os(visionOS)
61 | func testSheet() throws {
62 | XCTAssertViewIntrospection(of: UIPresentationController.self) { spies in
63 | let spy0 = spies[0]
64 |
65 | Text("Root")
66 | .sheet(isPresented: .constant(true)) {
67 | Text("Sheet")
68 | .introspect(
69 | .sheet,
70 | on: .visionOS(.v1, .v2),
71 | customize: spy0
72 | )
73 | }
74 | }
75 | }
76 | #endif
77 | }
78 | #endif
79 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/SliderTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(tvOS) && !os(visionOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @MainActor
7 | final class SliderTests: XCTestCase {
8 | #if canImport(UIKit)
9 | typealias PlatformSlider = UISlider
10 | #elseif canImport(AppKit)
11 | typealias PlatformSlider = NSSlider
12 | #endif
13 |
14 | func testSlider() {
15 | XCTAssertViewIntrospection(of: PlatformSlider.self) { spies in
16 | let spy0 = spies[0]
17 | let spy1 = spies[1]
18 | let spy2 = spies[2]
19 |
20 | VStack {
21 | Slider(value: .constant(0.2), in: 0...1)
22 | #if os(iOS)
23 | .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), customize: spy0)
24 | #elseif os(macOS)
25 | .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
26 | #endif
27 | .cornerRadius(8)
28 |
29 | Slider(value: .constant(0.5), in: 0...1)
30 | #if os(iOS)
31 | .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), customize: spy1)
32 | #elseif os(macOS)
33 | .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
34 | #endif
35 | .cornerRadius(8)
36 |
37 | Slider(value: .constant(0.8), in: 0...1)
38 | #if os(iOS)
39 | .introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), customize: spy2)
40 | #elseif os(macOS)
41 | .introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
42 | #endif
43 | }
44 | } extraAssertions: {
45 | #if canImport(UIKit)
46 | XCTAssertEqual($0[safe: 0]?.value, 0.2)
47 | XCTAssertEqual($0[safe: 1]?.value, 0.5)
48 | XCTAssertEqual($0[safe: 2]?.value, 0.8)
49 | #elseif canImport(AppKit)
50 | XCTAssertEqual($0[safe: 0]?.floatValue, 0.2)
51 | XCTAssertEqual($0[safe: 1]?.floatValue, 0.5)
52 | XCTAssertEqual($0[safe: 2]?.floatValue, 0.8)
53 | #endif
54 | }
55 | }
56 | }
57 | #endif
58 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/StepperTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(tvOS) && !os(visionOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @MainActor
7 | final class StepperTests: XCTestCase {
8 | #if canImport(UIKit)
9 | typealias PlatformStepper = UIStepper
10 | #elseif canImport(AppKit)
11 | typealias PlatformStepper = NSStepper
12 | #endif
13 |
14 | func testStepper() {
15 | XCTAssertViewIntrospection(of: PlatformStepper.self) { spies in
16 | let spy0 = spies[0]
17 | let spy1 = spies[1]
18 | let spy2 = spies[2]
19 |
20 | VStack {
21 | Stepper("", value: .constant(0), in: 0...10)
22 | #if os(iOS)
23 | .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), customize: spy0)
24 | #elseif os(macOS)
25 | .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
26 | #endif
27 | .cornerRadius(8)
28 |
29 | Stepper("", value: .constant(0), in: 0...10)
30 | #if os(iOS)
31 | .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), customize: spy1)
32 | #elseif os(macOS)
33 | .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
34 | #endif
35 | .cornerRadius(8)
36 |
37 | Stepper("", value: .constant(0), in: 0...10)
38 | #if os(iOS)
39 | .introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), customize: spy2)
40 | #elseif os(macOS)
41 | .introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
42 | #endif
43 | }
44 | } extraAssertions: {
45 | XCTAssert(Set($0.map(ObjectIdentifier.init)).count == 3)
46 | }
47 | }
48 | }
49 | #endif
50 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/TabViewTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(visionOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @MainActor
7 | final class TabViewTests: XCTestCase {
8 | #if canImport(UIKit)
9 | typealias PlatformTabView = UITabBarController
10 | #elseif canImport(AppKit)
11 | typealias PlatformTabView = NSTabView
12 | #endif
13 |
14 | func testTabView() {
15 | XCTAssertViewIntrospection(of: PlatformTabView.self) { spies in
16 | let spy = spies[0]
17 |
18 | TabView {
19 | ZStack {
20 | Color.red
21 | Text("Something")
22 | }
23 | }
24 | #if os(iOS) || os(tvOS)
25 | .introspect(.tabView, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), customize: spy)
26 | #elseif os(macOS)
27 | .introspect(.tabView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy)
28 | #endif
29 | }
30 | }
31 |
32 | func testTabViewAsAncestor() {
33 | XCTAssertViewIntrospection(of: PlatformTabView.self) { spies in
34 | let spy = spies[0]
35 |
36 | TabView {
37 | ZStack {
38 | Color.red
39 | Text("Something")
40 | #if os(iOS) || os(tvOS)
41 | .introspect(.tabView, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), scope: .ancestor, customize: spy)
42 | #elseif os(macOS)
43 | .introspect(.tabView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor, customize: spy)
44 | #endif
45 | }
46 | }
47 | }
48 | }
49 | }
50 | #endif
51 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/TabViewWithPageStyleTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(macOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @available(iOS 14, tvOS 14, *)
7 | @MainActor
8 | final class TabViewWithPageStyleTests: XCTestCase {
9 | #if canImport(UIKit)
10 | typealias PlatformTabViewWithPageStyle = UICollectionView
11 | #endif
12 |
13 | func testTabViewWithPageStyle() throws {
14 | guard #available(iOS 14, tvOS 14, *) else {
15 | throw XCTSkip()
16 | }
17 |
18 | XCTAssertViewIntrospection(of: PlatformTabViewWithPageStyle.self) { spies in
19 | let spy = spies[0]
20 |
21 | TabView {
22 | Text("Page 1").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red)
23 | Text("Page 2").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.blue)
24 | }
25 | .tabViewStyle(.page)
26 | #if os(iOS) || os(tvOS) || os(visionOS)
27 | .introspect(.tabView(style: .page), on: .iOS(.v14, .v15, .v16, .v17, .v18), .tvOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy)
28 | #endif
29 | }
30 | }
31 |
32 | func testTabViewWithPageStyleAsAncestor() throws {
33 | guard #available(iOS 14, tvOS 14, *) else {
34 | throw XCTSkip()
35 | }
36 |
37 | XCTAssertViewIntrospection(of: PlatformTabViewWithPageStyle.self) { spies in
38 | let spy = spies[0]
39 |
40 | TabView {
41 | Text("Page 1").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red)
42 | #if os(iOS) || os(tvOS) || os(visionOS)
43 | .introspect(.tabView(style: .page), on: .iOS(.v14, .v15, .v16, .v17, .v18), .tvOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), scope: .ancestor, customize: spy)
44 | #endif
45 | Text("Page 2").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.blue)
46 | }
47 | .tabViewStyle(.page)
48 | }
49 | }
50 | }
51 | #endif
52 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/TextEditorTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(tvOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @available(iOS 14, macOS 11, *)
7 | @MainActor
8 | final class TextEditorTests: XCTestCase {
9 | #if canImport(UIKit)
10 | typealias PlatformTextEditor = UITextView
11 | #elseif canImport(AppKit)
12 | typealias PlatformTextEditor = NSTextView
13 | #endif
14 |
15 | func testTextEditor() throws {
16 | guard #available(iOS 14, macOS 11, *) else {
17 | throw XCTSkip()
18 | }
19 |
20 | XCTAssertViewIntrospection(of: PlatformTextEditor.self) { spies in
21 | let spy0 = spies[0]
22 | let spy1 = spies[1]
23 | let spy2 = spies[2]
24 |
25 | VStack {
26 | TextEditor(text: .constant("Text Field 0"))
27 | #if os(iOS) || os(visionOS)
28 | .introspect(.textEditor, on: .iOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy0)
29 | #elseif os(macOS)
30 | .introspect(.textEditor, on: .macOS(.v11, .v12, .v13, .v14), customize: spy0)
31 | #endif
32 | .cornerRadius(8)
33 |
34 | TextEditor(text: .constant("Text Field 1"))
35 | #if os(iOS) || os(visionOS)
36 | .introspect(.textEditor, on: .iOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy1)
37 | #elseif os(macOS)
38 | .introspect(.textEditor, on: .macOS(.v11, .v12, .v13, .v14), customize: spy1)
39 | #endif
40 | .cornerRadius(8)
41 |
42 | TextEditor(text: .constant("Text Field 2"))
43 | #if os(iOS) || os(visionOS)
44 | .introspect(.textEditor, on: .iOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy2)
45 | #elseif os(macOS)
46 | .introspect(.textEditor, on: .macOS(.v11, .v12, .v13, .v14), customize: spy2)
47 | #endif
48 | }
49 | } extraAssertions: {
50 | #if canImport(UIKit)
51 | XCTAssertEqual($0[safe: 0]?.text, "Text Field 0")
52 | XCTAssertEqual($0[safe: 1]?.text, "Text Field 1")
53 | XCTAssertEqual($0[safe: 2]?.text, "Text Field 2")
54 | #elseif canImport(AppKit)
55 | XCTAssertEqual($0[safe: 0]?.string, "Text Field 0")
56 | XCTAssertEqual($0[safe: 1]?.string, "Text Field 1")
57 | XCTAssertEqual($0[safe: 2]?.string, "Text Field 2")
58 | #endif
59 | }
60 | }
61 | }
62 | #endif
63 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/TextFieldWithVerticalAxisTests.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import SwiftUIIntrospect
3 | import XCTest
4 |
5 | @available(iOS 16, tvOS 16, macOS 13, *)
6 | @MainActor
7 | final class TextFieldWithVerticalAxisTests: XCTestCase {
8 | #if canImport(UIKit) && (os(iOS) || os(visionOS))
9 | typealias PlatformTextField = UITextView
10 | #elseif canImport(UIKit) && os(tvOS)
11 | typealias PlatformTextField = UITextField
12 | #elseif canImport(AppKit)
13 | typealias PlatformTextField = NSTextField
14 | #endif
15 |
16 | func testTextFieldWithVerticalAxis() throws {
17 | guard #available(iOS 16, tvOS 16, macOS 13, *) else {
18 | throw XCTSkip()
19 | }
20 |
21 | XCTAssertViewIntrospection(of: PlatformTextField.self) { spies in
22 | let spy0 = spies[0]
23 | let spy1 = spies[1]
24 | let spy2 = spies[2]
25 |
26 | VStack {
27 | TextField("", text: .constant("Text Field 1"), axis: .vertical)
28 | #if os(iOS) || os(visionOS)
29 | .introspect(.textField(axis: .vertical), on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy0)
30 | #elseif os(tvOS)
31 | .introspect(.textField(axis: .vertical), on: .tvOS(.v16, .v17, .v18), customize: spy0)
32 | #elseif os(macOS)
33 | .introspect(.textField(axis: .vertical), on: .macOS(.v13, .v14), customize: spy0)
34 | #endif
35 | .cornerRadius(8)
36 |
37 | TextField("", text: .constant("Text Field 2"), axis: .vertical)
38 | #if os(iOS) || os(visionOS)
39 | .introspect(.textField(axis: .vertical), on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy1)
40 | #elseif os(tvOS)
41 | .introspect(.textField(axis: .vertical), on: .tvOS(.v16, .v17, .v18), customize: spy1)
42 | #elseif os(macOS)
43 | .introspect(.textField(axis: .vertical), on: .macOS(.v13, .v14), customize: spy1)
44 | #endif
45 | .cornerRadius(8)
46 |
47 | TextField("", text: .constant("Text Field 3"), axis: .vertical)
48 | #if os(iOS) || os(visionOS)
49 | .introspect(.textField(axis: .vertical), on: .iOS(.v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy2)
50 | #elseif os(tvOS)
51 | .introspect(.textField(axis: .vertical), on: .tvOS(.v16, .v17, .v18), customize: spy2)
52 | #elseif os(macOS)
53 | .introspect(.textField(axis: .vertical), on: .macOS(.v13, .v14), customize: spy2)
54 | #endif
55 | }
56 | } extraAssertions: {
57 | #if canImport(UIKit)
58 | XCTAssertEqual($0[safe: 0]?.text, "Text Field 1")
59 | XCTAssertEqual($0[safe: 1]?.text, "Text Field 2")
60 | XCTAssertEqual($0[safe: 2]?.text, "Text Field 3")
61 | #elseif canImport(AppKit)
62 | XCTAssertEqual($0[safe: 0]?.stringValue, "Text Field 1")
63 | XCTAssertEqual($0[safe: 1]?.stringValue, "Text Field 2")
64 | XCTAssertEqual($0[safe: 2]?.stringValue, "Text Field 3")
65 | #endif
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/ToggleTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(tvOS) && !os(visionOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @MainActor
7 | final class ToggleTests: XCTestCase {
8 | #if canImport(UIKit)
9 | typealias PlatformToggle = UISwitch
10 | #elseif canImport(AppKit)
11 | typealias PlatformToggle = NSButton
12 | #endif
13 |
14 | func testToggle() {
15 | XCTAssertViewIntrospection(of: PlatformToggle.self) { spies in
16 | let spy0 = spies[0]
17 | let spy1 = spies[1]
18 | let spy2 = spies[2]
19 |
20 | VStack {
21 | Toggle("", isOn: .constant(true))
22 | #if os(iOS)
23 | .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), customize: spy0)
24 | #elseif os(macOS)
25 | .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
26 | #endif
27 |
28 | Toggle("", isOn: .constant(false))
29 | #if os(iOS)
30 | .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), customize: spy1)
31 | #elseif os(macOS)
32 | .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
33 | #endif
34 |
35 | Toggle("", isOn: .constant(true))
36 | #if os(iOS)
37 | .introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), customize: spy2)
38 | #elseif os(macOS)
39 | .introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
40 | #endif
41 | }
42 | } extraAssertions: {
43 | #if canImport(UIKit)
44 | XCTAssertEqual($0[safe: 0]?.isOn, true)
45 | XCTAssertEqual($0[safe: 1]?.isOn, false)
46 | XCTAssertEqual($0[safe: 2]?.isOn, true)
47 | #elseif canImport(AppKit)
48 | XCTAssertEqual($0[safe: 0]?.state, .on)
49 | XCTAssertEqual($0[safe: 1]?.state, .off)
50 | XCTAssertEqual($0[safe: 2]?.state, .on)
51 | #endif
52 | }
53 | }
54 | }
55 | #endif
56 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/ToggleWithButtonStyleTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(iOS) && !os(tvOS) && !os(visionOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @available(macOS 12, *)
7 | @MainActor
8 | final class ToggleWithButtonStyleTests: XCTestCase {
9 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
10 | typealias PlatformToggleWithButtonStyle = NSButton
11 | #endif
12 |
13 | func testToggleWithButtonStyle() throws {
14 | guard #available(macOS 12, *) else {
15 | throw XCTSkip()
16 | }
17 |
18 | XCTAssertViewIntrospection(of: PlatformToggleWithButtonStyle.self) { spies in
19 | let spy0 = spies[0]
20 | let spy1 = spies[1]
21 | let spy2 = spies[2]
22 |
23 | VStack {
24 | Toggle("", isOn: .constant(true))
25 | .toggleStyle(.button)
26 | #if os(macOS)
27 | .introspect(.toggle(style: .button), on: .macOS(.v12, .v13, .v14), customize: spy0)
28 | #endif
29 |
30 | Toggle("", isOn: .constant(false))
31 | .toggleStyle(.button)
32 | #if os(macOS)
33 | .introspect(.toggle(style: .button), on: .macOS(.v12, .v13, .v14), customize: spy1)
34 | #endif
35 |
36 | Toggle("", isOn: .constant(true))
37 | .toggleStyle(.button)
38 | #if os(macOS)
39 | .introspect(.toggle(style: .button), on: .macOS(.v12, .v13, .v14), customize: spy2)
40 | #endif
41 | }
42 | } extraAssertions: {
43 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
44 | XCTAssertEqual($0[safe: 0]?.state, .on)
45 | XCTAssertEqual($0[safe: 1]?.state, .off)
46 | XCTAssertEqual($0[safe: 2]?.state, .on)
47 | #endif
48 | }
49 | }
50 | }
51 | #endif
52 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/ToggleWithCheckboxStyleTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(iOS) && !os(tvOS) && !os(visionOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @MainActor
7 | final class ToggleWithCheckboxStyleTests: XCTestCase {
8 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
9 | typealias PlatformToggleWithCheckboxStyle = NSButton
10 | #endif
11 |
12 | func testToggleWithCheckboxStyle() throws {
13 | XCTAssertViewIntrospection(of: PlatformToggleWithCheckboxStyle.self) { spies in
14 | let spy0 = spies[0]
15 | let spy1 = spies[1]
16 | let spy2 = spies[2]
17 |
18 | VStack {
19 | Toggle("", isOn: .constant(true))
20 | .toggleStyle(.checkbox)
21 | #if os(macOS)
22 | .introspect(.toggle(style: .checkbox), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
23 | #endif
24 |
25 | Toggle("", isOn: .constant(false))
26 | .toggleStyle(.checkbox)
27 | #if os(macOS)
28 | .introspect(.toggle(style: .checkbox), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
29 | #endif
30 |
31 | Toggle("", isOn: .constant(true))
32 | .toggleStyle(.checkbox)
33 | #if os(macOS)
34 | .introspect(.toggle(style: .checkbox), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
35 | #endif
36 | }
37 | } extraAssertions: {
38 | #if canImport(AppKit) && !targetEnvironment(macCatalyst)
39 | XCTAssertEqual($0[safe: 0]?.state, .on)
40 | XCTAssertEqual($0[safe: 1]?.state, .off)
41 | XCTAssertEqual($0[safe: 2]?.state, .on)
42 | #endif
43 | }
44 | }
45 | }
46 | #endif
47 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/ToggleWithSwitchStyleTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(tvOS) && !os(visionOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @MainActor
7 | final class ToggleWithSwitchStyleTests: XCTestCase {
8 | #if canImport(UIKit)
9 | typealias PlatformToggleWithSwitchStyle = UISwitch
10 | #elseif canImport(AppKit)
11 | typealias PlatformToggleWithSwitchStyle = NSSwitch
12 | #endif
13 |
14 | func testToggleWithSwitchStyle() {
15 | XCTAssertViewIntrospection(of: PlatformToggleWithSwitchStyle.self) { spies in
16 | let spy0 = spies[0]
17 | let spy1 = spies[1]
18 | let spy2 = spies[2]
19 |
20 | VStack {
21 | Toggle("", isOn: .constant(true))
22 | .toggleStyle(.switch)
23 | #if os(iOS)
24 | .introspect(.toggle(style: .switch), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), customize: spy0)
25 | #elseif os(macOS)
26 | .introspect(.toggle(style: .switch), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
27 | #endif
28 |
29 | Toggle("", isOn: .constant(false))
30 | .toggleStyle(.switch)
31 | #if os(iOS)
32 | .introspect(.toggle(style: .switch), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), customize: spy1)
33 | #elseif os(macOS)
34 | .introspect(.toggle(style: .switch), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
35 | #endif
36 |
37 | Toggle("", isOn: .constant(true))
38 | .toggleStyle(.switch)
39 | #if os(iOS)
40 | .introspect(.toggle(style: .switch), on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), customize: spy2)
41 | #elseif os(macOS)
42 | .introspect(.toggle(style: .switch), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
43 | #endif
44 | }
45 | } extraAssertions: {
46 | #if canImport(UIKit)
47 | XCTAssertEqual($0[safe: 0]?.isOn, true)
48 | XCTAssertEqual($0[safe: 1]?.isOn, false)
49 | XCTAssertEqual($0[safe: 2]?.isOn, true)
50 | #elseif canImport(AppKit)
51 | XCTAssertEqual($0[safe: 0]?.state, .on)
52 | XCTAssertEqual($0[safe: 1]?.state, .off)
53 | XCTAssertEqual($0[safe: 2]?.state, .on)
54 | #endif
55 | }
56 | }
57 | }
58 | #endif
59 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/VideoPlayerTests.swift:
--------------------------------------------------------------------------------
1 | #if canImport(AVKit)
2 | import AVKit
3 | import SwiftUI
4 | import SwiftUIIntrospect
5 | import XCTest
6 |
7 | @available(iOS 14, tvOS 14, macOS 11, *)
8 | @MainActor
9 | final class VideoPlayerTests: XCTestCase {
10 | #if canImport(UIKit)
11 | typealias PlatformVideoPlayer = AVPlayerViewController
12 | #elseif canImport(AppKit)
13 | typealias PlatformVideoPlayer = AVPlayerView
14 | #endif
15 |
16 | func testVideoPlayer() throws {
17 | guard #available(iOS 14, tvOS 14, macOS 11, *) else {
18 | throw XCTSkip()
19 | }
20 |
21 | let videoURL0 = URL(string: "https://bit.ly/swswift#1")!
22 | let videoURL1 = URL(string: "https://bit.ly/swswift#2")!
23 | let videoURL2 = URL(string: "https://bit.ly/swswift#3")!
24 |
25 | XCTAssertViewIntrospection(of: PlatformVideoPlayer.self) { spies in
26 | let spy0 = spies[0]
27 | let spy1 = spies[1]
28 | let spy2 = spies[2]
29 |
30 | VStack {
31 | VideoPlayer(player: AVPlayer(url: videoURL0))
32 | #if os(iOS) || os(tvOS) || os(visionOS)
33 | .introspect(.videoPlayer, on: .iOS(.v14, .v15, .v16, .v17, .v18), .tvOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy0)
34 | #elseif os(macOS)
35 | .introspect(.videoPlayer, on: .macOS(.v11, .v12, .v13, .v14), customize: spy0)
36 | #endif
37 |
38 | VideoPlayer(player: AVPlayer(url: videoURL1))
39 | #if os(iOS) || os(tvOS) || os(visionOS)
40 | .introspect(.videoPlayer, on: .iOS(.v14, .v15, .v16, .v17, .v18), .tvOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy1)
41 | #elseif os(macOS)
42 | .introspect(.videoPlayer, on: .macOS(.v11, .v12, .v13, .v14), customize: spy1)
43 | #endif
44 |
45 | VideoPlayer(player: AVPlayer(url: videoURL2))
46 | #if os(iOS) || os(tvOS) || os(visionOS)
47 | .introspect(.videoPlayer, on: .iOS(.v14, .v15, .v16, .v17, .v18), .tvOS(.v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy2)
48 | #elseif os(macOS)
49 | .introspect(.videoPlayer, on: .macOS(.v11, .v12, .v13, .v14), customize: spy2)
50 | #endif
51 | }
52 | } extraAssertions: {
53 | XCTAssertEqual(($0[safe: 0]?.player?.currentItem?.asset as? AVURLAsset)?.url, videoURL0)
54 | XCTAssertEqual(($0[safe: 1]?.player?.currentItem?.asset as? AVURLAsset)?.url, videoURL1)
55 | XCTAssertEqual(($0[safe: 2]?.player?.currentItem?.asset as? AVURLAsset)?.url, videoURL2)
56 | }
57 | }
58 | }
59 | #endif
60 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/ViewControllerTests.swift:
--------------------------------------------------------------------------------
1 | #if !os(macOS)
2 | import SwiftUI
3 | import SwiftUIIntrospect
4 | import XCTest
5 |
6 | @MainActor
7 | final class ViewControllerTests: XCTestCase {
8 | func testViewController() {
9 | XCTAssertViewIntrospection(of: PlatformViewController.self) { spies in
10 | let spy0 = spies[0]
11 | let spy1 = spies[1]
12 | let spy2 = spies[2]
13 |
14 | TabView {
15 | NavigationView {
16 | Text("Root").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red)
17 | .introspect(
18 | .viewController,
19 | on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2),
20 | customize: spy2
21 | )
22 | }
23 | .navigationViewStyle(.stack)
24 | .tabItem {
25 | Image(systemName: "1.circle")
26 | Text("Tab 1")
27 | }
28 | .introspect(
29 | .viewController,
30 | on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2),
31 | customize: spy1
32 | )
33 | }
34 | .introspect(
35 | .viewController,
36 | on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2),
37 | customize: spy0
38 | )
39 | } extraAssertions: {
40 | #if !os(visionOS)
41 | XCTAssert($0[safe: 0] is UITabBarController)
42 | #endif
43 | XCTAssert($0[safe: 1] is UINavigationController)
44 | XCTAssert(String(describing: $0[safe: 2]).contains("UIHostingController"))
45 | XCTAssert($0[safe: 1] === $0[safe: 2]?.parent)
46 | }
47 | }
48 | }
49 | #endif
50 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/ViewTests.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import SwiftUIIntrospect
3 | import XCTest
4 |
5 | @MainActor
6 | final class ViewTests: XCTestCase {
7 | func testView() {
8 | XCTAssertViewIntrospection(of: PlatformView.self) { spies in
9 | let spy0 = spies[0]
10 | let spy1 = spies[1]
11 | let spy2 = spies[2]
12 |
13 | VStack(spacing: 10) {
14 | Image(systemName: "scribble").resizable().frame(height: 30)
15 | #if os(iOS) || os(tvOS) || os(visionOS)
16 | .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy0)
17 | #elseif os(macOS)
18 | .introspect(.view, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
19 | #endif
20 |
21 | Text("Text").frame(height: 40)
22 | #if os(iOS) || os(tvOS) || os(visionOS)
23 | .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy1)
24 | #elseif os(macOS)
25 | .introspect(.view, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
26 | #endif
27 | }
28 | .padding(10)
29 | #if os(iOS) || os(tvOS) || os(visionOS)
30 | .introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy2)
31 | #elseif os(macOS)
32 | .introspect(.view, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
33 | #endif
34 | } extraAssertions: {
35 | XCTAssertEqual($0[safe: 0]?.frame.height, 30)
36 | XCTAssertEqual($0[safe: 1]?.frame.height, 40)
37 | XCTAssertEqual($0[safe: 2]?.frame.height, 100)
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Tests/Tests/ViewTypes/WindowTests.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import SwiftUIIntrospect
3 | import XCTest
4 |
5 | @MainActor
6 | final class WindowTests: XCTestCase {
7 | #if canImport(UIKit)
8 | typealias PlatformWindow = UIWindow
9 | #elseif canImport(AppKit)
10 | typealias PlatformWindow = NSWindow
11 | #endif
12 |
13 | func testWindow() {
14 | XCTAssertViewIntrospection(of: PlatformWindow.self) { spies in
15 | let spy0 = spies[0]
16 | let spy1 = spies[1]
17 | let spy2 = spies[2]
18 |
19 | VStack {
20 | Image(systemName: "scribble")
21 | #if os(iOS) || os(tvOS) || os(visionOS)
22 | .introspect(.window, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy0)
23 | #elseif os(macOS)
24 | .introspect(.window, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
25 | #endif
26 |
27 | Text("Text")
28 | #if os(iOS) || os(tvOS) || os(visionOS)
29 | .introspect(.window, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy1)
30 | #elseif os(macOS)
31 | .introspect(.window, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
32 | #endif
33 | }
34 | #if os(iOS) || os(tvOS) || os(visionOS)
35 | .introspect(.window, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18), .tvOS(.v13, .v14, .v15, .v16, .v17, .v18), .visionOS(.v1, .v2), customize: spy2)
36 | #elseif os(macOS)
37 | .introspect(.window, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
38 | #endif
39 | } extraAssertions: {
40 | XCTAssertIdentical($0[safe: 0], $0[safe: 1])
41 | XCTAssertIdentical($0[safe: 0], $0[safe: 2])
42 | XCTAssertIdentical($0[safe: 1], $0[safe: 2])
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Tests/Tests/WeakTests.swift:
--------------------------------------------------------------------------------
1 | @_spi(Advanced) import SwiftUIIntrospect
2 | import XCTest
3 |
4 | final class WeakTests: XCTestCase {
5 | final class Foo {}
6 |
7 | var strongFoo: Foo? = Foo()
8 |
9 | func testInit_nil() {
10 | @Weak var weakFoo: Foo?
11 | XCTAssertNil(weakFoo)
12 | }
13 |
14 | func testInit_nonNil() {
15 | @Weak var weakFoo: Foo? = strongFoo
16 | XCTAssertIdentical(weakFoo, strongFoo)
17 | }
18 |
19 | func testAssignment_nilToNil() {
20 | @Weak var weakFoo: Foo?
21 | weakFoo = nil
22 | XCTAssertNil(weakFoo)
23 | }
24 |
25 | func testAssignment_nilToNonNil() {
26 | @Weak var weakFoo: Foo?
27 | let otherFoo = Foo()
28 | weakFoo = otherFoo
29 | XCTAssertIdentical(weakFoo, otherFoo)
30 | }
31 |
32 | func testAssignment_nonNilToNil() {
33 | @Weak var weakFoo: Foo? = strongFoo
34 | weakFoo = nil
35 | XCTAssertNil(weakFoo)
36 | }
37 |
38 | func testAssignment_nonNilToNonNil() {
39 | @Weak var weakFoo: Foo? = strongFoo
40 | let otherFoo = Foo()
41 | weakFoo = otherFoo
42 | XCTAssertIdentical(weakFoo, otherFoo)
43 | }
44 |
45 | func testIndirectAssignment_nonNilToNil() {
46 | @Weak var weakFoo: Foo? = strongFoo
47 | strongFoo = nil
48 | XCTAssertNil(weakFoo)
49 | }
50 |
51 | func testIndirectAssignment_nonNilToNonNil() {
52 | @Weak var weakFoo: Foo? = strongFoo
53 | strongFoo = Foo()
54 | XCTAssertNil(weakFoo)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Tests/TestsHostApp/TestsHostApp.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | @main
4 | struct App: SwiftUI.App {
5 | var body: some Scene {
6 | WindowGroup {
7 | EmptyView()
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Tests/UITests/StatusBarStyleUITests.swift:
--------------------------------------------------------------------------------
1 | import SnapshotTesting
2 | import XCTest
3 |
4 | final class StatusBarStyleUITests: UITestCase {
5 | override var testCase: TestCase {
6 | .statusBarStyle
7 | }
8 |
9 | func test() throws {
10 | guard #unavailable(iOS 17) else {
11 | throw XCTSkip("SimulatorStatusMagic stopped working in iOS 17, so we can no longer consistently compare status bar screenshots")
12 | }
13 |
14 | app.buttons["Navigate To Detail"].tap()
15 | app.buttons["Navigate To Detail"].tap()
16 | app.buttons["Navigate Back"].tap()
17 |
18 | let iOSDevice = UIDevice.current.userInterfaceIdiom == .pad ? "ipad" : "iphone"
19 | let iOSVersion = ProcessInfo().operatingSystemVersion
20 | func screenshotName(_ number: Int) -> String {
21 | "\(iOSDevice)-ios-\(iOSVersion.majorVersion)-screenshot-\(number)"
22 | }
23 |
24 | assertSnapshot(
25 | matching: app.windows.firstMatch.screenshot().image,
26 | as: .image(perceptualPrecision: 0.95),
27 | named: screenshotName(1)
28 | )
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Tests/UITests/UITestCase.swift:
--------------------------------------------------------------------------------
1 | import SimulatorStatusMagic
2 | import XCTest
3 |
4 | class UITestCase: XCTestCase {
5 | var testCase: TestCase {
6 | preconditionFailure("Please override this property")
7 | }
8 |
9 | let app = XCUIApplication()
10 |
11 | override func invokeTest() {
12 | SDStatusBarManager.sharedInstance().enableOverrides()
13 |
14 | continueAfterFailure = false
15 |
16 | app.launchEnvironment["testCase"] = testCase.rawValue
17 | app.launch()
18 |
19 | super.invokeTest()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Tests/UITests/UITests.xctestplan:
--------------------------------------------------------------------------------
1 | {
2 | "configurations" : [
3 | {
4 | "id" : "DD0EEECD-4762-4A68-91A8-F7B5A2209B45",
5 | "name" : "Test Scheme Action",
6 | "options" : {
7 |
8 | }
9 | }
10 | ],
11 | "defaultOptions" : {
12 |
13 | },
14 | "testTargets" : [
15 | {
16 | "target" : {
17 | "containerPath" : "container:Tests.xcodeproj",
18 | "identifier" : "D58D832A2A66BDD500A203BE",
19 | "name" : "UITests"
20 | }
21 | }
22 | ],
23 | "version" : 1
24 | }
25 |
--------------------------------------------------------------------------------
/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.ipad-ios-13-screenshot-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siteline/swiftui-introspect/807f73ce09a9b9723f12385e592b4e0aaebd3336/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.ipad-ios-13-screenshot-1.png
--------------------------------------------------------------------------------
/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.ipad-ios-14-screenshot-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siteline/swiftui-introspect/807f73ce09a9b9723f12385e592b4e0aaebd3336/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.ipad-ios-14-screenshot-1.png
--------------------------------------------------------------------------------
/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.ipad-ios-15-screenshot-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siteline/swiftui-introspect/807f73ce09a9b9723f12385e592b4e0aaebd3336/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.ipad-ios-15-screenshot-1.png
--------------------------------------------------------------------------------
/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.ipad-ios-16-screenshot-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siteline/swiftui-introspect/807f73ce09a9b9723f12385e592b4e0aaebd3336/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.ipad-ios-16-screenshot-1.png
--------------------------------------------------------------------------------
/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.iphone-ios-13-screenshot-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siteline/swiftui-introspect/807f73ce09a9b9723f12385e592b4e0aaebd3336/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.iphone-ios-13-screenshot-1.png
--------------------------------------------------------------------------------
/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.iphone-ios-14-screenshot-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siteline/swiftui-introspect/807f73ce09a9b9723f12385e592b4e0aaebd3336/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.iphone-ios-14-screenshot-1.png
--------------------------------------------------------------------------------
/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.iphone-ios-15-screenshot-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siteline/swiftui-introspect/807f73ce09a9b9723f12385e592b4e0aaebd3336/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.iphone-ios-15-screenshot-1.png
--------------------------------------------------------------------------------
/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.iphone-ios-16-screenshot-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siteline/swiftui-introspect/807f73ce09a9b9723f12385e592b4e0aaebd3336/Tests/UITests/__Snapshots__/StatusBarStyleUITests/test.iphone-ios-16-screenshot-1.png
--------------------------------------------------------------------------------
/Tests/UITestsHostApp/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Tests/UITestsHostApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Tests/UITestsHostApp/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Tests/UITestsHostApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIViewControllerBasedStatusBarAppearance
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Tests/UITestsHostApp/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Tests/UITestsHostApp/StatusBarStyle/HostingController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HostingController.swift
3 | // Blago
4 | //
5 | // Created by Dmytro Chumakov on 04.05.2023.
6 | //
7 |
8 | import SwiftUI
9 |
10 | final class HostingController: UIHostingController where ContentView: View {
11 |
12 | var statusBarStyle: UIStatusBarStyle = .darkContent
13 | var isInteractivePopGestureEnabled = true
14 |
15 | override var preferredStatusBarStyle: UIStatusBarStyle {
16 | statusBarStyle
17 | }
18 |
19 | override func viewDidLoad() {
20 | super.viewDidLoad()
21 | navigationController?.interactivePopGestureRecognizer?.isEnabled = isInteractivePopGestureEnabled
22 | }
23 |
24 | override func viewWillLayoutSubviews() {
25 | super.viewWillLayoutSubviews()
26 |
27 | guard #available(iOS 16, *) else {
28 | navigationController?.setNavigationBarHidden(true, animated: false)
29 | return
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Tests/UITestsHostApp/StatusBarStyle/NavigationView.swift:
--------------------------------------------------------------------------------
1 |
2 | //
3 | // NavigationController.swift
4 | // Blago
5 | //
6 | // Created by Dmytro Chumakov on 11.05.2023.
7 | //
8 |
9 | import UIKit
10 |
11 | // MARK: - NavigationController
12 |
13 | final class NavigationController: UINavigationController {
14 |
15 | static var shared: UINavigationController?
16 |
17 | // MARK: Lifecycle
18 |
19 | override init(rootViewController: UIViewController) {
20 | super.init(rootViewController: rootViewController)
21 | setNavigationBarHidden(true, animated: false)
22 | }
23 |
24 | required init?(coder _: NSCoder) {
25 | fatalError("init(coder:) has not been implemented")
26 | }
27 |
28 | override var preferredStatusBarStyle: UIStatusBarStyle {
29 | topViewController?.preferredStatusBarStyle ?? .default
30 | }
31 |
32 | override func viewDidLoad() {
33 | super.viewDidLoad()
34 | interactivePopGestureRecognizer?.delegate = self
35 | }
36 | }
37 |
38 | // MARK: UIGestureRecognizerDelegate
39 |
40 | extension NavigationController: UIGestureRecognizerDelegate {
41 | func gestureRecognizerShouldBegin(_: UIGestureRecognizer) -> Bool {
42 | viewControllers.count > 1
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Tests/UITestsHostApp/StatusBarStyle/RootView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootView.swift
3 | // Showcase
4 | //
5 | // Created by Orackle on 14.07.2023.
6 | //
7 |
8 | import SwiftUI
9 | import SwiftUIIntrospect
10 |
11 | struct RootView: View {
12 |
13 | var body: some View {
14 | VStack {
15 | Button("Navigate To Detail", action: navigateToDetail)
16 | }
17 | }
18 |
19 | @MainActor // for below Swift 6.0
20 | private func navigateToDetail() {
21 | let controller = HostingController(rootView: DetailView())
22 | controller.statusBarStyle = .lightContent
23 | NavigationController.shared?.pushViewController(controller, animated: true)
24 | }
25 | }
26 |
27 | struct DetailView: View {
28 | @Environment(\.presentationMode) var dismiss
29 |
30 | var body: some View {
31 | ZStack {
32 | Color.red.edgesIgnoringSafeArea(.all)
33 |
34 | VStack {
35 | Button("Navigate To Detail", action: navigateToDetail)
36 | Button("Navigate Back", action: goBack)
37 | }
38 | }
39 | .introspect(.viewController, on: .iOS(.v13, .v14, .v15, .v16)) { viewController in
40 | /// some customizations there
41 | }
42 | }
43 |
44 | private func goBack() {
45 | dismiss.wrappedValue.dismiss()
46 | }
47 |
48 | @MainActor // for below Swift 6.0
49 | private func navigateToDetail() {
50 | let controller = HostingController(rootView: DetailView())
51 | controller.statusBarStyle = .lightContent
52 | NavigationController.shared?.pushViewController(controller, animated: true)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Tests/UITestsHostApp/TestCases.swift:
--------------------------------------------------------------------------------
1 | public enum TestCase: String {
2 | case statusBarStyle = "Status Bar Style"
3 | }
4 |
--------------------------------------------------------------------------------
/Tests/UITestsHostApp/UITestsHostApp.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | @main
4 | final class AppDelegate: UIResponder, UIApplicationDelegate {
5 |
6 | var window: UIWindow?
7 |
8 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
9 | window = UIWindow(frame: UIScreen.main.bounds)
10 | guard
11 | let testCaseRawValue = ProcessInfo.processInfo.environment["testCase"],
12 | let testCase = TestCase(rawValue: testCaseRawValue)
13 | else {
14 | preconditionFailure("entryViewController not set")
15 | }
16 |
17 | window?.rootViewController = {
18 | switch testCase {
19 | case .statusBarStyle:
20 | let navController = NavigationController(rootViewController: HostingController(rootView: RootView()))
21 | NavigationController.shared = navController
22 | return navController
23 | }
24 | }()
25 | window?.makeKeyAndVisible()
26 | return true
27 | }
28 | }
29 |
--------------------------------------------------------------------------------