Merge branch 'master' into master

This commit is contained in:
David Roman 2023-06-06 13:02:40 +01:00 committed by GitHub
commit 1da25fa402
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
127 changed files with 7492 additions and 284 deletions

View File

@ -8,17 +8,23 @@ on:
jobs:
deploy:
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
podspec:
- Introspect.podspec
- SwiftUIIntrospect.podspec
steps:
- name: Git Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0 # required to be able to find Git tags
- name: Deploy to CocoaPods Trunk
- name: Deploy to CocoaPods Trunk (${{ matrix.podspec }})
run: |
set -eo pipefail
export LIB_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`)
pod lib lint --allow-warnings
pod trunk push --allow-warnings
pod lib lint ${{ matrix.podspec }} --allow-warnings
pod trunk push ${{ matrix.podspec }} --allow-warnings
env:
COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}

View File

@ -13,8 +13,8 @@ concurrency:
cancel-in-progress: true
jobs:
lint-podspec:
name: lint podspec
lint-podspecs:
name: lint podspecs
runs-on: macos-latest
steps:
- name: Git Checkout
@ -22,61 +22,77 @@ jobs:
with:
fetch-depth: 0 # required to be able to find Git tags
- name: Lint Podspec
- name: Lint Introspect.podspec
run: |
set -eo pipefail
export LIB_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`)
pod lib lint --allow-warnings
pod lib lint Introspect.podspec --allow-warnings
- name: Lint SwiftUIIntrospect.podspec
run: |
set -eo pipefail
export LIB_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`)
pod lib lint SwiftUIIntrospect.podspec --allow-warnings
ci:
name: ${{ matrix.platform[0] }} ${{ matrix.platform[1] }}
runs-on: ${{ matrix.os }}
runs-on: ${{ matrix.os || 'macos-13' }}
strategy:
fail-fast: false
matrix:
platform:
- [ios, 13]
- [ios, 14]
- [ios, 15]
- [ios, 16]
- [tvos, 13]
- [tvos, 14]
- [tvos, 15]
- [tvos, 16]
- [macos, 11]
- [macos, 12]
- [macos, 13]
include:
- platform: [ios, 13]
runtime: iOS 13.7
install: true
- platform: [ios, 14]
os: macos-11
xcode_version: 13.2.1
sdk: [12.5.1, iPhoneOS, iOS, 14.5]
- platform: [tvos, 14]
os: macos-11
xcode_version: 13.2.1
sdk: [12.5.1, AppleTVOS, tvOS, 14.5]
runtime: iOS 14.5
install: true
- platform: [ios, 15]
os: macos-12
xcode_version: 14.2
sdk: [13.4.1, iPhoneOS, iOS, 15.5]
- platform: [tvos, 15]
os: macos-12
xcode_version: 14.2
sdk: [13.4.1, AppleTVOS, tvOS, 15.5]
runtime: iOS 15.5
install: true
- platform: [ios, 16]
os: macos-12
xcode_version: 14.2
runtime: iOS 16.4
install: false
- platform: [tvos, 13]
runtime: tvOS 13.4
install: true
- platform: [tvos, 14]
runtime: tvOS 14.5
install: true
- platform: [tvos, 15]
runtime: tvOS 15.4
install: true
- platform: [tvos, 16]
os: macos-12
xcode_version: 14.2
runtime: tvOS 16.4
install: false
- platform: [macos, 11]
os: macos-11
xcode_version: 13.2.1
xcode: 13.2.1
install: false
- platform: [macos, 12]
os: macos-12
xcode_version: 14.2
xcode: 14.2
install: false
- platform: [macos, 13]
os: macos-13
xcode: 14.3
install: false
steps:
- name: Git Checkout
uses: actions/checkout@v3
@ -84,22 +100,32 @@ jobs:
- name: Select Xcode version
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ matrix.xcode_version }}
xcode-version: ${{ matrix.xcode || '14.3' }}
- if: ${{ matrix.sdk }}
name: Symlink SDK
run: |
echo "Creating Runtimes folder if needed..."
sudo mkdir -p /Library/Developer/CoreSimulator/Profiles/Runtimes
echo "Creating symlink of the ${{ matrix.sdk[2] }} ${{ matrix.sdk[3] }} runtime..."
sudo ln -s /Applications/Xcode_${{ matrix.sdk[0] }}.app/Contents/Developer/Platforms/${{ matrix.sdk[1] }}.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/${{ matrix.sdk[2] }}.simruntime /Library/Developer/CoreSimulator/Profiles/Runtimes/${{ matrix.sdk[2] }}\ ${{ matrix.sdk[3] }}.simruntime
- name: Install Homebrew dependencies
- name: Install xcbeautify
run: brew install xcbeautify
- name: Run Tests
run: fastlane test platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }}
env:
SKIP_SLOW_FASTLANE_WARNINGS: 1
FASTLANE_SKIP_UPDATE_CHECK: 1
- if: ${{ matrix.install }}
name: Install Required Runtime
run: |
brew install xcodesorg/made/xcodes
sudo xcodes runtimes install '${{ matrix.runtime }}'
- name: List Available Simulators
run: xcrun simctl list devices available
- if: ${{ join(matrix.platform, ' ') != 'macos 11' }}
name: Build Showcase
run: fastlane build platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }} scheme:Showcase
- if: ${{ join(matrix.platform, ' ') != 'ios 13' && join(matrix.platform, ' ') != 'tvos 13' }}
name: Run Tests (Introspect)
run: fastlane test platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }} scheme:Introspect
- if: ${{ join(matrix.platform, ' ') != 'macos 11' }}
name: Run Tests (SwiftUIIntrospect, Debug)
run: fastlane test platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }} scheme:SwiftUIIntrospectTests configuration:Debug
- if: ${{ join(matrix.platform, ' ') != 'macos 11' }}
name: Run Tests (SwiftUIIntrospect, Release)
run: fastlane test platform:${{ matrix.platform[0] }} version:${{ matrix.platform[1] }} scheme:SwiftUIIntrospectTests configuration:Release

View File

@ -3,8 +3,36 @@ Changelog
## master
## [0.5.2]
- Added: selector overrides (#239)
- Changed: optimized ancestor controller selectors (#240)
## [0.5.1]
- Fixed: SwiftUIIntrospect.podspec (#237)
## [0.5.0]
- Added: support for custom selectors (#233)
- Changed: unified introspect modifiers into one (#232)
- Fixed: `searchField` introspection (#234)
- Documentation: added explicit SPI import (#229)
## [0.4.0]
- Added: all-new implementation, API, and module (#207)
## [0.3.1]
- Fixed: wrong Swift version in podspec (#220)
## [0.3.0]
- Changed: minimum language version required is now Swift 5.5 (#209)
- Infrastructure: symlink older SDKs to use in newer Xcode versions (#208)
- Fixed: finding UIScrollViews that are clipped(), masked or combined with clipShape() or cornerRadius() (#213)
- Documentation: UICollectionView introspection support in README (#218)
- Infrastructure: symlink older SDKs to use in newer Xcode versions on CI (#208)
## [0.2.3]

View File

@ -3,24 +3,20 @@
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objectVersion = 55;
objects = {
/* Begin PBXBuildFile section */
D53071F729983CEF00F1936C /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53071F629983CEF00F1936C /* App.swift */; };
D53071F929983CEF00F1936C /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53071F829983CEF00F1936C /* ContentView.swift */; };
D53071FB29983CF000F1936C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D53071FA29983CF000F1936C /* Assets.xcassets */; };
D53071FE29983CF000F1936C /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D53071FD29983CF000F1936C /* Preview Assets.xcassets */; };
D530720729983DCA00F1936C /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = D530720629983DCA00F1936C /* Introspect */; };
D5B829752999738200920EBD /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B829742999738200920EBD /* Helpers.swift */; };
D5E3180329C132B6005847DC /* SwiftUIIntrospect in Frameworks */ = {isa = PBXBuildFile; productRef = D5E3180229C132B6005847DC /* SwiftUIIntrospect */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
D53071F329983CEF00F1936C /* Showcase.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Showcase.app; sourceTree = BUILT_PRODUCTS_DIR; };
D53071F629983CEF00F1936C /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
D53071F829983CEF00F1936C /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
D53071FA29983CF000F1936C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
D53071FD29983CF000F1936C /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
D530720429983D9300F1936C /* Showcase.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Showcase.entitlements; sourceTree = "<group>"; };
D5B829742999738200920EBD /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -30,7 +26,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D530720729983DCA00F1936C /* Introspect in Frameworks */,
D5E3180329C132B6005847DC /* SwiftUIIntrospect in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -61,20 +57,10 @@
D53071F629983CEF00F1936C /* App.swift */,
D53071F829983CEF00F1936C /* ContentView.swift */,
D5B829742999738200920EBD /* Helpers.swift */,
D53071FA29983CF000F1936C /* Assets.xcassets */,
D53071FC29983CF000F1936C /* Preview Content */,
);
path = Showcase;
sourceTree = "<group>";
};
D53071FC29983CF000F1936C /* Preview Content */ = {
isa = PBXGroup;
children = (
D53071FD29983CF000F1936C /* Preview Assets.xcassets */,
);
path = "Preview Content";
sourceTree = "<group>";
};
D530720529983DCA00F1936C /* Frameworks */ = {
isa = PBXGroup;
children = (
@ -99,7 +85,7 @@
);
name = Showcase;
packageProductDependencies = (
D530720629983DCA00F1936C /* Introspect */,
D5E3180229C132B6005847DC /* SwiftUIIntrospect */,
);
productName = Showcase;
productReference = D53071F329983CEF00F1936C /* Showcase.app */;
@ -121,7 +107,7 @@
};
};
buildConfigurationList = D53071EE29983CEF00F1936C /* Build configuration list for PBXProject "Showcase" */;
compatibilityVersion = "Xcode 14.0";
compatibilityVersion = "Xcode 13.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@ -143,8 +129,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D53071FE29983CF000F1936C /* Preview Assets.xcassets in Resources */,
D53071FB29983CF000F1936C /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -167,6 +151,7 @@
D53071FF29983CF000F1936C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
@ -214,20 +199,24 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MACOSX_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos";
SUPPORTS_MACCATALYST = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TVOS_DEPLOYMENT_TARGET = 14.0;
TVOS_DEPLOYMENT_TARGET = 13.0;
};
name = Debug;
};
D530720029983CF000F1936C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
@ -269,13 +258,16 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MACOSX_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos";
SUPPORTS_MACCATALYST = YES;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TVOS_DEPLOYMENT_TARGET = 14.0;
TVOS_DEPLOYMENT_TARGET = 13.0;
VALIDATE_PRODUCT = YES;
};
name = Release;
@ -283,18 +275,16 @@
D530720229983CF000F1936C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Showcase/Showcase.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"Showcase/Preview Content\"";
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UILaunchStoryboardName = "";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
LD_RUNPATH_SEARCH_PATHS = (
@ -304,7 +294,6 @@
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Showcase;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = YES;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = YES;
@ -316,18 +305,16 @@
D530720329983CF000F1936C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Showcase/Showcase.entitlements;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"Showcase/Preview Content\"";
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UILaunchStoryboardName = "";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
LD_RUNPATH_SEARCH_PATHS = (
@ -337,7 +324,6 @@
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Showcase;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = YES;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = YES;
@ -370,9 +356,9 @@
/* End XCConfigurationList section */
/* Begin XCSwiftPackageProductDependency section */
D530720629983DCA00F1936C /* Introspect */ = {
D5E3180229C132B6005847DC /* SwiftUIIntrospect */ = {
isa = XCSwiftPackageProductDependency;
productName = Introspect;
productName = SwiftUIIntrospect;
};
/* End XCSwiftPackageProductDependency section */
};

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1420"
LastUpgradeVersion = "1340"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -14,10 +14,10 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "Introspect-Dynamic"
BuildableName = "Introspect-Dynamic"
BlueprintName = "Introspect-Dynamic"
ReferencedContainer = "container:">
BlueprintIdentifier = "D53071F229983CEF00F1936C"
BuildableName = "Showcase.app"
BlueprintName = "Showcase"
ReferencedContainer = "container:Showcase.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
@ -40,6 +40,16 @@
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D53071F229983CEF00F1936C"
BuildableName = "Showcase.app"
BlueprintName = "Showcase"
ReferencedContainer = "container:Showcase.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
@ -47,15 +57,16 @@
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "Introspect-Dynamic"
BuildableName = "Introspect-Dynamic"
BlueprintName = "Introspect-Dynamic"
ReferencedContainer = "container:">
BlueprintIdentifier = "D53071F229983CEF00F1936C"
BuildableName = "Showcase.app"
BlueprintName = "Showcase"
ReferencedContainer = "container:Showcase.xcodeproj">
</BuildableReference>
</MacroExpansion>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">

View File

@ -1,5 +1,19 @@
import SwiftUI
#if os(iOS) || os(tvOS)
@UIApplicationMain
final class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = UIHostingController(rootView: ContentView())
window?.makeKeyAndVisible()
return true
}
}
#elseif os(macOS)
@main
struct App: SwiftUI.App {
var body: some Scene {
@ -8,3 +22,4 @@ struct App: SwiftUI.App {
}
}
}
#endif

View File

@ -1,11 +0,0 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -1,13 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -1,6 +0,0 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -1,38 +1,50 @@
import SwiftUI
import Introspect
import SwiftUIIntrospect
struct ContentView: View {
@State private var selection = 0
@State var selection = 0
var body: some View {
TabView(selection: $selection) {
ListShowcase()
.tabItem { Text("List") }
.tag(0)
.introspectTabBarController { tabBarController in
tabBarController.tabBar.layer.backgroundColor = UIColor.green.cgColor
}
ScrollViewShowcase()
.tabItem { Text("ScrollView") }
.tag(1)
#if !os(macOS)
NavigationShowcase()
.tabItem { Text("Navigation") }
.tag(2)
ViewControllerShowcase()
.tabItem { Text("ViewController") }
.tag(3)
#endif
SimpleElementsShowcase()
.tabItem { Text("Simple elements") }
.tag(4)
}
#if os(iOS) || os(tvOS)
.introspect(.tabView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { tabBarController in
tabBarController.tabBar.layer.backgroundColor = UIColor.green.cgColor
}
#elseif os(macOS)
.introspect(.tabView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { splitView in
splitView.subviews.first?.layer?.backgroundColor = NSColor.green.cgColor
}
#endif
.preferredColorScheme(.light)
}
}
struct ListShowcase: View {
var body: some View {
HStack {
VStack(spacing: 40) {
VStack {
Text("Default")
.lineLimit(1)
.minimumScaleFactor(0.5)
.padding(.horizontal, 12)
List {
Text("Item 1")
Text("Item 2")
@ -40,28 +52,54 @@ struct ListShowcase: View {
}
VStack {
Text("List.introspectTableView()")
Text(".introspect(.list, ...)")
.lineLimit(1)
.minimumScaleFactor(0.5)
.padding(.horizontal, 12)
.font(.system(.subheadline, design: .monospaced))
List {
Text("Item 1")
Text("Item 2")
}
.introspectTableView { tableView in
#if !os(tvOS)
tableView.separatorStyle = .none
#endif
#if os(iOS) || os(tvOS)
.introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { tableView in
tableView.backgroundView = UIView()
tableView.backgroundColor = .cyan
}
.introspect(.list, on: .iOS(.v16, .v17)) { collectionView in
collectionView.backgroundView = UIView()
collectionView.subviews.dropFirst(1).first?.backgroundColor = .cyan
}
#elseif os(macOS)
.introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { tableView in
tableView.backgroundColor = .cyan
}
#endif
}
VStack {
Text("child.introspectTableView()")
Text(".introspect(.list, ..., scope: .ancestor)")
.lineLimit(1)
.minimumScaleFactor(0.5)
.padding(.horizontal, 12)
.font(.system(.subheadline, design: .monospaced))
List {
Text("Item 1")
Text("Item 2")
.introspectTableView { tableView in
#if !os(tvOS)
tableView.separatorStyle = .none
#endif
#if os(iOS) || os(tvOS)
.introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { tableView in
tableView.backgroundView = UIView()
tableView.backgroundColor = .cyan
}
.introspect(.list, on: .iOS(.v16, .v17), scope: .ancestor) { collectionView in
collectionView.backgroundView = UIView()
collectionView.subviews.dropFirst(1).first?.backgroundColor = .cyan
}
#elseif os(macOS)
.introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { tableView in
tableView.backgroundColor = .cyan
}
#endif
}
}
}
@ -69,75 +107,114 @@ struct ListShowcase: View {
}
}
struct ScrollViewShowcase: View {
var body: some View {
VStack(spacing: 40) {
ScrollView {
Text("Default")
.frame(maxWidth: .infinity)
.lineLimit(1)
.minimumScaleFactor(0.5)
.padding(.horizontal, 12)
}
ScrollView {
Text(".introspect(.scrollView, ...)")
.frame(maxWidth: .infinity)
.lineLimit(1)
.minimumScaleFactor(0.5)
.padding(.horizontal, 12)
.font(.system(.subheadline, design: .monospaced))
}
#if os(iOS) || os(tvOS)
.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { scrollView in
scrollView.layer.backgroundColor = UIColor.cyan.cgColor
}
#elseif os(macOS)
.introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { scrollView in
scrollView.drawsBackground = true
scrollView.backgroundColor = .cyan
}
#endif
ScrollView {
Text(".introspect(.scrollView, ..., scope: .ancestor)")
.frame(maxWidth: .infinity)
.lineLimit(1)
.minimumScaleFactor(0.5)
.padding(.horizontal, 12)
.font(.system(.subheadline, design: .monospaced))
#if os(iOS) || os(tvOS)
.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { scrollView in
scrollView.layer.backgroundColor = UIColor.cyan.cgColor
}
#elseif os(macOS)
.introspect(.scrollView, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { scrollView in
scrollView.drawsBackground = true
scrollView.backgroundColor = .cyan
}
#endif
}
}
}
}
struct NavigationShowcase: View {
var body: some View {
NavigationView {
Text("Customized")
.do {
if #available(iOS 15, tvOS 15, *) {
Text("Content")
.modifier {
if #available(iOS 15, tvOS 15, macOS 12, *) {
$0.searchable(text: .constant(""))
} else {
$0
}
}
.do {
#if os(iOS)
if #available(iOS 15, *) {
$0.introspectSearchController { searchController in
searchController.searchBar.backgroundColor = .purple
}
}
#else
$0
#endif
}
#if !os(tvOS)
#if os(iOS)
.navigationBarTitle(Text("Customized"), displayMode: .inline)
#elseif os(macOS)
.navigationTitle(Text("Navigation"))
#endif
.introspectNavigationController { navigationController in
navigationController.navigationBar.backgroundColor = .red
}
}
.introspectSplitViewController { splitViewController in
#if os(iOS) || os(tvOS)
.introspect(.navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { navigationController in
navigationController.navigationBar.backgroundColor = .cyan
}
.introspect(.navigationView(style: .columns), on: .iOS(.v13, .v14, .v15, .v16, .v17)) { splitViewController in
splitViewController.preferredDisplayMode = .oneOverSecondary
}
.introspect(.navigationView(style: .columns), on: .tvOS(.v13, .v14, .v15, .v16, .v17)) { navigationController in
navigationController.navigationBar.backgroundColor = .cyan
}
.introspect(.searchField, on: .iOS(.v15, .v16, .v17), .tvOS(.v15, .v16, .v17)) { searchBar in
searchBar.backgroundColor = .red
#if os(iOS)
searchBar.searchTextField.backgroundColor = .purple
#endif
}
#endif
}
}
#if os(iOS) || os(tvOS)
struct ViewControllerShowcase: View {
var body: some View {
NavigationView {
VStack {
Text("Customized")
}
.introspectViewController { viewController in
viewController.navigationItem.title = "Customized"
}
}
}
}
struct ScrollViewShowcase: View {
var body: some View {
HStack {
ScrollView {
Text("Default")
}
ScrollView {
Text("ScrollView.introspectScrollView()")
}
.introspectScrollView { scrollView in
scrollView.layer.backgroundColor = UIColor.red.cgColor
}
ScrollView {
Text("child.introspectScrollView()")
.introspectScrollView { scrollView in
scrollView.layer.backgroundColor = UIColor.green.cgColor
}
Text(".introspect(.view, ...)")
.lineLimit(1)
.minimumScaleFactor(0.5)
.padding(.horizontal, 12)
.font(.system(.subheadline, design: .monospaced))
}
}
.navigationViewStyle(.stack)
.introspect(.view, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { viewController in
viewController.children.first?.view.backgroundColor = .cyan
}
}
}
#endif
struct SimpleElementsShowcase: View {
@ -151,28 +228,49 @@ struct SimpleElementsShowcase: View {
VStack {
HStack {
TextField("Text Field Red", text: $textFieldValue)
.introspectTextField { textField in
textField.layer.backgroundColor = UIColor.red.cgColor
#if os(iOS) || os(tvOS)
.introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { textField in
textField.backgroundColor = .red
}
#elseif os(macOS)
.introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { textField in
textField.backgroundColor = .red
}
#endif
TextField("Text Field Green", text: $textFieldValue)
.introspectTextField { textField in
textField.layer.backgroundColor = UIColor.green.cgColor
.cornerRadius(8)
#if os(iOS) || os(tvOS)
.introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { textField in
textField.backgroundColor = .green
}
#elseif os(macOS)
.introspect(.textField, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { textField in
textField.backgroundColor = .green
}
#endif
}
HStack {
Toggle("Toggle Red", isOn: $toggleValue)
#if !os(tvOS)
.introspectSwitch { uiSwitch in
uiSwitch.layer.backgroundColor = UIColor.red.cgColor
#if os(iOS)
.introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { toggle in
toggle.backgroundColor = .red
}
#elseif os(macOS)
.introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { toggle in
toggle.layer?.backgroundColor = NSColor.red.cgColor
}
#endif
Toggle("Toggle Green", isOn: $toggleValue)
#if !os(tvOS)
.introspectSwitch { uiSwitch in
uiSwitch.layer.backgroundColor = UIColor.green.cgColor
#if os(iOS)
.introspect(.toggle, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { toggle in
toggle.backgroundColor = .green
}
#elseif os(macOS)
.introspect(.toggle, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { toggle in
toggle.layer?.backgroundColor = NSColor.green.cgColor
}
#endif
}
@ -180,39 +278,69 @@ struct SimpleElementsShowcase: View {
#if !os(tvOS)
HStack {
Slider(value: $sliderValue, in: 0...100)
.introspectSlider { slider in
slider.layer.backgroundColor = UIColor.red.cgColor
#if os(iOS)
.introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { slider in
slider.backgroundColor = .red
}
#elseif os(macOS)
.introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { slider in
slider.layer?.backgroundColor = NSColor.red.cgColor
}
#endif
Slider(value: $sliderValue, in: 0...100)
.introspectSlider { slider in
slider.layer.backgroundColor = UIColor.green.cgColor
#if os(iOS)
.introspect(.slider, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { slider in
slider.backgroundColor = .green
}
#elseif os(macOS)
.introspect(.slider, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { slider in
slider.layer?.backgroundColor = NSColor.green.cgColor
}
#endif
}
HStack {
Stepper(onIncrement: {}, onDecrement: {}) {
Text("Stepper Red")
}
.introspectStepper { stepper in
stepper.layer.backgroundColor = UIColor.red.cgColor
#if os(iOS)
.introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { stepper in
stepper.backgroundColor = .red
}
#elseif os(macOS)
.introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { stepper in
stepper.layer?.backgroundColor = NSColor.red.cgColor
}
#endif
Stepper(onIncrement: {}, onDecrement: {}) {
Text("Stepper Green")
}
.introspectStepper { stepper in
stepper.layer.backgroundColor = UIColor.green.cgColor
#if os(iOS)
.introspect(.stepper, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { stepper in
stepper.backgroundColor = .green
}
#elseif os(macOS)
.introspect(.stepper, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { stepper in
stepper.layer?.backgroundColor = NSColor.green.cgColor
}
#endif
}
HStack {
DatePicker(selection: $datePickerValue) {
Text("DatePicker Red")
}
.introspectDatePicker { datePicker in
datePicker.layer.backgroundColor = UIColor.red.cgColor
#if os(iOS)
.introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { datePicker in
datePicker.backgroundColor = .red
}
#elseif os(macOS)
.introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { datePicker in
datePicker.layer?.backgroundColor = NSColor.red.cgColor
}
#endif
}
#endif
@ -223,9 +351,15 @@ struct SimpleElementsShowcase: View {
Text("Option 3").tag(2)
}
.pickerStyle(SegmentedPickerStyle())
.introspectSegmentedControl { segmentedControl in
segmentedControl.layer.backgroundColor = UIColor.red.cgColor
#if os(iOS) || os(tvOS)
.introspect(.picker(style: .segmented), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17)) { datePicker in
datePicker.backgroundColor = .red
}
#elseif os(macOS)
.introspect(.picker(style: .segmented), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { datePicker in
datePicker.layer?.backgroundColor = NSColor.red.cgColor
}
#endif
}
}
@ -234,9 +368,6 @@ struct SimpleElementsShowcase: View {
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ListShowcase()
NavigationShowcase()
}
ContentView()
}
}

View File

@ -1,7 +1,8 @@
import SwiftUI
extension View {
func `do`<TransformedView: View>(
@ViewBuilder
func modifier<TransformedView: View>(
@ViewBuilder transform: (Self) -> TransformedView
) -> TransformedView {
transform(self)

View File

@ -1,6 +0,0 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -12,7 +12,7 @@ Pod::Spec.new do |spec|
spec.source_files = 'Introspect/*.swift'
spec.swift_version = '5.4'
spec.swift_version = '5.5'
spec.ios.deployment_target = '13.0'
spec.tvos.deployment_target = '13.0'
spec.osx.deployment_target = '10.15'

View File

@ -4,6 +4,9 @@
<FileRef
location = "group:Examples/Showcase/Showcase.xcodeproj">
</FileRef>
<FileRef
location = "group:Tests/Tests.xcodeproj">
</FileRef>
<FileRef
location = "group:">
</FileRef>

View File

@ -14,9 +14,9 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "Introspect-Static"
BuildableName = "Introspect-Static"
BlueprintName = "Introspect-Static"
BlueprintIdentifier = "SwiftUIIntrospect"
BuildableName = "SwiftUIIntrospect"
BlueprintName = "SwiftUIIntrospect"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
@ -50,9 +50,9 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "Introspect-Static"
BuildableName = "Introspect-Static"
BlueprintName = "Introspect-Static"
BlueprintIdentifier = "SwiftUIIntrospect"
BuildableName = "SwiftUIIntrospect"
BlueprintName = "SwiftUIIntrospect"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>

View File

@ -318,6 +318,16 @@ public enum TargetViewSelector {
}
return Introspect.findAncestor(ofType: TargetView.self, from: entry)
}
public static func siblingOrAncestorOrSiblingContainingOrAncestorChild<TargetView: PlatformView>(from entry: PlatformView) -> TargetView? {
if let sibling: TargetView = siblingOfType(from: entry) {
return sibling
}
if let ancestor: TargetView = Introspect.findAncestor(ofType: TargetView.self, from: entry) {
return ancestor
}
return siblingContainingOrAncestorOrAncestorChild(from: entry)
}
public static func ancestorOrSiblingContaining<TargetView: PlatformView>(from entry: PlatformView) -> TargetView? {
if let tableView = Introspect.findAncestor(ofType: TargetView.self, from: entry) {

View File

@ -120,7 +120,7 @@ extension View {
/// Finds a `UIScrollView` from a `SwiftUI.ScrollView`, or `SwiftUI.ScrollView` child.
public func introspectScrollView(customize: @escaping (UIScrollView) -> ()) -> some View {
if #available(iOS 14, tvOS 14, *) {
return introspect(selector: TargetViewSelector.siblingOfTypeOrAncestor, customize: customize)
return introspect(selector: TargetViewSelector.siblingOrAncestorOrSiblingContainingOrAncestorChild, customize: customize)
} else {
return introspect(selector: TargetViewSelector.siblingContainingOrAncestor, customize: customize)
}
@ -229,7 +229,7 @@ extension View {
/// Finds a `NSScrollView` from a `SwiftUI.ScrollView`, or `SwiftUI.ScrollView` child.
public func introspectScrollView(customize: @escaping (NSScrollView) -> ()) -> some View {
if #available(macOS 11, *) {
return introspect(selector: TargetViewSelector.siblingOfTypeOrAncestor, customize: customize)
return introspect(selector: TargetViewSelector.siblingOrAncestorOrSiblingContainingOrAncestorChild, customize: customize)
} else {
return introspect(selector: TargetViewSelector.siblingContainingOrAncestor, customize: customize)
}

View File

@ -113,6 +113,32 @@ private struct NestedScrollTestView: View {
}
}
private struct MaskedScrollTestView: View {
let spy1: (NSScrollView) -> Void
let spy2: (NSScrollView) -> Void
var body: some View {
HStack {
ScrollView {
Text("Item 1")
}
.introspectScrollView { scrollView in
self.spy1(scrollView)
}
.clipped()
.clipShape(RoundedRectangle(cornerRadius: 20.0))
.cornerRadius(2.0)
ScrollView {
Text("Item 1")
.introspectScrollView { scrollView in
self.spy2(scrollView)
}
}
}
}
}
private struct TextFieldTestView: View {
let spy: () -> Void
@State private var textFieldValue = ""
@ -314,7 +340,6 @@ class AppKitTests: XCTestCase {
}
func testNestedScrollView() throws {
let expectation1 = XCTestExpectation()
let expectation2 = XCTestExpectation()
@ -339,6 +364,33 @@ class AppKitTests: XCTestCase {
XCTAssertNotEqual(unwrappedScrollView1, unwrappedScrollView2)
}
func testMaskedScrollView() throws {
let expectation1 = XCTestExpectation()
let expectation2 = XCTestExpectation()
var scrollView1: NSScrollView?
var scrollView2: NSScrollView?
let view = MaskedScrollTestView(
spy1: { scrollView in
scrollView1 = scrollView
expectation1.fulfill()
},
spy2: { scrollView in
scrollView2 = scrollView
expectation2.fulfill()
}
)
TestUtils.present(view: view)
wait(for: [expectation1, expectation2], timeout: TestUtils.Constants.timeout)
let unwrappedScrollView1 = try XCTUnwrap(scrollView1)
let unwrappedScrollView2 = try XCTUnwrap(scrollView2)
XCTAssertNotEqual(unwrappedScrollView1, unwrappedScrollView2)
}
func testTextField() {

View File

@ -183,6 +183,7 @@ private struct ListTestView: View {
}
}
private struct ScrollTestView: View {
let spy1: (UIScrollView) -> Void
@ -230,6 +231,32 @@ private struct NestedScrollTestView: View {
}
}
private struct MaskedScrollTestView: View {
let spy1: (UIScrollView) -> Void
let spy2: (UIScrollView) -> Void
var body: some View {
HStack {
ScrollView {
Text("Item 1")
}
.introspectScrollView { scrollView in
self.spy1(scrollView)
}
.clipped()
.clipShape(RoundedRectangle(cornerRadius: 20.0))
.cornerRadius(2.0)
ScrollView {
Text("Item 1")
.introspectScrollView { scrollView in
self.spy2(scrollView)
}
}
}
}
}
private struct TextFieldTestView: View {
let spy1: (UITextField) -> Void
let spy2: (UITextField) -> Void
@ -484,6 +511,34 @@ class UIKitTests: XCTestCase {
XCTAssertNotEqual(unwrappedScrollView1, unwrappedScrollView2)
}
func testMaskedScrollView() throws {
let expectation1 = XCTestExpectation()
let expectation2 = XCTestExpectation()
var scrollView1: UIScrollView?
var scrollView2: UIScrollView?
let view = MaskedScrollTestView(
spy1: { scrollView in
scrollView1 = scrollView
expectation1.fulfill()
},
spy2: { scrollView in
scrollView2 = scrollView
expectation2.fulfill()
}
)
TestUtils.present(view: view)
wait(for: [expectation1, expectation2], timeout: TestUtils.Constants.timeout)
let unwrappedScrollView1 = try XCTUnwrap(scrollView1)
let unwrappedScrollView2 = try XCTUnwrap(scrollView2)
XCTAssertNotEqual(unwrappedScrollView1, unwrappedScrollView2)
}
func testTextField() throws {

View File

@ -17,12 +17,12 @@ let package = Package(
targets: [
.target(
name: "Introspect",
path: "Introspect" // TODO: rename to Sources for v1.0
path: "Introspect"
),
.testTarget(
name: "IntrospectTests",
dependencies: ["Introspect"],
path: "IntrospectTests" // TODO: rename to Tests for v1.0
path: "IntrospectTests"
),
]
)

39
Package@swift-5.7.swift Normal file
View File

@ -0,0 +1,39 @@
// swift-tools-version:5.7
import PackageDescription
let package = Package(
name: "swiftui-introspect",
platforms: [
.iOS(.v13),
.macOS(.v10_15),
.tvOS(.v13),
],
products: [
// legacy library
.library(name: "Introspect", targets: ["Introspect"]),
.library(name: "Introspect-Static", type: .static, targets: ["Introspect"]),
.library(name: "Introspect-Dynamic", type: .dynamic, targets: ["Introspect"]),
// new experimental library
.library(name: "SwiftUIIntrospect", targets: ["SwiftUIIntrospect"]),
.library(name: "SwiftUIIntrospect-Static", type: .static, targets: ["SwiftUIIntrospect"]),
.library(name: "SwiftUIIntrospect-Dynamic", type: .dynamic, targets: ["SwiftUIIntrospect"]),
],
targets: [
.target(
name: "Introspect",
path: "Introspect"
),
.testTarget(
name: "IntrospectTests",
dependencies: ["Introspect"],
path: "IntrospectTests"
),
.target(
name: "SwiftUIIntrospect",
path: "Sources"
),
]
)

View File

@ -1,3 +1,11 @@
> **Note**
>
> [`SwiftUIIntrospect`](Package@swift-5.7.swift#L19) is an all-new module based off the original [`Introspect`](Package.swift#L13) module that improves on stability, predictability, and ergonomics.
>
> Both modules currently live together under this repo, but the plan is to ultimately obsolete `Introspect` in favor of `SwiftUIIntrospect` as part of a 1.0 release.
>
> Read the [`SwiftUIIntrospect` documentation](docs/SwiftUIIntrospect.md) to learn more.
Introspect for SwiftUI
======================
@ -54,8 +62,10 @@ NavigationView (DoubleColumnNavigationViewStyle) | UISplitViewController | _N/A_
NavigationView (DoubleColumnNavigationViewStyle) | _N/A_ | NSSplitView | `.introspectSplitView()`
_Any embedded view_ | UIViewController | _N/A_ | `.introspectViewController()`
ScrollView | UIScrollView | NSScrollView | `.introspectScrollView()`
List | UITableView | NSTableView | `.introspectTableView()`
View in List | UITableViewCell | NSTableCellView | `introspectTableViewCell()`
List (iOS15 and below) | UITableView | NSTableView | `.introspectTableView()`
View in List (iOS15 and below) | UITableViewCell | NSTableCellView | `introspectTableViewCell()`
List (iOS 16) | UICollectionView | _N/A_ | `.introspectCollectionView()`
View in List (iOS 16) | UICollectionViewCell | _N/A_ | `.introspectCollectionViewCell()`
TabView | UITabBarController | NSTabView | `.introspectTabBarController()` (iOS) <br/> `.introspectTabView()` (macOS)
TextField | UITextField | NSTextField | `.introspectTextField()`
Toggle | UISwitch | NSButton | `.introspectSwitch()` (iOS) <br/> `.introspectButton()` (macOS)

172
Sources/Introspect.swift Normal file
View File

@ -0,0 +1,172 @@
import SwiftUI
public struct IntrospectionScope: OptionSet {
public static let receiver = Self(rawValue: 1 << 0)
public static let ancestor = Self(rawValue: 1 << 1)
@_spi(Private) public let rawValue: UInt
@_spi(Private) public init(rawValue: UInt) {
self.rawValue = rawValue
}
}
extension View {
@ViewBuilder
public func introspect<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity>(
_ viewType: SwiftUIViewType,
on platforms: (PlatformViewVersions<SwiftUIViewType, PlatformSpecificEntity>)...,
scope: IntrospectionScope? = nil,
customize: @escaping (PlatformSpecificEntity) -> Void
) -> some View {
if let platform = platforms.first(where: \.isCurrent) {
let anchorID = IntrospectionAnchorID()
self.background(
IntrospectionAnchorView(
id: anchorID
)
.frame(width: 0, height: 0)
)
.overlay(
IntrospectionView(
selector: { entity in
(platform.selector ?? .default)(entity, scope ?? viewType.scope, anchorID)
},
customize: customize
)
.frame(width: 0, height: 0)
)
} else {
self
}
}
}
public protocol PlatformEntity: AnyObject {
associatedtype Base: PlatformEntity
@_spi(Internals)
var ancestor: Base? { get }
@_spi(Internals)
var descendants: [Base] { get }
@_spi(Internals)
func isDescendant(of other: Base) -> Bool
@_spi(Internals)
func entityWithTag(_ tag: Int) -> Base?
}
extension PlatformEntity {
@_spi(Internals)
public var ancestors: some Sequence<Base> {
sequence(first: self~, next: { $0.ancestor~ }).dropFirst()
}
@_spi(Internals)
public var allDescendants: [Base] {
self.descendants.reduce([self~]) { $0 + $1.allDescendants~ }
}
func nearestCommonAncestor(with other: Base) -> Base? {
var nearestAncestor: Base? = self~
while let currentEntity = nearestAncestor, !other.isDescendant(of: currentEntity~) {
nearestAncestor = currentEntity.ancestor~
}
return nearestAncestor
}
func descendantsBetween(_ bottomEntity: Base, and topEntity: Base) -> [Base] {
var result: [Base] = []
var entered = false
for descendant in self.allDescendants {
if descendant === bottomEntity {
entered = true
} else if descendant === topEntity {
break
} else if entered {
result.append(descendant)
}
}
return result
}
func receiver<PlatformSpecificEntity: PlatformEntity>(
ofType type: PlatformSpecificEntity.Type,
anchorID: IntrospectionAnchorID
) -> PlatformSpecificEntity? {
let frontEntity = self
guard
let backEntity = Array(frontEntity.ancestors).last?.entityWithTag(anchorID.hashValue),
let commonAncestor = backEntity.nearestCommonAncestor(with: frontEntity~)
else {
return nil
}
return commonAncestor
.descendantsBetween(backEntity~, and: frontEntity~)
.compactMap { $0 as? PlatformSpecificEntity }
.first
}
func ancestor<PlatformSpecificEntity: PlatformEntity>(
ofType type: PlatformSpecificEntity.Type
) -> PlatformSpecificEntity? {
self.ancestors
.lazy
.compactMap { $0 as? PlatformSpecificEntity }
.first
}
}
extension PlatformView: PlatformEntity {
@_spi(Internals)
public var ancestor: PlatformView? {
superview
}
@_spi(Internals)
public var descendants: [PlatformView] {
subviews
}
@_spi(Internals)
public func entityWithTag(_ tag: Int) -> PlatformView? {
viewWithTag(tag)
}
}
extension PlatformViewController: PlatformEntity {
@_spi(Internals)
public var ancestor: PlatformViewController? {
parent
}
@_spi(Internals)
public var descendants: [PlatformViewController] {
children
}
@_spi(Internals)
public func isDescendant(of other: PlatformViewController) -> Bool {
self.ancestors.contains(other)
}
@_spi(Internals)
public func entityWithTag(_ tag: Int) -> PlatformViewController? {
if self.view.tag == tag {
return self
}
for child in children {
if let childWithTag = child.entityWithTag(tag) {
return childWithTag
}
}
return nil
}
}

View File

@ -0,0 +1,7 @@
public protocol IntrospectableViewType {
var scope: IntrospectionScope { get }
}
extension IntrospectableViewType {
public var scope: IntrospectionScope { .receiver }
}

View File

@ -0,0 +1,77 @@
@_spi(Internals)
public struct IntrospectionSelector<Target: PlatformEntity> {
@_spi(Internals)
public static var `default`: Self { .from(Target.self, selector: { $0 }) }
@_spi(Internals)
public static func from<Entry: PlatformEntity>(_ entryType: Entry.Type, selector: @escaping (Entry) -> Target?) -> Self {
.init(
receiverSelector: { controller, anchorID in
controller.as(Entry.self)?.receiver(ofType: Entry.self, anchorID: anchorID).flatMap(selector)
},
ancestorSelector: { controller in
controller.as(Entry.self)?.ancestor(ofType: Entry.self).flatMap(selector)
}
)
}
private var receiverSelector: (IntrospectionPlatformViewController, IntrospectionAnchorID) -> Target?
private var ancestorSelector: (IntrospectionPlatformViewController) -> Target?
private init(
receiverSelector: @escaping (IntrospectionPlatformViewController, IntrospectionAnchorID) -> Target?,
ancestorSelector: @escaping (IntrospectionPlatformViewController) -> Target?
) {
self.receiverSelector = receiverSelector
self.ancestorSelector = ancestorSelector
}
@_spi(Internals)
public func withReceiverSelector(_ selector: @escaping (PlatformViewController, IntrospectionAnchorID) -> Target?) -> Self {
var copy = self
copy.receiverSelector = selector
return copy
}
@_spi(Internals)
public func withAncestorSelector(_ selector: @escaping (PlatformViewController) -> Target?) -> Self {
var copy = self
copy.ancestorSelector = selector
return copy
}
func callAsFunction(
_ controller: IntrospectionPlatformViewController,
_ scope: IntrospectionScope,
_ anchorID: IntrospectionAnchorID
) -> Target? {
if
scope.contains(.receiver),
let target = receiverSelector(controller, anchorID)
{
return target
}
if
scope.contains(.ancestor),
let target = ancestorSelector(controller)
{
return target
}
return nil
}
}
extension PlatformViewController {
func `as`<Entity: PlatformEntity>(_ entityType: Entity.Type) -> (any PlatformEntity)? {
if Entity.Base.self == PlatformView.self {
#if canImport(UIKit)
return viewIfLoaded
#elseif canImport(AppKit)
return isViewLoaded ? view : nil
#endif
} else if Entity.Base.self == PlatformViewController.self {
return self
}
return nil
}
}

View File

@ -0,0 +1,183 @@
import SwiftUI
@_spi(Internals)
public typealias IntrospectionAnchorID = UUID
///
struct IntrospectionAnchorView: PlatformViewControllerRepresentable {
#if canImport(UIKit)
typealias UIViewControllerType = IntrospectionAnchorPlatformViewController
#elseif canImport(AppKit)
typealias NSViewControllerType = IntrospectionAnchorPlatformViewController
#endif
@Binding
private var observed: Void // workaround for state changes not triggering view updates
let id: IntrospectionAnchorID
init(id: IntrospectionAnchorID) {
self._observed = .constant(())
self.id = id
}
func makePlatformViewController(context: Context) -> IntrospectionAnchorPlatformViewController {
IntrospectionAnchorPlatformViewController(id: id)
}
func updatePlatformViewController(_ controller: IntrospectionAnchorPlatformViewController, context: Context) {}
static func dismantlePlatformViewController(_ controller: IntrospectionAnchorPlatformViewController, coordinator: Coordinator) {}
}
final class IntrospectionAnchorPlatformViewController: PlatformViewController {
let id: IntrospectionAnchorID
init(id: IntrospectionAnchorID) {
self.id = id
super.init(nibName: nil, bundle: nil)
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#if canImport(UIKit)
override func viewDidLoad() {
super.viewDidLoad()
view.tag = id.hashValue
}
#elseif canImport(AppKit)
final class TaggableView: NSView {
private var _tag: Int?
override var tag: Int {
get { _tag ?? super.tag }
set { _tag = newValue }
}
}
override func loadView() {
let view = TaggableView()
view.tag = id.hashValue
self.view = view
}
#endif
}
struct IntrospectionView<Target: PlatformEntity>: PlatformViewControllerRepresentable {
#if canImport(UIKit)
typealias UIViewControllerType = IntrospectionPlatformViewController
#elseif canImport(AppKit)
typealias NSViewControllerType = IntrospectionPlatformViewController
#endif
final class TargetCache {
weak var target: Target?
}
@Binding
private var observed: Void // workaround for state changes not triggering view updates
private let selector: (IntrospectionPlatformViewController) -> Target?
private let customize: (Target) -> Void
init(
selector: @escaping (IntrospectionPlatformViewController) -> Target?,
customize: @escaping (Target) -> Void
) {
self._observed = .constant(())
self.selector = selector
self.customize = customize
}
func makeCoordinator() -> TargetCache {
TargetCache()
}
func makePlatformViewController(context: Context) -> IntrospectionPlatformViewController {
let controller = IntrospectionPlatformViewController { controller in
guard let target = selector(controller) else {
return
}
context.coordinator.target = target
customize(target)
controller.handler = nil
}
// - Workaround -
// iOS/tvOS 13 sometimes need a nudge on the next run loop.
if #available(iOS 14, tvOS 14, *) {} else {
DispatchQueue.main.async { [weak controller] in
controller?.handler?()
}
}
return controller
}
func updatePlatformViewController(_ controller: IntrospectionPlatformViewController, context: Context) {
guard let target = context.coordinator.target ?? selector(controller) else {
return
}
customize(target)
}
static func dismantlePlatformViewController(_ controller: IntrospectionPlatformViewController, coordinator: Coordinator) {
controller.handler = nil
}
}
final class IntrospectionPlatformViewController: PlatformViewController {
var handler: (() -> Void)? = nil
fileprivate init(handler: ((IntrospectionPlatformViewController) -> Void)?) {
super.init(nibName: nil, bundle: nil)
self.handler = { [weak self] in
guard let self = self else {
return
}
handler?(self)
}
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#if canImport(UIKit)
override func didMove(toParent parent: UIViewController?) {
super.didMove(toParent: parent)
handler?()
}
override func viewDidLoad() {
super.viewDidLoad()
handler?()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
handler?()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
handler?()
}
#elseif canImport(AppKit)
override func loadView() {
view = NSView()
}
override func viewDidLoad() {
super.viewDidLoad()
handler?()
}
override func viewDidAppear() {
super.viewDidAppear()
handler?()
}
#endif
}

View File

@ -0,0 +1,195 @@
import Foundation
public protocol PlatformVersion {
var isCurrent: Bool { get }
}
public struct iOSVersion: PlatformVersion {
public let isCurrent: Bool
public init(isCurrent: () -> Bool) {
self.isCurrent = isCurrent()
}
}
extension iOSVersion {
public static let v13 = iOSVersion {
if #available(iOS 14, *) {
return false
}
if #available(iOS 13, *) {
return true
}
return false
}
public static let v14 = iOSVersion {
if #available(iOS 15, *) {
return false
}
if #available(iOS 14, *) {
return true
}
return false
}
public static let v15 = iOSVersion {
if #available(iOS 16, *) {
return false
}
if #available(iOS 15, *) {
return true
}
return false
}
public static let v16 = iOSVersion {
if #available(iOS 17, *) {
return false
}
if #available(iOS 16, *) {
return true
}
return false
}
public static let v17 = iOSVersion {
if #available(iOS 18, *) {
return false
}
if #available(iOS 17, *) {
return true
}
return false
}
}
public struct tvOSVersion: PlatformVersion {
public let isCurrent: Bool
public init(isCurrent: () -> Bool) {
self.isCurrent = isCurrent()
}
}
extension tvOSVersion {
public static let v13 = tvOSVersion {
if #available(tvOS 14, *) {
return false
}
if #available(tvOS 13, *) {
return true
}
return false
}
public static let v14 = tvOSVersion {
if #available(tvOS 15, *) {
return false
}
if #available(tvOS 14, *) {
return true
}
return false
}
public static let v15 = tvOSVersion {
if #available(tvOS 16, *) {
return false
}
if #available(tvOS 15, *) {
return true
}
return false
}
public static let v16 = tvOSVersion {
if #available(tvOS 17, *) {
return false
}
if #available(tvOS 16, *) {
return true
}
return false
}
public static let v17 = tvOSVersion {
if #available(tvOS 18, *) {
return false
}
if #available(tvOS 17, *) {
return true
}
return false
}
}
public struct macOSVersion: PlatformVersion {
public let isCurrent: Bool
public init(isCurrent: () -> Bool) {
self.isCurrent = isCurrent()
}
}
extension macOSVersion {
public static let v10_15 = macOSVersion {
if #available(macOS 11, *) {
return false
}
if #available(macOS 10.15, *) {
return true
}
return false
}
public static let v10_15_4 = macOSVersion {
if #available(macOS 11, *) {
return false
}
if #available(macOS 10.15.4, *) {
return true
}
return false
}
public static let v11 = macOSVersion {
if #available(macOS 12, *) {
return false
}
if #available(macOS 11, *) {
return true
}
return false
}
public static let v12 = macOSVersion {
if #available(macOS 13, *) {
return false
}
if #available(macOS 12, *) {
return true
}
return false
}
public static let v13 = macOSVersion {
if #available(macOS 14, *) {
return false
}
if #available(macOS 13, *) {
return true
}
return false
}
public static let v14 = macOSVersion {
if #available(macOS 15, *) {
return false
}
if #available(macOS 14, *) {
return true
}
return false
}
}

View File

@ -0,0 +1,55 @@
import SwiftUI
#if canImport(UIKit)
public typealias PlatformView = UIView
#elseif canImport(AppKit)
public typealias PlatformView = NSView
#endif
#if canImport(UIKit)
public typealias PlatformViewController = UIViewController
#elseif canImport(AppKit)
public typealias PlatformViewController = NSViewController
#endif
#if canImport(UIKit)
typealias _PlatformViewControllerRepresentable = UIViewControllerRepresentable
#elseif canImport(AppKit)
typealias _PlatformViewControllerRepresentable = NSViewControllerRepresentable
#endif
protocol PlatformViewControllerRepresentable: _PlatformViewControllerRepresentable {
#if canImport(UIKit)
typealias ViewController = UIViewControllerType
#elseif canImport(AppKit)
typealias ViewController = NSViewControllerType
#endif
func makePlatformViewController(context: Context) -> ViewController
func updatePlatformViewController(_ controller: ViewController, context: Context)
static func dismantlePlatformViewController(_ controller: ViewController, coordinator: Coordinator)
}
extension PlatformViewControllerRepresentable {
#if canImport(UIKit)
func makeUIViewController(context: Context) -> ViewController {
makePlatformViewController(context: context)
}
func updateUIViewController(_ controller: ViewController, context: Context) {
updatePlatformViewController(controller, context: context)
}
static func dismantleUIViewController(_ controller: ViewController, coordinator: Coordinator) {
dismantlePlatformViewController(controller, coordinator: coordinator)
}
#elseif canImport(AppKit)
func makeNSViewController(context: Context) -> ViewController {
makePlatformViewController(context: context)
}
func updateNSViewController(_ controller: ViewController, context: Context) {
updatePlatformViewController(controller, context: context)
}
static func dismantleNSViewController(_ controller: ViewController, coordinator: Coordinator) {
dismantlePlatformViewController(controller, coordinator: coordinator)
}
#endif
}

View File

@ -0,0 +1,65 @@
import SwiftUI
public struct PlatformViewVersions<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity> {
let isCurrent: Bool
let selector: IntrospectionSelector<PlatformSpecificEntity>?
private init<Version: PlatformVersion>(
_ versions: [PlatformViewVersion<Version, SwiftUIViewType, PlatformSpecificEntity>]
) {
if let currentVersion = versions.first(where: \.isCurrent) {
self.isCurrent = true
self.selector = currentVersion.selector
} else {
self.isCurrent = false
self.selector = nil
}
}
public static func iOS(_ versions: (iOSViewVersion<SwiftUIViewType, PlatformSpecificEntity>)...) -> Self {
Self(versions)
}
public static func tvOS(_ versions: (tvOSViewVersion<SwiftUIViewType, PlatformSpecificEntity>)...) -> Self {
Self(versions)
}
public static func macOS(_ versions: (macOSViewVersion<SwiftUIViewType, PlatformSpecificEntity>)...) -> Self {
Self(versions)
}
}
public typealias iOSViewVersion<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity> =
PlatformViewVersion<iOSVersion, SwiftUIViewType, PlatformSpecificEntity>
public typealias tvOSViewVersion<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity> =
PlatformViewVersion<tvOSVersion, SwiftUIViewType, PlatformSpecificEntity>
public typealias macOSViewVersion<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity> =
PlatformViewVersion<macOSVersion, SwiftUIViewType, PlatformSpecificEntity>
public struct PlatformViewVersion<Version: PlatformVersion, SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity> {
let isCurrent: Bool
let selector: IntrospectionSelector<PlatformSpecificEntity>?
}
extension PlatformViewVersion {
@_spi(Internals) public init(for version: Version, selector: IntrospectionSelector<PlatformSpecificEntity>? = nil) {
self.init(isCurrent: version.isCurrent, selector: selector)
}
@_spi(Internals) public static func unavailable(file: StaticString = #file, line: UInt = #line) -> Self {
let filePath = file.withUTF8Buffer { String(decoding: $0, as: UTF8.self) }
let fileName = URL(fileURLWithPath: filePath).lastPathComponent
runtimeWarn(
"""
If you're seeing this, someone forgot to mark \(fileName):\(line) as unavailable.
This won't have any effect, but it should be disallowed altogether.
Please report it upstream so we can properly fix it by using the following link:
https://github.com/siteline/swiftui-introspect/issues/new?title=`\(fileName):\(line)`+should+be+marked+unavailable
"""
)
return Self(isCurrent: false, selector: nil)
}
}

View File

@ -0,0 +1,83 @@
// MIT License
//
// Copyright (c) 2020 Point-Free, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Foundation
@_transparent
@usableFromInline
@inline(__always)
func runtimeWarn(
_ message: @autoclosure () -> String,
category: String? = "SwiftUIIntrospect"
) {
#if DEBUG
let message = message()
let category = category ?? "Runtime Warning"
#if canImport(os)
os_log(
.fault,
dso: dso,
log: OSLog(subsystem: "com.apple.runtime-issues", category: category),
"%@",
message
)
#else
fputs("\(formatter.string(from: Date())) [\(category)] \(message)\n", stderr)
#endif
#endif
}
#if DEBUG
#if canImport(os)
import os
// NB: Xcode runtime warnings offer a much better experience than traditional assertions and
// breakpoints, but Apple provides no means of creating custom runtime warnings ourselves.
// To work around this, we hook into SwiftUI's runtime issue delivery mechanism, instead.
//
// Feedback filed: https://gist.github.com/stephencelis/a8d06383ed6ccde3e5ef5d1b3ad52bbc
@usableFromInline
let dso = { () -> UnsafeMutableRawPointer in
let count = _dyld_image_count()
for i in 0..<count {
if let name = _dyld_get_image_name(i) {
let swiftString = String(cString: name)
if swiftString.hasSuffix("/SwiftUI") {
if let header = _dyld_get_image_header(i) {
return UnsafeMutableRawPointer(mutating: UnsafeRawPointer(header))
}
}
}
}
return UnsafeMutableRawPointer(mutating: #dsohandle)
}()
#else
import Foundation
@usableFromInline
let formatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:MM:SS.sssZ"
return formatter
}()
#endif
#endif

View File

@ -0,0 +1,21 @@
#if os(macOS)
import SwiftUI
// MARK: SwiftUI.Button
public struct ButtonType: IntrospectableViewType {}
extension IntrospectableViewType where Self == ButtonType {
public static var button: Self { .init() }
}
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
extension macOSViewVersion<ButtonType, NSButton> {
public static let v10_15 = Self(for: .v10_15)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif
#endif

View File

@ -0,0 +1,33 @@
#if !os(tvOS)
import SwiftUI
// MARK: SwiftUI.ColorPicker
public struct ColorPickerType: IntrospectableViewType {}
extension IntrospectableViewType where Self == ColorPickerType {
public static var colorPicker: Self { .init() }
}
#if canImport(UIKit)
@available(iOS 14, *)
extension iOSViewVersion<ColorPickerType, UIColorWell> {
@available(*, unavailable, message: "ColorPicker isn't available on iOS 13")
public static let v13 = Self.unavailable()
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit)
@available(macOS 11, *)
extension macOSViewVersion<ColorPickerType, NSColorWell> {
@available(*, unavailable, message: "ColorPicker isn't available on macOS 10.15")
public static let v10_15 = Self.unavailable()
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif
#endif

View File

@ -0,0 +1,29 @@
#if os(iOS) || os(macOS)
import SwiftUI
// MARK: SwiftUI.DatePicker
public struct DatePickerType: IntrospectableViewType {}
extension IntrospectableViewType where Self == DatePickerType {
public static var datePicker: Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<DatePickerType, UIDatePicker> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit)
extension macOSViewVersion<DatePickerType, NSDatePicker> {
public static let v10_15 = Self(for: .v10_15)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif
#endif

View File

@ -0,0 +1,36 @@
#if os(iOS) || os(macOS)
import SwiftUI
// MARK: SwiftUI.DatePicker { ... }.datePickerStyle(.compact)
public struct DatePickerWithCompactStyleType: IntrospectableViewType {
public enum Style {
case compact
}
}
extension IntrospectableViewType where Self == DatePickerWithCompactStyleType {
public static func datePicker(style: Self.Style) -> Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<DatePickerWithCompactStyleType, UIDatePicker> {
@available(*, unavailable, message: ".datePickerStyle(.compact) isn't available on iOS 13")
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit) && !targetEnvironment(macCatalyst)
extension macOSViewVersion<DatePickerWithCompactStyleType, NSDatePicker> {
@available(*, unavailable, message: ".datePickerStyle(.compact) isn't available on macOS 10.15")
public static let v10_15 = Self(for: .v10_15)
public static let v10_15_4 = Self(for: .v10_15_4)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif
#endif

View File

@ -0,0 +1,25 @@
#if os(macOS)
import SwiftUI
// MARK: SwiftUI.DatePicker { ... }.datePickerStyle(.field)
public struct DatePickerWithFieldStyleType: IntrospectableViewType {
public enum Style {
case field
}
}
extension IntrospectableViewType where Self == DatePickerWithFieldStyleType {
public static func datePicker(style: Self.Style) -> Self { .init() }
}
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
extension macOSViewVersion<DatePickerWithFieldStyleType, NSDatePicker> {
public static let v10_15 = Self(for: .v10_15)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif
#endif

View File

@ -0,0 +1,34 @@
#if os(iOS) || os(macOS)
import SwiftUI
// MARK: SwiftUI.DatePicker { ... }.datePickerStyle(.graphical)
public struct DatePickerWithGraphicalStyleType: IntrospectableViewType {
public enum Style {
case graphical
}
}
extension IntrospectableViewType where Self == DatePickerWithGraphicalStyleType {
public static func datePicker(style: Self.Style) -> Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<DatePickerWithGraphicalStyleType, UIDatePicker> {
@available(*, unavailable, message: ".datePickerStyle(.graphical) isn't available on iOS 13")
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit) && !targetEnvironment(macCatalyst)
extension macOSViewVersion<DatePickerWithGraphicalStyleType, NSDatePicker> {
public static let v10_15 = Self(for: .v10_15)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif
#endif

View File

@ -0,0 +1,25 @@
#if os(macOS)
import SwiftUI
// MARK: SwiftUI.DatePicker { ... }.datePickerStyle(.stepperField)
public struct DatePickerWithStepperFieldStyleType: IntrospectableViewType {
public enum Style {
case stepperField
}
}
extension IntrospectableViewType where Self == DatePickerWithStepperFieldStyleType {
public static func datePicker(style: Self.Style) -> Self { .init() }
}
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
extension macOSViewVersion<DatePickerWithStepperFieldStyleType, NSDatePicker> {
public static let v10_15 = Self(for: .v10_15)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif
#endif

View File

@ -0,0 +1,25 @@
#if os(iOS)
import SwiftUI
// MARK: SwiftUI.DatePicker { ... }.datePickerStyle(.wheel)
public struct DatePickerWithWheelStyleType: IntrospectableViewType {
public enum Style {
case wheel
}
}
extension IntrospectableViewType where Self == DatePickerWithWheelStyleType {
public static func datePicker(style: Self.Style) -> Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<DatePickerWithWheelStyleType, UIDatePicker> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#endif
#endif

View File

@ -0,0 +1,32 @@
#if !os(macOS)
import SwiftUI
// MARK: SwiftUI.Form
public struct FormType: IntrospectableViewType {}
extension IntrospectableViewType where Self == FormType {
public static var form: Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<FormType, UITableView> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
}
extension iOSViewVersion<FormType, UICollectionView> {
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
extension tvOSViewVersion<FormType, UITableView> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#endif
#endif

View File

@ -0,0 +1,51 @@
import SwiftUI
// MARK: SwiftUI.Form { ... }.formStyle(.grouped)
public struct FormWithGroupedStyleType: IntrospectableViewType {
public enum Style {
case grouped
}
}
extension IntrospectableViewType where Self == FormWithGroupedStyleType {
public static func form(style: Self.Style) -> Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<FormWithGroupedStyleType, UITableView> {
@available(*, unavailable, message: ".formStyle(.grouped) isn't available on iOS 13")
public static let v13 = Self.unavailable()
@available(*, unavailable, message: ".formStyle(.grouped) isn't available on iOS 14")
public static let v14 = Self.unavailable()
@available(*, unavailable, message: ".formStyle(.grouped) isn't available on iOS 15")
public static let v15 = Self.unavailable()
}
extension iOSViewVersion<FormWithGroupedStyleType, UICollectionView> {
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
extension tvOSViewVersion<FormWithGroupedStyleType, UITableView> {
@available(*, unavailable, message: ".formStyle(.grouped) isn't available on tvOS 13")
public static let v13 = Self.unavailable()
@available(*, unavailable, message: ".formStyle(.grouped) isn't available on tvOS 14")
public static let v14 = Self.unavailable()
@available(*, unavailable, message: ".formStyle(.grouped) isn't available on tvOS 15")
public static let v15 = Self.unavailable()
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit)
extension macOSViewVersion<FormWithGroupedStyleType, NSScrollView> {
@available(*, unavailable, message: ".formStyle(.grouped) isn't available on macOS 10.15")
public static let v10_15 = Self.unavailable()
@available(*, unavailable, message: ".formStyle(.grouped) isn't available on macOS 11")
public static let v11 = Self.unavailable()
@available(*, unavailable, message: ".formStyle(.grouped) isn't available on macOS 12")
public static let v12 = Self.unavailable()
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif

View File

@ -0,0 +1,43 @@
import SwiftUI
// MARK: SwiftUI.List
public struct ListType: IntrospectableViewType {
public enum Style {
case plain
}
}
extension IntrospectableViewType where Self == ListType {
public static var list: Self { .init() }
public static func list(style: Self.Style) -> Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<ListType, UITableView> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
}
extension iOSViewVersion<ListType, UICollectionView> {
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
extension tvOSViewVersion<ListType, UITableView> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit)
extension macOSViewVersion<ListType, NSTableView> {
public static let v10_15 = Self(for: .v10_15)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif

View File

@ -0,0 +1,40 @@
import SwiftUI
// MARK: SwiftUI.List { Cell() }
public struct ListCellType: IntrospectableViewType {
public var scope: IntrospectionScope { .ancestor }
}
extension IntrospectableViewType where Self == ListCellType {
public static var listCell: Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<ListCellType, UITableViewCell> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
}
extension iOSViewVersion<ListCellType, UICollectionViewCell> {
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
extension tvOSViewVersion<ListCellType, UITableViewCell> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit)
extension macOSViewVersion<ListCellType, NSTableCellView> {
public static let v10_15 = Self(for: .v10_15)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif

View File

@ -0,0 +1,27 @@
#if os(macOS)
import SwiftUI
// MARK: SwiftUI.List { ... }.listStyle(.bordered)
public struct ListWithBorderedStyleType: IntrospectableViewType {
public enum Style {
case bordered
}
}
extension IntrospectableViewType where Self == ListWithBorderedStyleType {
public static func list(style: Self.Style) -> Self { .init() }
}
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
extension macOSViewVersion<ListWithBorderedStyleType, NSTableView> {
@available(*, unavailable, message: ".listStyle(.insetGrouped) isn't available on macOS 10.15")
public static let v10_15 = Self.unavailable()
@available(*, unavailable, message: ".listStyle(.insetGrouped) isn't available on macOS 11")
public static let v11 = Self.unavailable()
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif
#endif

View File

@ -0,0 +1,36 @@
#if os(iOS) || os(tvOS)
import SwiftUI
// MARK: SwiftUI.List { ... }.listStyle(.grouped)
public struct ListWithGroupedStyleType: IntrospectableViewType {
public enum Style {
case grouped
}
}
extension IntrospectableViewType where Self == ListWithGroupedStyleType {
public static func list(style: Self.Style) -> Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<ListWithGroupedStyleType, UITableView> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
}
extension iOSViewVersion<ListWithGroupedStyleType, UICollectionView> {
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
extension tvOSViewVersion<ListWithGroupedStyleType, UITableView> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#endif
#endif

View File

@ -0,0 +1,29 @@
#if os(iOS)
import SwiftUI
// MARK: SwiftUI.List { ... }.listStyle(.insetGrouped)
public struct ListWithInsetGroupedStyleType: IntrospectableViewType {
public enum Style {
case insetGrouped
}
}
extension IntrospectableViewType where Self == ListWithInsetGroupedStyleType {
public static func list(style: Self.Style) -> Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<ListWithInsetGroupedStyleType, UITableView> {
@available(*, unavailable, message: ".listStyle(.insetGrouped) isn't available on iOS 13")
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
}
extension iOSViewVersion<ListWithInsetGroupedStyleType, UICollectionView> {
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#endif
#endif

View File

@ -0,0 +1,38 @@
#if os(iOS) || os(macOS)
import SwiftUI
// MARK: SwiftUI.List { ... }.listStyle(.inset)
public struct ListWithInsetStyleType: IntrospectableViewType {
public enum Style {
case inset
}
}
extension IntrospectableViewType where Self == ListWithInsetStyleType {
public static func list(style: Self.Style) -> Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<ListWithInsetStyleType, UITableView> {
@available(*, unavailable, message: ".listStyle(.inset) isn't available on iOS 13")
public static let v13 = Self.unavailable()
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
}
extension iOSViewVersion<ListWithInsetStyleType, UICollectionView> {
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit)
extension macOSViewVersion<ListWithInsetStyleType, NSTableView> {
@available(*, unavailable, message: ".listStyle(.inset) isn't available on macOS 10.15")
public static let v10_15 = Self.unavailable()
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif
#endif

View File

@ -0,0 +1,37 @@
#if os(iOS) || os(macOS)
import SwiftUI
// MARK: SwiftUI.List { ... }.listStyle(.sidebar)
public struct ListWithSidebarStyleType: IntrospectableViewType {
public enum Style {
case sidebar
}
}
extension IntrospectableViewType where Self == ListWithSidebarStyleType {
public static func list(style: Self.Style) -> Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<ListWithSidebarStyleType, UITableView> {
@available(*, unavailable, message: ".listStyle(.sidebar) isn't available on iOS 13")
public static let v13 = Self.unavailable()
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
}
extension iOSViewVersion<ListWithSidebarStyleType, UICollectionView> {
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit)
extension macOSViewVersion<ListWithSidebarStyleType, NSTableView> {
public static let v10_15 = Self(for: .v10_15)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif
#endif

View File

@ -0,0 +1,55 @@
import SwiftUI
// MARK: SwiftUI.NavigationSplitView
public struct NavigationSplitViewType: IntrospectableViewType {}
extension IntrospectableViewType where Self == NavigationSplitViewType {
public static var navigationSplitView: Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<NavigationSplitViewType, UISplitViewController> {
@available(*, unavailable, message: "NavigationSplitView isn't available on iOS 13")
public static let v13 = Self.unavailable()
@available(*, unavailable, message: "NavigationSplitView isn't available on iOS 14")
public static let v14 = Self.unavailable()
@available(*, unavailable, message: "NavigationSplitView isn't available on iOS 15")
public static let v15 = Self.unavailable()
public static let v16 = Self(for: .v16, selector: selector)
public static let v17 = Self(for: .v17, selector: selector)
private static var selector: IntrospectionSelector<UISplitViewController> {
.default.withAncestorSelector(\.splitViewController)
}
}
extension tvOSViewVersion<NavigationSplitViewType, UINavigationController> {
@available(*, unavailable, message: "NavigationSplitView isn't available on tvOS 13")
public static let v13 = Self.unavailable()
@available(*, unavailable, message: "NavigationSplitView isn't available on tvOS 14")
public static let v14 = Self.unavailable()
@available(*, unavailable, message: "NavigationSplitView isn't available on tvOS 15")
public static let v15 = Self.unavailable()
public static let v16 = Self(for: .v16, selector: selector)
public static let v17 = Self(for: .v17, selector: selector)
private static var selector: IntrospectionSelector<UINavigationController> {
.default.withAncestorSelector(\.navigationController)
}
}
#elseif canImport(AppKit)
extension macOSViewVersion<NavigationSplitViewType, NSSplitView> {
@available(*, unavailable, message: "NavigationSplitView isn't available on macOS 10.15")
public static let v10_15 = Self.unavailable()
@available(*, unavailable, message: "NavigationSplitView isn't available on macOS 11")
public static let v11 = Self.unavailable()
@available(*, unavailable, message: "NavigationSplitView isn't available on macOS 12")
public static let v12 = Self.unavailable()
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif

View File

@ -0,0 +1,43 @@
import SwiftUI
// MARK: SwiftUI.NavigationStack
public struct NavigationStackType: IntrospectableViewType {}
extension IntrospectableViewType where Self == NavigationStackType {
public static var navigationStack: Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<NavigationStackType, UINavigationController> {
@available(*, unavailable, message: "NavigationStack isn't available on iOS 13")
public static let v13 = Self.unavailable()
@available(*, unavailable, message: "NavigationStack isn't available on iOS 14")
public static let v14 = Self.unavailable()
@available(*, unavailable, message: "NavigationStack isn't available on iOS 15")
public static let v15 = Self.unavailable()
public static let v16 = Self(for: .v16, selector: selector)
public static let v17 = Self(for: .v17, selector: selector)
private static var selector: IntrospectionSelector<UINavigationController> {
.default.withAncestorSelector(\.navigationController)
}
}
extension tvOSViewVersion<NavigationStackType, UINavigationController> {
@available(*, unavailable, message: "NavigationStack isn't available on tvOS 13")
public static let v13 = Self.unavailable()
@available(*, unavailable, message: "NavigationStack isn't available on tvOS 14")
public static let v14 = Self.unavailable()
@available(*, unavailable, message: "NavigationStack isn't available on tvOS 15")
public static let v15 = Self.unavailable()
public static let v16 = Self(for: .v16, selector: selector)
public static let v17 = Self(for: .v17, selector: selector)
private static var selector: IntrospectionSelector<UINavigationController> {
.default.withAncestorSelector(\.navigationController)
}
}
#endif

View File

@ -0,0 +1,47 @@
import SwiftUI
// MARK: SwiftUI.NavigationView { ... }.navigationViewStyle(.columns)
public struct NavigationViewWithColumnsStyleType: IntrospectableViewType {
public enum Style {
case columns
}
}
extension IntrospectableViewType where Self == NavigationViewWithColumnsStyleType {
public static func navigationView(style: Self.Style) -> Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<NavigationViewWithColumnsStyleType, UISplitViewController> {
public static let v13 = Self(for: .v13, selector: selector)
public static let v14 = Self(for: .v14, selector: selector)
public static let v15 = Self(for: .v15, selector: selector)
public static let v16 = Self(for: .v16, selector: selector)
public static let v17 = Self(for: .v17, selector: selector)
private static var selector: IntrospectionSelector<UISplitViewController> {
.default.withAncestorSelector(\.splitViewController)
}
}
extension tvOSViewVersion<NavigationViewWithColumnsStyleType, UINavigationController> {
public static let v13 = Self(for: .v13, selector: selector)
public static let v14 = Self(for: .v14, selector: selector)
public static let v15 = Self(for: .v15, selector: selector)
public static let v16 = Self(for: .v16, selector: selector)
public static let v17 = Self(for: .v17, selector: selector)
private static var selector: IntrospectionSelector<UINavigationController> {
.default.withAncestorSelector(\.navigationController)
}
}
#elseif canImport(AppKit)
extension macOSViewVersion<NavigationViewWithColumnsStyleType, NSSplitView> {
public static let v10_15 = Self(for: .v10_15)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif

View File

@ -0,0 +1,39 @@
import SwiftUI
// MARK: SwiftUI.NavigationView { ... }.navigationViewStyle(.stack)
public struct NavigationViewWithStackStyleType: IntrospectableViewType {
public enum Style {
case stack
}
}
extension IntrospectableViewType where Self == NavigationViewWithStackStyleType {
public static func navigationView(style: Self.Style) -> Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<NavigationViewWithStackStyleType, UINavigationController> {
public static let v13 = Self(for: .v13, selector: selector)
public static let v14 = Self(for: .v14, selector: selector)
public static let v15 = Self(for: .v15, selector: selector)
public static let v16 = Self(for: .v16, selector: selector)
public static let v17 = Self(for: .v17, selector: selector)
private static var selector: IntrospectionSelector<UINavigationController> {
.default.withAncestorSelector(\.navigationController)
}
}
extension tvOSViewVersion<NavigationViewWithStackStyleType, UINavigationController> {
public static let v13 = Self(for: .v13, selector: selector)
public static let v14 = Self(for: .v14, selector: selector)
public static let v15 = Self(for: .v15, selector: selector)
public static let v16 = Self(for: .v16, selector: selector)
public static let v17 = Self(for: .v17, selector: selector)
private static var selector: IntrospectionSelector<UINavigationController> {
.default.withAncestorSelector(\.navigationController)
}
}
#endif

View File

@ -0,0 +1,26 @@
#if os(macOS)
import SwiftUI
// MARK: SwiftUI.Picker { ... }.pickerStyle(.menu)
public struct PickerWithMenuStyleType: IntrospectableViewType {
public enum Style {
case menu
}
}
extension IntrospectableViewType where Self == PickerWithMenuStyleType {
public static func picker(style: Self.Style) -> Self { .init() }
}
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
extension macOSViewVersion<PickerWithMenuStyleType, NSPopUpButton> {
@available(*, unavailable, message: ".pickerStyle(.menu) isn't available on macOS 10.15")
public static let v10_15 = Self.unavailable()
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif
#endif

View File

@ -0,0 +1,39 @@
import SwiftUI
// MARK: SwiftUI.Picker { ... }.pickerStyle(.segmented)
public struct PickerWithSegmentedStyleType: IntrospectableViewType {
public enum Style {
case segmented
}
}
extension IntrospectableViewType where Self == PickerWithSegmentedStyleType {
public static func picker(style: Self.Style) -> Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<PickerWithSegmentedStyleType, UISegmentedControl> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
extension tvOSViewVersion<PickerWithSegmentedStyleType, UISegmentedControl> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit)
extension macOSViewVersion<PickerWithSegmentedStyleType, NSSegmentedControl> {
public static let v10_15 = Self(for: .v10_15)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif

View File

@ -0,0 +1,25 @@
#if os(iOS)
import SwiftUI
// MARK: SwiftUI.Picker { ... }.pickerStyle(.wheel)
public struct PickerWithWheelStyleType: IntrospectableViewType {
public enum Style {
case wheel
}
}
extension IntrospectableViewType where Self == PickerWithWheelStyleType {
public static func picker(style: Self.Style) -> Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<PickerWithWheelStyleType, UIPickerView> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#endif
#endif

View File

@ -0,0 +1,42 @@
import SwiftUI
// MARK: SwiftUI.ProgressView().progressViewStyle(.circular)
public struct ProgressViewWithCircularStyleType: IntrospectableViewType {
public enum Style {
case circular
}
}
extension IntrospectableViewType where Self == ProgressViewWithCircularStyleType {
public static func progressView(style: Self.Style) -> Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<ProgressViewWithCircularStyleType, UIActivityIndicatorView> {
@available(*, unavailable, message: ".progressViewStyle(.circular) isn't available on iOS 13")
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
extension tvOSViewVersion<ProgressViewWithCircularStyleType, UIActivityIndicatorView> {
@available(*, unavailable, message: ".progressViewStyle(.circular) isn't available on tvOS 13")
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit)
extension macOSViewVersion<ProgressViewWithCircularStyleType, NSProgressIndicator> {
@available(*, unavailable, message: ".progressViewStyle(.circular) isn't available on macOS 10.15")
public static let v10_15 = Self(for: .v10_15)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif

View File

@ -0,0 +1,42 @@
import SwiftUI
// MARK: SwiftUI.ProgressView().progressViewStyle(.linear)
public struct ProgressViewWithLinearStyleType: IntrospectableViewType {
public enum Style {
case linear
}
}
extension IntrospectableViewType where Self == ProgressViewWithLinearStyleType {
public static func progressView(style: Self.Style) -> Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<ProgressViewWithLinearStyleType, UIProgressView> {
@available(*, unavailable, message: ".progressViewStyle(.linear) isn't available on iOS 13")
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
extension tvOSViewVersion<ProgressViewWithLinearStyleType, UIProgressView> {
@available(*, unavailable, message: ".progressViewStyle(.linear) isn't available on tvOS 13")
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit)
extension macOSViewVersion<ProgressViewWithLinearStyleType, NSProgressIndicator> {
@available(*, unavailable, message: ".progressViewStyle(.linear) isn't available on macOS 10.15")
public static let v10_15 = Self(for: .v10_15)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif

View File

@ -0,0 +1,35 @@
import SwiftUI
// MARK: SwiftUI.ScrollView
public struct ScrollViewType: IntrospectableViewType {}
extension IntrospectableViewType where Self == ScrollViewType {
public static var scrollView: Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<ScrollViewType, UIScrollView> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
extension tvOSViewVersion<ScrollViewType, UIScrollView> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit)
extension macOSViewVersion<ScrollViewType, NSScrollView> {
public static let v10_15 = Self(for: .v10_15)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif

View File

@ -0,0 +1,43 @@
import SwiftUI
// MARK: SwiftUI.View.searchable(...)
public struct SearchFieldType: IntrospectableViewType {}
extension IntrospectableViewType where Self == SearchFieldType {
public static var searchField: Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<SearchFieldType, UISearchBar> {
@available(*, unavailable, message: ".searchable isn't available on iOS 13")
public static let v13 = Self.unavailable()
@available(*, unavailable, message: ".searchable isn't available on iOS 14")
public static let v14 = Self.unavailable()
public static let v15 = Self(for: .v15, selector: selector)
public static let v16 = Self(for: .v16, selector: selector)
public static let v17 = Self(for: .v17, selector: selector)
private static var selector: IntrospectionSelector<UISearchBar> {
.from(UINavigationController.self) {
$0.viewIfLoaded?.allDescendants.lazy.compactMap { $0 as? UISearchBar }.first
}
}
}
extension tvOSViewVersion<SearchFieldType, UISearchBar> {
@available(*, unavailable, message: ".searchable isn't available on tvOS 13")
public static let v13 = Self.unavailable()
@available(*, unavailable, message: ".searchable isn't available on tvOS 14")
public static let v14 = Self.unavailable()
public static let v15 = Self(for: .v15, selector: selector)
public static let v16 = Self(for: .v16, selector: selector)
public static let v17 = Self(for: .v17, selector: selector)
private static var selector: IntrospectionSelector<UISearchBar> {
.from(UINavigationController.self) {
$0.viewIfLoaded?.allDescendants.lazy.compactMap { $0 as? UISearchBar }.first
}
}
}
#endif

View File

@ -0,0 +1,29 @@
#if !os(tvOS)
import SwiftUI
// MARK: SwiftUI.Slider
public struct SliderType: IntrospectableViewType {}
extension IntrospectableViewType where Self == SliderType {
public static var slider: Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<SliderType, UISlider> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit)
extension macOSViewVersion<SliderType, NSSlider> {
public static let v10_15 = Self(for: .v10_15)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif
#endif

View File

@ -0,0 +1,29 @@
#if !os(tvOS)
import SwiftUI
// MARK: SwiftUI.Stepper
public struct StepperType: IntrospectableViewType {}
extension IntrospectableViewType where Self == StepperType {
public static var stepper: Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<StepperType, UIStepper> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit)
extension macOSViewVersion<StepperType, NSStepper> {
public static let v10_15 = Self(for: .v10_15)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif
#endif

View File

@ -0,0 +1,43 @@
import SwiftUI
// MARK: SwiftUI.TabView
public struct TabViewType: IntrospectableViewType {}
extension IntrospectableViewType where Self == TabViewType {
public static var tabView: Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<TabViewType, UITabBarController> {
public static let v13 = Self(for: .v13, selector: selector)
public static let v14 = Self(for: .v14, selector: selector)
public static let v15 = Self(for: .v15, selector: selector)
public static let v16 = Self(for: .v16, selector: selector)
public static let v17 = Self(for: .v17, selector: selector)
private static var selector: IntrospectionSelector<UITabBarController> {
.default.withAncestorSelector(\.tabBarController)
}
}
extension tvOSViewVersion<TabViewType, UITabBarController> {
public static let v13 = Self(for: .v13, selector: selector)
public static let v14 = Self(for: .v14, selector: selector)
public static let v15 = Self(for: .v15, selector: selector)
public static let v16 = Self(for: .v16, selector: selector)
public static let v17 = Self(for: .v17, selector: selector)
private static var selector: IntrospectionSelector<UITabBarController> {
.default.withAncestorSelector(\.tabBarController)
}
}
#elseif canImport(AppKit)
extension macOSViewVersion<TabViewType, NSTabView> {
public static let v10_15 = Self(for: .v10_15)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif

View File

@ -0,0 +1,35 @@
#if !os(macOS)
import SwiftUI
// MARK: SwiftUI.TabView { ... }.tabViewStyle(.page)
public struct TabViewWithPageStyleType: IntrospectableViewType {
public enum Style {
case page
}
}
extension IntrospectableViewType where Self == TabViewWithPageStyleType {
public static func tabView(style: Self.Style) -> Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<TabViewWithPageStyleType, UICollectionView> {
@available(*, unavailable, message: "TabView {}.tabViewStyle(.page) isn't available on iOS 13")
public static let v13 = Self.unavailable()
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
extension tvOSViewVersion<TabViewWithPageStyleType, UICollectionView> {
@available(*, unavailable, message: "TabView {}.tabViewStyle(.page) isn't available on tvOS 13")
public static let v13 = Self.unavailable()
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#endif
#endif

View File

@ -0,0 +1,34 @@
#if os(iOS) || os(macOS)
import SwiftUI
// MARK: SwiftUI.Table
public struct TableType: IntrospectableViewType {}
extension IntrospectableViewType where Self == TableType {
public static var table: Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<TableType, UICollectionView> {
@available(*, unavailable, message: "Table isn't available on iOS 13")
public static let v13 = Self(for: .v13)
@available(*, unavailable, message: "Table isn't available on iOS 14")
public static let v14 = Self(for: .v14)
@available(*, unavailable, message: "Table isn't available on iOS 15")
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit) && !targetEnvironment(macCatalyst)
extension macOSViewVersion<TableType, NSTableView> {
@available(*, unavailable, message: "Table isn't available on macOS 10.15")
public static let v10_15 = Self(for: .v10_15)
@available(*, unavailable, message: "Table isn't available on macOS 11")
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif
#endif

View File

@ -0,0 +1,31 @@
#if !os(tvOS)
import SwiftUI
// MARK: SwiftUI.TextEditor
public struct TextEditorType: IntrospectableViewType {}
extension IntrospectableViewType where Self == TextEditorType {
public static var textEditor: Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<TextEditorType, UITextView> {
@available(*, unavailable, message: "TextEditor isn't available on iOS 13")
public static let v13 = Self.unavailable()
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit)
extension macOSViewVersion<TextEditorType, NSTextView> {
@available(*, unavailable, message: "TextEditor isn't available on macOS 10.15")
public static let v10_15 = Self.unavailable()
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif
#endif

View File

@ -0,0 +1,35 @@
import SwiftUI
// MARK: SwiftUI.TextField
public struct TextFieldType: IntrospectableViewType {}
extension IntrospectableViewType where Self == TextFieldType {
public static var textField: Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<TextFieldType, UITextField> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
extension tvOSViewVersion<TextFieldType, UITextField> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit)
extension macOSViewVersion<TextFieldType, NSTextField> {
public static let v10_15 = Self(for: .v10_15)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif

View File

@ -0,0 +1,53 @@
import SwiftUI
// MARK: SwiftUI.TextField(..., axis: .vertical)
public struct TextFieldWithVerticalAxisType: IntrospectableViewType {
public enum Axis {
case vertical
}
}
extension IntrospectableViewType where Self == TextFieldWithVerticalAxisType {
public static func textField(axis: Self.Axis) -> Self { .init() }
}
// MARK: SwiftUI.TextField(..., axis: .vertical) - iOS
#if canImport(UIKit)
extension iOSViewVersion<TextFieldWithVerticalAxisType, UITextView> {
@available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on iOS 13")
public static let v13 = Self.unavailable()
@available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on iOS 14")
public static let v14 = Self.unavailable()
@available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on iOS 15")
public static let v15 = Self.unavailable()
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
extension tvOSViewVersion<TextFieldWithVerticalAxisType, UITextField> {
@available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on tvOS 13")
public static let v13 = Self.unavailable()
@available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on tvOS 14")
public static let v14 = Self.unavailable()
@available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on tvOS 15")
public static let v15 = Self.unavailable()
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit)
extension macOSViewVersion<TextFieldWithVerticalAxisType, NSTextField> {
@available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on macOS 10.15")
public static let v10_15 = Self.unavailable()
@available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on macOS 11")
public static let v11 = Self.unavailable()
@available(*, unavailable, message: "TextField(..., axis: .vertical) isn't available on macOS 12")
public static let v12 = Self.unavailable()
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif

View File

@ -0,0 +1,29 @@
#if !os(tvOS)
import SwiftUI
// MARK: SwiftUI.Toggle
public struct ToggleType: IntrospectableViewType {}
extension IntrospectableViewType where Self == ToggleType {
public static var toggle: Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<ToggleType, UISwitch> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit)
extension macOSViewVersion<ToggleType, NSButton> {
public static let v10_15 = Self(for: .v10_15)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif
#endif

View File

@ -0,0 +1,27 @@
#if os(macOS)
import SwiftUI
// MARK: SwiftUI.Toggle(...).toggleStyle(.button)
public struct ToggleWithButtonStyleType: IntrospectableViewType {
public enum Style {
case button
}
}
extension IntrospectableViewType where Self == ToggleWithButtonStyleType {
public static func toggle(style: Self.Style) -> Self { .init() }
}
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
extension macOSViewVersion<ToggleWithButtonStyleType, NSButton> {
@available(*, unavailable, message: ".toggleStyle(.button) isn't available on macOS 10.15")
public static let v10_15 = Self.unavailable()
@available(*, unavailable, message: ".toggleStyle(.button) isn't available on macOS 11")
public static let v11 = Self.unavailable()
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif
#endif

View File

@ -0,0 +1,25 @@
#if os(macOS)
import SwiftUI
// MARK: SwiftUI.Toggle(...).toggleStyle(.checkbox)
public struct ToggleWithCheckboxStyleType: IntrospectableViewType {
public enum Style {
case checkbox
}
}
extension IntrospectableViewType where Self == ToggleWithCheckboxStyleType {
public static func toggle(style: Self.Style) -> Self { .init() }
}
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
extension macOSViewVersion<ToggleWithCheckboxStyleType, NSButton> {
public static let v10_15 = Self(for: .v10_15)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif
#endif

View File

@ -0,0 +1,33 @@
#if !os(tvOS)
import SwiftUI
// MARK: SwiftUI.Toggle(...).toggleStyle(.switch)
public struct ToggleWithSwitchStyleType: IntrospectableViewType {
public enum Style {
case `switch`
}
}
extension IntrospectableViewType where Self == ToggleWithSwitchStyleType {
public static func toggle(style: Self.Style) -> Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<ToggleWithSwitchStyleType, UISwitch> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#elseif canImport(AppKit)
extension macOSViewVersion<ToggleWithSwitchStyleType, NSSwitch> {
public static let v10_15 = Self(for: .v10_15)
public static let v11 = Self(for: .v11)
public static let v12 = Self(for: .v12)
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
}
#endif
#endif

View File

@ -0,0 +1,29 @@
import SwiftUI
// MARK: SwiftUI.View
public struct ViewType: IntrospectableViewType {
public var scope: IntrospectionScope { [.receiver, .ancestor] }
}
extension IntrospectableViewType where Self == ViewType {
public static var view: Self { .init() }
}
#if canImport(UIKit)
extension iOSViewVersion<ViewType, UIViewController> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
extension tvOSViewVersion<ViewType, UIViewController> {
public static let v13 = Self(for: .v13)
public static let v14 = Self(for: .v14)
public static let v15 = Self(for: .v15)
public static let v16 = Self(for: .v16)
public static let v17 = Self(for: .v17)
}
#endif

9
Sources/Voodoo.swift Normal file
View File

@ -0,0 +1,9 @@
postfix operator ~
postfix func ~ <LHS, T>(lhs: LHS) -> T {
lhs as! T
}
postfix func ~ <LHS, T>(lhs: LHS?) -> T? {
lhs as? T
}

19
SwiftUIIntrospect.podspec Normal file
View File

@ -0,0 +1,19 @@
Pod::Spec.new do |spec|
spec.name = 'SwiftUIIntrospect'
spec.version = ENV['LIB_VERSION']
spec.license = { type: 'MIT' }
spec.homepage = 'https://github.com/siteline/swiftui-introspect'
spec.author = 'David Roman'
spec.summary = 'Introspect underlying UIKit/AppKit components from SwiftUI.'
spec.source = {
git: 'https://github.com/siteline/swiftui-introspect.git',
tag: spec.version
}
spec.source_files = 'Sources/**/*.swift'
spec.swift_version = '5.7'
spec.ios.deployment_target = '13.0'
spec.tvos.deployment_target = '13.0'
spec.osx.deployment_target = '10.15'
end

View File

@ -0,0 +1,14 @@
import SwiftUI
@main
final class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = UIHostingController(rootView: EmptyView())
window?.makeKeyAndVisible()
return true
}
}

9
Tests/Package.swift Normal file
View File

@ -0,0 +1,9 @@
// swift-tools-version:5.5
import PackageDescription
let package = Package(
name: "Tests",
products: [],
targets: []
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1420"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D50E2F592A2B9F6600BAFB03"
BuildableName = "LegacyTests.xctest"
BlueprintName = "LegacyTests"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D50E2F4D2A2B9DEE00BAFB03"
BuildableName = "LegacyTestsHostApp.app"
BlueprintName = "LegacyTestsHostApp"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D50E2F4D2A2B9DEE00BAFB03"
BuildableName = "LegacyTestsHostApp.app"
BlueprintName = "LegacyTestsHostApp"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1420"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D5F0BE4829C0DBE800AD95AB"
BuildableName = "TestsHostApp.app"
BlueprintName = "TestsHostApp"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D5F0BE5C29C0DC0000AD95AB"
BuildableName = "Tests.xctest"
BlueprintName = "Tests"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D5F0BE4829C0DBE800AD95AB"
BuildableName = "TestsHostApp.app"
BlueprintName = "TestsHostApp"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D5F0BE4829C0DBE800AD95AB"
BuildableName = "TestsHostApp.app"
BlueprintName = "TestsHostApp"
ReferencedContainer = "container:Tests.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,130 @@
import SwiftUIIntrospect
import XCTest
final class PlatformTests: XCTestCase {
func test_iOS() {
#if os(iOS)
if #available(iOS 17, *) {
XCTAssertEqual(iOSVersion.v17.isCurrent, true)
XCTAssertEqual(iOSVersion.v16.isCurrent, false)
XCTAssertEqual(iOSVersion.v15.isCurrent, false)
XCTAssertEqual(iOSVersion.v14.isCurrent, false)
XCTAssertEqual(iOSVersion.v13.isCurrent, false)
} else if #available(iOS 16, *) {
XCTAssertEqual(iOSVersion.v17.isCurrent, false)
XCTAssertEqual(iOSVersion.v16.isCurrent, true)
XCTAssertEqual(iOSVersion.v15.isCurrent, false)
XCTAssertEqual(iOSVersion.v14.isCurrent, false)
XCTAssertEqual(iOSVersion.v13.isCurrent, false)
} else if #available(iOS 15, *) {
XCTAssertEqual(iOSVersion.v17.isCurrent, false)
XCTAssertEqual(iOSVersion.v16.isCurrent, false)
XCTAssertEqual(iOSVersion.v15.isCurrent, true)
XCTAssertEqual(iOSVersion.v14.isCurrent, false)
XCTAssertEqual(iOSVersion.v13.isCurrent, false)
} else if #available(iOS 14, *) {
XCTAssertEqual(iOSVersion.v17.isCurrent, false)
XCTAssertEqual(iOSVersion.v16.isCurrent, false)
XCTAssertEqual(iOSVersion.v15.isCurrent, false)
XCTAssertEqual(iOSVersion.v14.isCurrent, true)
XCTAssertEqual(iOSVersion.v13.isCurrent, false)
} else if #available(iOS 13, *) {
XCTAssertEqual(iOSVersion.v17.isCurrent, false)
XCTAssertEqual(iOSVersion.v16.isCurrent, false)
XCTAssertEqual(iOSVersion.v15.isCurrent, false)
XCTAssertEqual(iOSVersion.v14.isCurrent, false)
XCTAssertEqual(iOSVersion.v13.isCurrent, true)
}
#else
XCTAssertEqual(iOSVersion.v17.isCurrent, false)
XCTAssertEqual(iOSVersion.v16.isCurrent, false)
XCTAssertEqual(iOSVersion.v15.isCurrent, false)
XCTAssertEqual(iOSVersion.v14.isCurrent, false)
XCTAssertEqual(iOSVersion.v13.isCurrent, false)
#endif
}
func test_macOS() {
#if os(macOS)
if #available(macOS 14, *) {
XCTAssertEqual(macOSVersion.v14.isCurrent, true)
XCTAssertEqual(macOSVersion.v13.isCurrent, false)
XCTAssertEqual(macOSVersion.v12.isCurrent, false)
XCTAssertEqual(macOSVersion.v11.isCurrent, false)
XCTAssertEqual(macOSVersion.v10_15.isCurrent, false)
} else if #available(macOS 13, *) {
XCTAssertEqual(macOSVersion.v14.isCurrent, false)
XCTAssertEqual(macOSVersion.v13.isCurrent, true)
XCTAssertEqual(macOSVersion.v12.isCurrent, false)
XCTAssertEqual(macOSVersion.v11.isCurrent, false)
XCTAssertEqual(macOSVersion.v10_15.isCurrent, false)
} else if #available(macOS 12, *) {
XCTAssertEqual(macOSVersion.v14.isCurrent, false)
XCTAssertEqual(macOSVersion.v13.isCurrent, false)
XCTAssertEqual(macOSVersion.v12.isCurrent, true)
XCTAssertEqual(macOSVersion.v11.isCurrent, false)
XCTAssertEqual(macOSVersion.v10_15.isCurrent, false)
} else if #available(macOS 11, *) {
XCTAssertEqual(macOSVersion.v14.isCurrent, false)
XCTAssertEqual(macOSVersion.v13.isCurrent, false)
XCTAssertEqual(macOSVersion.v12.isCurrent, false)
XCTAssertEqual(macOSVersion.v11.isCurrent, true)
XCTAssertEqual(macOSVersion.v10_15.isCurrent, false)
} else if #available(macOS 10.15, *) {
XCTAssertEqual(macOSVersion.v14.isCurrent, false)
XCTAssertEqual(macOSVersion.v13.isCurrent, false)
XCTAssertEqual(macOSVersion.v12.isCurrent, false)
XCTAssertEqual(macOSVersion.v11.isCurrent, false)
XCTAssertEqual(macOSVersion.v10_15.isCurrent, true)
}
#else
XCTAssertEqual(macOSVersion.v14.isCurrent, false)
XCTAssertEqual(macOSVersion.v13.isCurrent, false)
XCTAssertEqual(macOSVersion.v12.isCurrent, false)
XCTAssertEqual(macOSVersion.v11.isCurrent, false)
XCTAssertEqual(macOSVersion.v10_15.isCurrent, false)
#endif
}
func test_tvOS() {
#if os(tvOS)
if #available(tvOS 17, *) {
XCTAssertEqual(tvOSVersion.v17.isCurrent, true)
XCTAssertEqual(tvOSVersion.v16.isCurrent, false)
XCTAssertEqual(tvOSVersion.v15.isCurrent, false)
XCTAssertEqual(tvOSVersion.v14.isCurrent, false)
XCTAssertEqual(tvOSVersion.v13.isCurrent, false)
} else if #available(tvOS 16, *) {
XCTAssertEqual(tvOSVersion.v17.isCurrent, false)
XCTAssertEqual(tvOSVersion.v16.isCurrent, true)
XCTAssertEqual(tvOSVersion.v15.isCurrent, false)
XCTAssertEqual(tvOSVersion.v14.isCurrent, false)
XCTAssertEqual(tvOSVersion.v13.isCurrent, false)
} else if #available(tvOS 15, *) {
XCTAssertEqual(tvOSVersion.v17.isCurrent, false)
XCTAssertEqual(tvOSVersion.v16.isCurrent, false)
XCTAssertEqual(tvOSVersion.v15.isCurrent, true)
XCTAssertEqual(tvOSVersion.v14.isCurrent, false)
XCTAssertEqual(tvOSVersion.v13.isCurrent, false)
} else if #available(tvOS 14, *) {
XCTAssertEqual(tvOSVersion.v17.isCurrent, false)
XCTAssertEqual(tvOSVersion.v16.isCurrent, false)
XCTAssertEqual(tvOSVersion.v15.isCurrent, false)
XCTAssertEqual(tvOSVersion.v14.isCurrent, true)
XCTAssertEqual(tvOSVersion.v13.isCurrent, false)
} else if #available(tvOS 13, *) {
XCTAssertEqual(tvOSVersion.v17.isCurrent, false)
XCTAssertEqual(tvOSVersion.v16.isCurrent, false)
XCTAssertEqual(tvOSVersion.v15.isCurrent, false)
XCTAssertEqual(tvOSVersion.v14.isCurrent, false)
XCTAssertEqual(tvOSVersion.v13.isCurrent, true)
}
#else
XCTAssertEqual(tvOSVersion.v17.isCurrent, false)
XCTAssertEqual(tvOSVersion.v16.isCurrent, false)
XCTAssertEqual(tvOSVersion.v15.isCurrent, false)
XCTAssertEqual(tvOSVersion.v14.isCurrent, false)
XCTAssertEqual(tvOSVersion.v13.isCurrent, false)
#endif
}
}

108
Tests/Tests/TestUtils.swift Normal file
View File

@ -0,0 +1,108 @@
import SwiftUI
import XCTest
#if canImport(UIKit)
enum TestUtils {
private static let window = UIWindow(frame: UIScreen.main.bounds)
static func present(view: some View) {
window.rootViewController = UIHostingController(rootView: view)
window.makeKeyAndVisible()
window.layoutIfNeeded()
}
}
#elseif canImport(AppKit)
enum TestUtils {
private static let window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered,
defer: true
)
static func present(view: some View) {
window.contentView = NSHostingView(rootView: view)
window.makeKeyAndOrderFront(nil)
window.layoutIfNeeded()
}
}
#endif
func XCTAssertViewIntrospection<V: View, PV: AnyObject>(
of type: PV.Type,
@ViewBuilder view: (Spies<PV>) -> V,
extraAssertions: ([PV]) -> Void = { _ in },
file: StaticString = #file,
line: UInt = #line
) {
let spies = Spies<PV>()
let view = view(spies)
TestUtils.present(view: view)
XCTWaiter(delegate: spies).wait(for: spies.expectations.values.map(\.0), timeout: 3)
extraAssertions(spies.objects.sorted(by: { $0.key < $1.key }).map(\.value))
}
final class Spies<PV: AnyObject>: NSObject, XCTWaiterDelegate {
private(set) var objects: [Int: PV] = [:]
private(set) var expectations: [ObjectIdentifier: (XCTestExpectation, StaticString, UInt)] = [:]
subscript(
number: Int,
file: StaticString = #file,
line: UInt = #line
) -> (PV) -> Void {
let expectation = XCTestExpectation()
expectations[ObjectIdentifier(expectation)] = (expectation, file, line)
return { [self] in
if let object = objects[number] {
XCTAssert(object === $0, "Found view was overriden by another view", file: file, line: line)
}
objects[number] = $0
expectation.fulfill()
}
}
func waiter(
_ waiter: XCTWaiter,
didTimeoutWithUnfulfilledExpectations unfulfilledExpectations: [XCTestExpectation]
) {
for expectation in unfulfilledExpectations {
let (_, file, line) = expectations[ObjectIdentifier(expectation)]!
XCTFail("Spy not called", file: file, line: line)
}
}
func nestedWaiter(
_ waiter: XCTWaiter,
wasInterruptedByTimedOutWaiter outerWaiter: XCTWaiter
) {
XCTFail("wasInterruptedByTimedOutWaiter")
}
func waiter(
_ waiter: XCTWaiter,
fulfillmentDidViolateOrderingConstraintsFor expectation: XCTestExpectation,
requiredExpectation: XCTestExpectation
) {
XCTFail("fulfillmentDidViolateOrderingConstraintsFor")
}
func waiter(
_ waiter: XCTWaiter,
didFulfillInvertedExpectation expectation: XCTestExpectation
) {
XCTFail("didFulfillInvertedExpectation")
}
}
extension Collection {
subscript(safe index: Index, file: StaticString = #file, line: UInt = #line) -> Element? {
get {
guard indices.contains(index) else {
XCTFail("Index \(index) is out of bounds", file: file, line: line)
return nil
}
return self[index]
}
}
}

View File

@ -0,0 +1,49 @@
#if os(macOS)
import SwiftUI
import SwiftUIIntrospect
import XCTest
final class ButtonTests: XCTestCase {
#if canImport(AppKit)
typealias PlatformButton = NSButton
#endif
func testButton() {
XCTAssertViewIntrospection(of: PlatformButton.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
let spy2 = spies[2]
let spy3 = spies[3]
VStack {
Button("Button 0", action: {})
.buttonStyle(.bordered)
#if os(macOS)
.introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
#endif
Button("Button 1", action: {})
.buttonStyle(.borderless)
#if os(macOS)
.introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
#endif
Button("Button 2", action: {})
.buttonStyle(.link)
#if os(macOS)
.introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
#endif
Button("Button 3", action: {})
#if os(macOS)
.introspect(.button, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy3)
#endif
}
} extraAssertions: {
#if canImport(AppKit)
XCTAssert(Set($0.map(ObjectIdentifier.init)).count == 4)
#endif
}
}
}
#endif

View File

@ -0,0 +1,61 @@
#if !os(tvOS)
import SwiftUI
import SwiftUIIntrospect
import XCTest
@available(iOS 14, macOS 11, *)
final class ColorPickerTests: XCTestCase {
#if canImport(UIKit)
typealias PlatformColor = UIColor
typealias PlatformColorPicker = UIColorWell
#elseif canImport(AppKit)
typealias PlatformColor = NSColor
typealias PlatformColorPicker = NSColorWell
#endif
func testColorPicker() throws {
guard #available(iOS 14, macOS 11, *) else {
throw XCTSkip()
}
XCTAssertViewIntrospection(of: PlatformColorPicker.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
let spy2 = spies[2]
VStack {
ColorPicker("", selection: .constant(PlatformColor.red.cgColor))
#if os(iOS)
.introspect(.colorPicker, on: .iOS(.v14, .v15, .v16, .v17), customize: spy0)
#elseif os(macOS)
.introspect(.colorPicker, on: .macOS(.v11, .v12, .v13, .v14), customize: spy0)
#endif
ColorPicker("", selection: .constant(PlatformColor.green.cgColor))
#if os(iOS)
.introspect(.colorPicker, on: .iOS(.v14, .v15, .v16, .v17), customize: spy1)
#elseif os(macOS)
.introspect(.colorPicker, on: .macOS(.v11, .v12, .v13, .v14), customize: spy1)
#endif
ColorPicker("", selection: .constant(PlatformColor.blue.cgColor))
#if os(iOS)
.introspect(.colorPicker, on: .iOS(.v14, .v15, .v16, .v17), customize: spy2)
#elseif os(macOS)
.introspect(.colorPicker, on: .macOS(.v11, .v12, .v13, .v14), customize: spy2)
#endif
}
} extraAssertions: {
#if canImport(UIKit)
XCTAssertEqual($0[safe: 0]?.selectedColor, .red)
XCTAssertEqual($0[safe: 1]?.selectedColor, .green)
XCTAssertEqual($0[safe: 2]?.selectedColor, .blue)
#elseif canImport(AppKit)
XCTAssertEqual($0[safe: 0]?.color, .red)
XCTAssertEqual($0[safe: 1]?.color, .green)
XCTAssertEqual($0[safe: 2]?.color, .blue)
#endif
}
}
}
#endif

View File

@ -0,0 +1,60 @@
#if os(iOS) || os(macOS)
import SwiftUI
import SwiftUIIntrospect
import XCTest
final class DatePickerTests: XCTestCase {
#if canImport(UIKit)
typealias PlatformDatePicker = UIDatePicker
#elseif canImport(AppKit)
typealias PlatformDatePicker = NSDatePicker
#endif
func testDatePicker() {
let date0 = Date(timeIntervalSince1970: 0)
let date1 = Date(timeIntervalSince1970: 5)
let date2 = Date(timeIntervalSince1970: 10)
XCTAssertViewIntrospection(of: PlatformDatePicker.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
let spy2 = spies[2]
VStack {
DatePicker("", selection: .constant(date0))
#if os(iOS)
.introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy0)
#elseif os(macOS)
.introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
#endif
.cornerRadius(8)
DatePicker("", selection: .constant(date1))
#if os(iOS)
.introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy1)
#elseif os(macOS)
.introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
#endif
.cornerRadius(8)
DatePicker("", selection: .constant(date2))
#if os(iOS)
.introspect(.datePicker, on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy2)
#elseif os(macOS)
.introspect(.datePicker, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
#endif
}
} extraAssertions: {
#if canImport(UIKit)
XCTAssertEqual($0[safe: 0]?.date, date0)
XCTAssertEqual($0[safe: 1]?.date, date1)
XCTAssertEqual($0[safe: 2]?.date, date2)
#elseif canImport(AppKit)
XCTAssertEqual($0[safe: 0]?.dateValue, date0)
XCTAssertEqual($0[safe: 1]?.dateValue, date1)
XCTAssertEqual($0[safe: 2]?.dateValue, date2)
#endif
}
}
}
#endif

View File

@ -0,0 +1,68 @@
#if os(iOS) || os(macOS)
import SwiftUI
import SwiftUIIntrospect
import XCTest
@available(iOS 14, macOS 10.15.4, *)
final class DatePickerWithCompactStyleTests: XCTestCase {
#if canImport(UIKit)
typealias PlatformDatePickerWithCompactStyle = UIDatePicker
#elseif canImport(AppKit)
typealias PlatformDatePickerWithCompactStyle = NSDatePicker
#endif
func testDatePickerWithCompactStyle() throws {
guard #available(iOS 14, macOS 10.15.4, *) else {
throw XCTSkip()
}
let date0 = Date(timeIntervalSince1970: 0)
let date1 = Date(timeIntervalSince1970: 5)
let date2 = Date(timeIntervalSince1970: 10)
XCTAssertViewIntrospection(of: PlatformDatePickerWithCompactStyle.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
let spy2 = spies[2]
VStack {
DatePicker("", selection: .constant(date0))
.datePickerStyle(.compact)
#if os(iOS)
.introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17), customize: spy0)
#elseif os(macOS)
.introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13, .v14), customize: spy0)
#endif
.cornerRadius(8)
DatePicker("", selection: .constant(date1))
.datePickerStyle(.compact)
#if os(iOS)
.introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17), customize: spy1)
#elseif os(macOS)
.introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13, .v14), customize: spy1)
#endif
.cornerRadius(8)
DatePicker("", selection: .constant(date2))
.datePickerStyle(.compact)
#if os(iOS)
.introspect(.datePicker(style: .compact), on: .iOS(.v14, .v15, .v16, .v17), customize: spy2)
#elseif os(macOS)
.introspect(.datePicker(style: .compact), on: .macOS(.v10_15_4, .v11, .v12, .v13, .v14), customize: spy2)
#endif
}
} extraAssertions: {
#if canImport(UIKit)
XCTAssertEqual($0[safe: 0]?.date, date0)
XCTAssertEqual($0[safe: 1]?.date, date1)
XCTAssertEqual($0[safe: 2]?.date, date2)
#elseif canImport(AppKit)
XCTAssertEqual($0[safe: 0]?.dateValue, date0)
XCTAssertEqual($0[safe: 1]?.dateValue, date1)
XCTAssertEqual($0[safe: 2]?.dateValue, date2)
#endif
}
}
}
#endif

View File

@ -0,0 +1,51 @@
#if os(macOS)
import SwiftUI
import SwiftUIIntrospect
import XCTest
final class DatePickerWithFieldStyleTests: XCTestCase {
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
typealias PlatformDatePickerWithFieldStyle = NSDatePicker
#endif
func testDatePickerWithFieldStyle() {
let date0 = Date(timeIntervalSince1970: 0)
let date1 = Date(timeIntervalSince1970: 5)
let date2 = Date(timeIntervalSince1970: 10)
XCTAssertViewIntrospection(of: PlatformDatePickerWithFieldStyle.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
let spy2 = spies[2]
VStack {
DatePicker("", selection: .constant(date0))
.datePickerStyle(.field)
#if os(macOS)
.introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
#endif
.cornerRadius(8)
DatePicker("", selection: .constant(date1))
.datePickerStyle(.field)
#if os(macOS)
.introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
#endif
.cornerRadius(8)
DatePicker("", selection: .constant(date2))
.datePickerStyle(.field)
#if os(macOS)
.introspect(.datePicker(style: .field), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
#endif
}
} extraAssertions: {
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
XCTAssertEqual($0[safe: 0]?.dateValue, date0)
XCTAssertEqual($0[safe: 1]?.dateValue, date1)
XCTAssertEqual($0[safe: 2]?.dateValue, date2)
#endif
}
}
}
#endif

View File

@ -0,0 +1,68 @@
#if os(iOS) || os(macOS)
import SwiftUI
import SwiftUIIntrospect
import XCTest
@available(iOS 14, *)
final class DatePickerWithGraphicalStyleTests: XCTestCase {
#if canImport(UIKit)
typealias PlatformDatePickerWithGraphicalStyle = UIDatePicker
#elseif canImport(AppKit)
typealias PlatformDatePickerWithGraphicalStyle = NSDatePicker
#endif
func testDatePickerWithGraphicalStyle() throws {
guard #available(iOS 14, *) else {
throw XCTSkip()
}
let date0 = Date(timeIntervalSince1970: 0)
let date1 = Date(timeIntervalSince1970: 3600 * 24 * 1)
let date2 = Date(timeIntervalSince1970: 3600 * 24 * 2)
XCTAssertViewIntrospection(of: PlatformDatePickerWithGraphicalStyle.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
let spy2 = spies[2]
VStack {
DatePicker("", selection: .constant(date0))
.datePickerStyle(.graphical)
#if os(iOS)
.introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17), customize: spy0)
#elseif os(macOS)
.introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
#endif
.cornerRadius(8)
DatePicker("", selection: .constant(date1))
.datePickerStyle(.graphical)
#if os(iOS)
.introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17), customize: spy1)
#elseif os(macOS)
.introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
#endif
.cornerRadius(8)
DatePicker("", selection: .constant(date2))
.datePickerStyle(.graphical)
#if os(iOS)
.introspect(.datePicker(style: .graphical), on: .iOS(.v14, .v15, .v16, .v17), customize: spy2)
#elseif os(macOS)
.introspect(.datePicker(style: .graphical), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
#endif
}
} extraAssertions: {
#if canImport(UIKit)
XCTAssertEqual($0[safe: 0]?.date, date0)
XCTAssertEqual($0[safe: 1]?.date, date1)
XCTAssertEqual($0[safe: 2]?.date, date2)
#elseif canImport(AppKit)
XCTAssertEqual($0[safe: 0]?.dateValue, date0)
XCTAssertEqual($0[safe: 1]?.dateValue, date1)
XCTAssertEqual($0[safe: 2]?.dateValue, date2)
#endif
}
}
}
#endif

View File

@ -0,0 +1,51 @@
#if os(macOS)
import SwiftUI
import SwiftUIIntrospect
import XCTest
final class DatePickerWithWheelStyleTests: XCTestCase {
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
typealias PlatformDatePickerWithWheelStyle = NSDatePicker
#endif
func testDatePickerWithWheelStyle() {
let date0 = Date(timeIntervalSince1970: 0)
let date1 = Date(timeIntervalSince1970: 5)
let date2 = Date(timeIntervalSince1970: 10)
XCTAssertViewIntrospection(of: PlatformDatePickerWithWheelStyle.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
let spy2 = spies[2]
VStack {
DatePicker("", selection: .constant(date0))
.datePickerStyle(.stepperField)
#if os(macOS)
.introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy0)
#endif
.cornerRadius(8)
DatePicker("", selection: .constant(date1))
.datePickerStyle(.stepperField)
#if os(macOS)
.introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy1)
#endif
.cornerRadius(8)
DatePicker("", selection: .constant(date2))
.datePickerStyle(.stepperField)
#if os(macOS)
.introspect(.datePicker(style: .stepperField), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), customize: spy2)
#endif
}
} extraAssertions: {
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
XCTAssertEqual($0[safe: 0]?.dateValue, date0)
XCTAssertEqual($0[safe: 1]?.dateValue, date1)
XCTAssertEqual($0[safe: 2]?.dateValue, date2)
#endif
}
}
}
#endif

View File

@ -0,0 +1,51 @@
#if os(iOS)
import SwiftUI
import SwiftUIIntrospect
import XCTest
final class DatePickerWithWheelStyleTests: XCTestCase {
#if canImport(UIKit)
typealias PlatformDatePickerWithWheelStyle = UIDatePicker
#endif
func testDatePickerWithWheelStyle() {
let date0 = Date(timeIntervalSince1970: 0)
let date1 = Date(timeIntervalSince1970: 5)
let date2 = Date(timeIntervalSince1970: 10)
XCTAssertViewIntrospection(of: PlatformDatePickerWithWheelStyle.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
let spy2 = spies[2]
VStack {
DatePicker("", selection: .constant(date0))
.datePickerStyle(.wheel)
#if os(iOS)
.introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy0)
#endif
.cornerRadius(8)
DatePicker("", selection: .constant(date1))
.datePickerStyle(.wheel)
#if os(iOS)
.introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy1)
#endif
.cornerRadius(8)
DatePicker("", selection: .constant(date2))
.datePickerStyle(.wheel)
#if os(iOS)
.introspect(.datePicker(style: .wheel), on: .iOS(.v13, .v14, .v15, .v16, .v17), customize: spy2)
#endif
}
} extraAssertions: {
#if canImport(UIKit)
XCTAssertEqual($0[safe: 0]?.date, date0)
XCTAssertEqual($0[safe: 1]?.date, date1)
XCTAssertEqual($0[safe: 2]?.date, date2)
#endif
}
}
}
#endif

View File

@ -0,0 +1,40 @@
#if !os(macOS)
import SwiftUI
import SwiftUIIntrospect
import XCTest
final class FormTests: XCTestCase {
#if canImport(UIKit)
typealias PlatformForm = UIScrollView // covers both UITableView and UICollectionView
#elseif canImport(AppKit)
typealias PlatformForm = NSScrollView
#endif
func testForm() throws {
XCTAssertViewIntrospection(of: PlatformForm.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
HStack {
Form {
Text("Item 1")
}
#if os(iOS) || os(tvOS)
.introspect(.form, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) }
.introspect(.form, on: .iOS(.v16, .v17)) { spy0($0) }
#endif
Form {
Text("Item 1")
#if os(iOS) || os(tvOS)
.introspect(.form, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { spy1($0) }
.introspect(.form, on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) }
#endif
}
}
} extraAssertions: {
XCTAssert($0[safe: 0] !== $0[safe: 1])
}
}
}
#endif

View File

@ -0,0 +1,49 @@
import SwiftUI
import SwiftUIIntrospect
import XCTest
@available(iOS 16, tvOS 16, macOS 13, *)
final class FormWithGroupedStyleTests: XCTestCase {
#if canImport(UIKit)
typealias PlatformFormWithGroupedStyle = UIScrollView // covers both UITableView and UICollectionView
#elseif canImport(AppKit)
typealias PlatformFormWithGroupedStyle = NSScrollView
#endif
func testFormWithGroupedStyle() throws {
guard #available(iOS 16, tvOS 16, macOS 13, *) else {
throw XCTSkip()
}
XCTAssertViewIntrospection(of: PlatformFormWithGroupedStyle.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
HStack {
Form {
Text("Item 1")
}
.formStyle(.grouped)
#if os(iOS) || os(tvOS)
.introspect(.form(style: .grouped), on: .iOS(.v16, .v17)) { spy0($0) }
.introspect(.form(style: .grouped), on: .tvOS(.v16, .v17)) { spy0($0) }
#elseif os(macOS)
.introspect(.form(style: .grouped), on: .macOS(.v13, .v14)) { spy0($0) }
#endif
Form {
Text("Item 1")
#if os(iOS) || os(tvOS)
.introspect(.form(style: .grouped), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) }
.introspect(.form(style: .grouped), on: .tvOS(.v16, .v17), scope: .ancestor) { spy1($0) }
#elseif os(macOS)
.introspect(.form(style: .grouped), on: .macOS(.v13, .v14), scope: .ancestor) { spy1($0) }
#endif
}
.formStyle(.grouped)
}
} extraAssertions: {
XCTAssert($0[safe: 0] !== $0[safe: 1])
}
}
}

View File

@ -0,0 +1,46 @@
import SwiftUI
import SwiftUIIntrospect
import XCTest
final class ListCellTests: XCTestCase {
#if canImport(UIKit)
typealias PlatformListCell = UIView // covers both UITableViewCell and UICollectionViewCell
#elseif canImport(AppKit)
typealias PlatformListCell = NSTableCellView
#endif
func testListCell() {
XCTAssertViewIntrospection(of: PlatformListCell.self) { spies in
let spy = spies[0]
List {
Text("Item 1")
#if os(iOS) || os(tvOS)
.introspect(.listCell, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy($0) }
.introspect(.listCell, on: .iOS(.v16, .v17)) { spy($0) }
#elseif os(macOS)
.introspect(.listCell, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy($0) }
#endif
}
}
}
func testMaskedListCell() {
XCTAssertViewIntrospection(of: PlatformListCell.self) { spies in
let spy = spies[0]
List {
Text("Item 1")
#if os(iOS) || os(tvOS)
.introspect(.listCell, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy($0) }
.introspect(.listCell, on: .iOS(.v16, .v17)) { spy($0) }
#elseif os(macOS)
.introspect(.listCell, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy($0) }
#endif
.clipped()
.clipShape(RoundedRectangle(cornerRadius: 20.0))
.cornerRadius(2.0)
}
}
}
}

View File

@ -0,0 +1,103 @@
import SwiftUI
import SwiftUIIntrospect
import XCTest
final class ListTests: XCTestCase {
#if canImport(UIKit)
typealias PlatformList = UIScrollView // covers both UITableView and UICollectionView
#elseif canImport(AppKit)
typealias PlatformList = NSTableView
#endif
func testList() {
XCTAssertViewIntrospection(of: PlatformList.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
HStack {
List {
Text("Item 1")
}
#if os(iOS) || os(tvOS)
.introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) }
.introspect(.list, on: .iOS(.v16, .v17)) { spy0($0) }
#elseif os(macOS)
.introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy0($0) }
#endif
List {
Text("Item 1")
#if os(iOS) || os(tvOS)
.introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { spy1($0) }
.introspect(.list, on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) }
#elseif os(macOS)
.introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) }
#endif
}
}
} extraAssertions: {
XCTAssert($0[safe: 0] !== $0[safe: 1])
}
}
#if !os(macOS)
func testNestedList() {
XCTAssertViewIntrospection(of: PlatformList.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
List {
Text("Item 1")
List {
Text("Item 1")
}
#if os(iOS) || os(tvOS)
.introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy1($0) }
.introspect(.list, on: .iOS(.v16, .v17)) { spy1($0) }
#endif
}
#if os(iOS) || os(tvOS)
.introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) }
.introspect(.list, on: .iOS(.v16, .v17)) { spy0($0) }
#endif
} extraAssertions: {
XCTAssert($0[safe: 0] !== $0[safe: 1])
}
}
#endif
func testMaskedList() {
XCTAssertViewIntrospection(of: PlatformList.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
HStack {
List {
Text("Item 1")
}
#if os(iOS) || os(tvOS)
.introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) }
.introspect(.list, on: .iOS(.v16, .v17)) { spy0($0) }
#elseif os(macOS)
.introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy0($0) }
#endif
.clipped()
.clipShape(RoundedRectangle(cornerRadius: 20.0))
.cornerRadius(2.0)
List {
Text("Item 1")
#if os(iOS) || os(tvOS)
.introspect(.list, on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { spy1($0) }
.introspect(.list, on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) }
#elseif os(macOS)
.introspect(.list, on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) }
#endif
}
}
} extraAssertions: {
XCTAssert($0[safe: 0] !== $0[safe: 1])
}
}
}

View File

@ -0,0 +1,43 @@
#if os(macOS)
import SwiftUI
import SwiftUIIntrospect
import XCTest
@available(macOS 12, *)
final class ListWithBorderedStyleTests: XCTestCase {
#if canImport(AppKit)
typealias PlatformListWithBorderedStyle = NSTableView
#endif
func testListWithBorderedStyle() throws {
guard #available(macOS 12, *) else {
throw XCTSkip()
}
XCTAssertViewIntrospection(of: PlatformListWithBorderedStyle.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
HStack {
List {
Text("Item 1")
}
.listStyle(.bordered)
#if os(macOS)
.introspect(.list(style: .bordered), on: .macOS(.v12, .v13, .v14)) { spy0($0) }
#endif
List {
Text("Item 1")
#if os(macOS)
.introspect(.list(style: .bordered), on: .macOS(.v12, .v13, .v14), scope: .ancestor) { spy1($0) }
#endif
}
.listStyle(.bordered)
}
} extraAssertions: {
XCTAssert($0[safe: 0] !== $0[safe: 1])
}
}
}
#endif

View File

@ -0,0 +1,40 @@
#if !os(macOS)
import SwiftUI
import SwiftUIIntrospect
import XCTest
final class ListWithGroupedStyleTests: XCTestCase {
#if canImport(UIKit)
typealias PlatformListWithGroupedStyle = UIScrollView // covers both UITableView and UICollectionView
#endif
func testListWithGroupedStyle() {
XCTAssertViewIntrospection(of: PlatformListWithGroupedStyle.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
HStack {
List {
Text("Item 1")
}
.listStyle(.grouped)
#if os(iOS) || os(tvOS)
.introspect(.list(style: .grouped), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) }
.introspect(.list(style: .grouped), on: .iOS(.v16, .v17)) { spy0($0) }
#endif
List {
Text("Item 1")
#if os(iOS) || os(tvOS)
.introspect(.list(style: .grouped), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { spy1($0) }
.introspect(.list(style: .grouped), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) }
#endif
}
.listStyle(.grouped)
}
} extraAssertions: {
XCTAssert($0[safe: 0] !== $0[safe: 1])
}
}
}
#endif

View File

@ -0,0 +1,45 @@
#if os(iOS)
import SwiftUI
import SwiftUIIntrospect
import XCTest
@available(iOS 14, *)
final class ListWithInsetGroupedStyleTests: XCTestCase {
#if canImport(UIKit)
typealias PlatformListWithInsetGroupedStyle = UIScrollView // covers both UITableView and UICollectionView
#endif
func testListWithInsetGroupedStyle() throws {
guard #available(iOS 14, *) else {
throw XCTSkip()
}
XCTAssertViewIntrospection(of: PlatformListWithInsetGroupedStyle.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
HStack {
List {
Text("Item 1")
}
.listStyle(.insetGrouped)
#if os(iOS)
.introspect(.list(style: .insetGrouped), on: .iOS(.v14, .v15)) { spy0($0) }
.introspect(.list(style: .insetGrouped), on: .iOS(.v16, .v17)) { spy0($0) }
#endif
List {
Text("Item 1")
#if os(iOS)
.introspect(.list(style: .insetGrouped), on: .iOS(.v14, .v15), scope: .ancestor) { spy1($0) }
.introspect(.list(style: .insetGrouped), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) }
#endif
}
.listStyle(.insetGrouped)
}
} extraAssertions: {
XCTAssert($0[safe: 0] !== $0[safe: 1])
}
}
}
#endif

View File

@ -0,0 +1,51 @@
#if !os(tvOS)
import SwiftUI
import SwiftUIIntrospect
import XCTest
@available(iOS 14, macOS 11, *)
final class ListWithInsetStyleTests: XCTestCase {
#if canImport(UIKit)
typealias PlatformListWithInsetStyle = UIScrollView // covers both UITableView and UICollectionView
#elseif canImport(AppKit)
typealias PlatformListWithInsetStyle = NSTableView
#endif
func testListWithInsetStyle() throws {
guard #available(iOS 14, macOS 11, *) else {
throw XCTSkip()
}
XCTAssertViewIntrospection(of: PlatformListWithInsetStyle.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
HStack {
List {
Text("Item 1")
}
.listStyle(.inset)
#if os(iOS)
.introspect(.list(style: .inset), on: .iOS(.v14, .v15)) { spy0($0) }
.introspect(.list(style: .inset), on: .iOS(.v16, .v17)) { spy0($0) }
#elseif os(macOS)
.introspect(.list(style: .inset), on: .macOS(.v11, .v12, .v13, .v14)) { spy0($0) }
#endif
List {
Text("Item 1")
#if os(iOS)
.introspect(.list(style: .inset), on: .iOS(.v14, .v15), scope: .ancestor) { spy1($0) }
.introspect(.list(style: .inset), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) }
#elseif os(macOS)
.introspect(.list(style: .inset), on: .macOS(.v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) }
#endif
}
.listStyle(.inset)
}
} extraAssertions: {
XCTAssert($0[safe: 0] !== $0[safe: 1])
}
}
}
#endif

View File

@ -0,0 +1,44 @@
import SwiftUI
import SwiftUIIntrospect
import XCTest
final class ListWithPlainStyleTests: XCTestCase {
#if canImport(UIKit)
typealias PlatformListWithPlainStyle = UIScrollView // covers both UITableView and UICollectionView
#elseif canImport(AppKit)
typealias PlatformListWithPlainStyle = NSTableView
#endif
func testListWithPlainStyle() {
XCTAssertViewIntrospection(of: PlatformListWithPlainStyle.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
HStack {
List {
Text("Item 1")
}
.listStyle(.plain)
#if os(iOS) || os(tvOS)
.introspect(.list(style: .plain), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17)) { spy0($0) }
.introspect(.list(style: .plain), on: .iOS(.v16, .v17)) { spy0($0) }
#elseif os(macOS)
.introspect(.list(style: .plain), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy0($0) }
#endif
List {
Text("Item 1")
#if os(iOS) || os(tvOS)
.introspect(.list(style: .plain), on: .iOS(.v13, .v14, .v15), .tvOS(.v13, .v14, .v15, .v16, .v17), scope: .ancestor) { spy1($0) }
.introspect(.list(style: .plain), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) }
#elseif os(macOS)
.introspect(.list(style: .plain), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) }
#endif
}
.listStyle(.plain)
}
} extraAssertions: {
XCTAssert($0[safe: 0] !== $0[safe: 1])
}
}
}

View File

@ -0,0 +1,51 @@
#if !os(tvOS)
import SwiftUI
import SwiftUIIntrospect
import XCTest
@available(iOS 14, macOS 10.15, *)
final class ListWithSidebarStyleTests: XCTestCase {
#if canImport(UIKit)
typealias PlatformListWithSidebarStyle = UIScrollView // covers both UITableView and UICollectionView
#elseif canImport(AppKit)
typealias PlatformListWithSidebarStyle = NSTableView
#endif
func testListWithSidebarStyle() throws {
guard #available(iOS 14, macOS 10.15, *) else {
throw XCTSkip()
}
XCTAssertViewIntrospection(of: PlatformListWithSidebarStyle.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
HStack {
List {
Text("Item 1")
}
.listStyle(.sidebar)
#if os(iOS)
.introspect(.list(style: .sidebar), on: .iOS(.v14, .v15)) { spy0($0) }
.introspect(.list(style: .sidebar), on: .iOS(.v16, .v17)) { spy0($0) }
#elseif os(macOS)
.introspect(.list(style: .sidebar), on: .macOS(.v10_15, .v11, .v12, .v13, .v14)) { spy0($0) }
#endif
List {
Text("Item 1")
#if os(iOS)
.introspect(.list(style: .sidebar), on: .iOS(.v14, .v15), scope: .ancestor) { spy1($0) }
.introspect(.list(style: .sidebar), on: .iOS(.v16, .v17), scope: .ancestor) { spy1($0) }
#elseif os(macOS)
.introspect(.list(style: .sidebar), on: .macOS(.v10_15, .v11, .v12, .v13, .v14), scope: .ancestor) { spy1($0) }
#endif
}
.listStyle(.sidebar)
}
} extraAssertions: {
XCTAssert($0[safe: 0] !== $0[safe: 1])
}
}
}
#endif

Some files were not shown because too many files have changed in this diff Show More