Compare commits

...

21 Commits

Author SHA1 Message Date
David Roman 84196bab1c
Bump to 0.6.1 (#255) 2023-06-10 21:22:41 +01:00
David Roman d7b1d71ee3
Refactor `.introspect` to use `ViewModifier` (#253) 2023-06-10 19:50:56 +01:00
David Roman 80356a6b96
[CI] Retry runtime download on timeout or error (#247) 2023-06-08 12:40:41 +01:00
David Roman a5a1d7c305
Optimize receiver lookup algorithm (#246) 2023-06-08 01:49:22 +01:00
David Roman c29b50a475
Bump to 0.6.0 (#244) 2023-06-06 14:19:22 +01:00
Kevin Bradley 01cf328bdb
Fix `UIColorWell` build error on tvOS 13 (#217)
Co-authored-by: Kevin Bradley <bradleyk@wondrium.com>
Co-authored-by: David Roman <2538074+davdroman@users.noreply.github.com>
2023-06-06 13:44:09 +01:00
David Roman 930432278c
iOS 17 / tvOS 17 / macOS 14 support (#243) 2023-06-06 12:00:42 +01:00
David Roman 67e2a59be1
Bump to 0.5.2 (#241) 2023-06-05 01:58:46 +01:00
David Roman 4b81342e51
Optimize ancestor controller selectors (#240) 2023-06-05 01:56:08 +01:00
David Roman 23b08e51fc
Selector overrides (#239) 2023-06-05 01:00:14 +01:00
David Roman 94ab0068fc
Bump to 0.5.1 (#238) 2023-06-03 22:04:22 +01:00
David Roman a6ced72772
Fix `SwiftUIIntrospect.podspec` (#237) 2023-06-03 22:02:44 +01:00
David Roman 95c4382161
Bump to 0.5.0 (#236) 2023-06-03 21:08:17 +01:00
David Roman 1627b93fed
Fix `searchField` introspection (#234) 2023-06-03 21:00:16 +01:00
David Roman f327069e9c
Custom selectors (#233) 2023-06-03 14:47:02 +01:00
David Roman 43a8b9cfe8
Unify `introspect` modifiers (#232) 2023-06-03 00:31:07 +01:00
David Roman a84dfbfb28
Add explicit SPI import in docs [skip ci] (#229) 2023-06-02 10:25:34 +01:00
David Roman f17535beda
Bump to 0.4.0 (#226) 2023-06-01 20:11:16 +01:00
David Roman 0955cfcec9
All-new implementation, API, and module (#207) 2023-06-01 19:40:17 +01:00
David Roman 5b3f3996c7
Bump to 0.3.1 (#221) 2023-05-12 19:10:15 +01:00
David Roman 05e13467a0
Fix wrong Swift version in podspec (#220) 2023-05-12 18:59:11 +01:00
125 changed files with 7428 additions and 278 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,38 @@ 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 xcodes
run: brew install xcodesorg/made/xcodes
- if: ${{ matrix.install }}
name: Install Required Runtime (${{ matrix.runtime }})
uses: nick-fields/retry@v2
with:
timeout_minutes: 12
max_attempts: 3
command: sudo xcodes runtimes install '${{ matrix.runtime }}'
- name: List Available Simulators
run: xcrun simctl list devices available
- 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,6 +3,52 @@ Changelog
## master
## [0.6.1]
- Improved: optimized receiver lookup algorithm (#246)
- Infrastructure: refactored `.introspect` to use `ViewModifier` (#253)
- Infrastructure: retry runtime download on timeout or error on CI (#247)
## [0.6.0]
### SwiftUIIntrospect
- Added: iOS 17 / tvOS 17 / macOS 14 compatibility (#243)
### Introspect
- Fixed: `UIColorWell` build error on tvOS 13 (#217)
## [0.5.2]
### SwiftUIIntrospect
- Added: selector overrides (#239)
- Changed: optimized ancestor controller selectors (#240)
## [0.5.1]
### SwiftUIIntrospect
- Fixed: SwiftUIIntrospect.podspec (#237)
## [0.5.0]
### SwiftUIIntrospect
- Added: support for custom selectors (#233)
- Changed: unified introspect modifiers into one (#232)
- Fixed: `searchField` introspection (#234)
- Documentation: added explicit SPI import (#229)
## [0.4.0]
- Added: all-new implementation, API, and module (SwiftUIIntrospect) (#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)

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

@ -187,11 +187,13 @@ extension View {
}
/// Finds a `UIColorWell` from a `SwiftUI.ColorPicker`
#if os(iOS)
@available(iOS 14, *)
@available(tvOS, unavailable)
public func introspectColorWell(customize: @escaping (UIColorWell) -> ()) -> some View {
introspect(selector: TargetViewSelector.siblingContaining, customize: customize)
}
#endif
}
#endif

View File

@ -367,6 +367,7 @@ private struct SegmentedControlTestView: View {
}
}
#if os(iOS)
@available(iOS 14.0, *)
@available(tvOS, unavailable)
private struct ColorWellTestView: View {
@ -380,6 +381,7 @@ private struct ColorWellTestView: View {
}
}
}
#endif
import MapKit
@available(iOS 14, tvOS 14, *)

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
======================

158
Sources/Introspect.swift Normal file
View File

@ -0,0 +1,158 @@
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 {
public func introspect<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity>(
_ viewType: SwiftUIViewType,
on platforms: (PlatformViewVersions<SwiftUIViewType, PlatformSpecificEntity>)...,
scope: IntrospectionScope? = nil,
customize: @escaping (PlatformSpecificEntity) -> Void
) -> some View {
self.modifier(IntrospectModifier(viewType, platforms: platforms, scope: scope, customize: customize))
}
}
struct IntrospectModifier<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity>: ViewModifier {
let id = IntrospectionViewID()
let scope: IntrospectionScope
let selector: IntrospectionSelector<PlatformSpecificEntity>?
let customize: (PlatformSpecificEntity) -> Void
init(
_ viewType: SwiftUIViewType,
platforms: [PlatformViewVersions<SwiftUIViewType, PlatformSpecificEntity>],
scope: IntrospectionScope?,
customize: @escaping (PlatformSpecificEntity) -> Void
) {
self.scope = scope ?? viewType.scope
if let platform = platforms.first(where: \.isCurrent) {
self.selector = platform.selector ?? .default
} else {
self.selector = nil
}
self.customize = customize
}
func body(content: Content) -> some View {
if let selector {
content
.background(
IntrospectionAnchorView(id: id)
.frame(width: 0, height: 0)
)
.overlay(
IntrospectionView(id: id, selector: { selector($0, scope) }, customize: customize)
.frame(width: 0, height: 0)
)
} else {
content
}
}
}
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
}
extension PlatformEntity {
@_spi(Internals)
public var ancestors: some Sequence<Base> {
sequence(first: self~, next: { $0.ancestor~ }).dropFirst()
}
@_spi(Internals)
public var allDescendants: some Sequence<Base> {
recursiveSequence([self~], children: { $0.descendants~ }).dropFirst()
}
func nearestCommonAncestor(with other: Base) -> Base? {
var nearestAncestor: Base? = self~
while let currentEntity = nearestAncestor, !other.isDescendant(of: currentEntity~) {
nearestAncestor = currentEntity.ancestor~
}
return nearestAncestor
}
func allDescendants(between bottomEntity: Base, and topEntity: Base) -> some Sequence<Base> {
self.allDescendants
.lazy
.drop(while: { $0 !== bottomEntity })
.prefix(while: { $0 !== topEntity })
}
func receiver<PlatformSpecificEntity: PlatformEntity>(
ofType type: PlatformSpecificEntity.Type
) -> PlatformSpecificEntity? {
let frontEntity = self
guard
let backEntity = frontEntity.introspectionAnchorEntity,
let commonAncestor = backEntity.nearestCommonAncestor(with: frontEntity~)
else {
return nil
}
return commonAncestor
.allDescendants(between: 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
}
}
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)
}
}

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,73 @@
@_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 in
controller.as(Entry.Base.self)?.receiver(ofType: Entry.self).flatMap(selector)
},
ancestorSelector: { controller in
controller.as(Entry.Base.self)?.ancestor(ofType: Entry.self).flatMap(selector)
}
)
}
private var receiverSelector: (IntrospectionPlatformViewController) -> Target?
private var ancestorSelector: (IntrospectionPlatformViewController) -> Target?
private init(
receiverSelector: @escaping (IntrospectionPlatformViewController) -> Target?,
ancestorSelector: @escaping (IntrospectionPlatformViewController) -> Target?
) {
self.receiverSelector = receiverSelector
self.ancestorSelector = ancestorSelector
}
@_spi(Internals)
public func withReceiverSelector(_ selector: @escaping (PlatformViewController) -> 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) -> Target? {
if
scope.contains(.receiver),
let target = receiverSelector(controller)
{
return target
}
if
scope.contains(.ancestor),
let target = ancestorSelector(controller)
{
return target
}
return nil
}
}
extension PlatformViewController {
func `as`<Base: PlatformEntity>(_ baseType: Base.Type) -> (any PlatformEntity)? {
if Base.self == PlatformView.self {
#if canImport(UIKit)
return viewIfLoaded
#elseif canImport(AppKit)
return isViewLoaded ? view : nil
#endif
} else if Base.self == PlatformViewController.self {
return self
}
return nil
}
}

View File

@ -0,0 +1,215 @@
import SwiftUI
typealias IntrospectionViewID = UUID
fileprivate enum IntrospectionStore {
static var shared: [IntrospectionViewID: Pair] = [:]
struct Pair {
weak var controller: IntrospectionPlatformViewController?
weak var anchor: IntrospectionAnchorPlatformViewController?
}
}
extension PlatformEntity {
var introspectionAnchorEntity: Base? {
if let introspectionController = self as? IntrospectionPlatformViewController {
return IntrospectionStore.shared[introspectionController.id]?.anchor~
}
if
let view = self as? PlatformView,
let introspectionController = view.introspectionController
{
return IntrospectionStore.shared[introspectionController.id]?.anchor?.view~
}
return nil
}
}
///
struct IntrospectionAnchorView: PlatformViewControllerRepresentable {
#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: IntrospectionViewID
init(id: IntrospectionViewID) {
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 {
init(id: IntrospectionViewID) {
super.init(nibName: nil, bundle: nil)
IntrospectionStore.shared[id, default: .init()].anchor = self
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
override func loadView() {
view = NSView()
}
#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 id: IntrospectionViewID
private let selector: (IntrospectionPlatformViewController) -> Target?
private let customize: (Target) -> Void
init(
id: IntrospectionViewID,
selector: @escaping (IntrospectionPlatformViewController) -> Target?,
customize: @escaping (Target) -> Void
) {
self._observed = .constant(())
self.id = id
self.selector = selector
self.customize = customize
}
func makeCoordinator() -> TargetCache {
TargetCache()
}
func makePlatformViewController(context: Context) -> IntrospectionPlatformViewController {
let controller = IntrospectionPlatformViewController(id: id) { 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 {
let id: IntrospectionViewID
var handler: (() -> Void)? = nil
fileprivate init(
id: IntrospectionViewID,
handler: ((IntrospectionPlatformViewController) -> Void)?
) {
self.id = id
super.init(nibName: nil, bundle: nil)
self.handler = { [weak self] in
guard let self = self else {
return
}
handler?(self)
}
IntrospectionStore.shared[id, default: .init()].controller = self
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#if canImport(UIKit)
override func viewDidLoad() {
super.viewDidLoad()
view.introspectionController = self
handler?()
}
override func didMove(toParent parent: UIViewController?) {
super.didMove(toParent: parent)
handler?()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
handler?()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
handler?()
}
#elseif canImport(AppKit)
override func loadView() {
view = NSView()
view.introspectionController = self
}
override func viewDidLoad() {
super.viewDidLoad()
handler?()
}
override func viewDidAppear() {
super.viewDidAppear()
handler?()
}
#endif
}
import ObjectiveC
extension PlatformView {
fileprivate var introspectionController: IntrospectionPlatformViewController? {
get {
let key = unsafeBitCast(Selector(#function), to: UnsafeRawPointer.self)
return objc_getAssociatedObject(self, key) as? IntrospectionPlatformViewController
}
set {
let key = unsafeBitCast(Selector(#function), to: UnsafeRawPointer.self)
objc_setAssociatedObject(self, key, newValue, .OBJC_ASSOCIATION_ASSIGN)
}
}
}

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

28
Sources/Utils.swift Normal file
View File

@ -0,0 +1,28 @@
postfix operator ~
postfix func ~ <LHS, T>(lhs: LHS) -> T {
lhs as! T
}
postfix func ~ <LHS, T>(lhs: LHS?) -> T? {
lhs as? T
}
func recursiveSequence<S: Sequence>(_ sequence: S, children: @escaping (S.Element) -> S) -> AnySequence<S.Element> {
AnySequence {
var mainIterator = sequence.makeIterator()
// Current iterator, or `nil` if all sequences are exhausted:
var iterator: AnyIterator<S.Element>?
return AnyIterator {
guard let iterator, let element = iterator.next() else {
if let element = mainIterator.next() {
iterator = recursiveSequence(children(element), children: children).makeIterator()
return element
}
return nil
}
return element
}
}
}

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

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

View File

@ -0,0 +1,72 @@
#if !LEGACY_MACOS_SDK
import SwiftUI
import SwiftUIIntrospect
import XCTest
@available(iOS 16, tvOS 16, macOS 13, *)
final class NavigationSplitViewTests: XCTestCase {
#if canImport(UIKit) && os(iOS)
typealias PlatformNavigationSplitView = UISplitViewController
#elseif canImport(UIKit) && os(tvOS)
typealias PlatformNavigationSplitView = UINavigationController
#elseif canImport(AppKit)
typealias PlatformNavigationSplitView = NSSplitView
#endif
func testNavigationSplitView() throws {
guard #available(iOS 16, tvOS 16, macOS 13, *) else {
throw XCTSkip()
}
XCTAssertViewIntrospection(of: PlatformNavigationSplitView.self) { spies in
let spy = spies[0]
NavigationSplitView {
ZStack {
Color.red
Text("Something")
}
} detail: {
ZStack {
Color.red
Text("Detail")
}
}
#if os(iOS)
.introspect(.navigationSplitView, on: .iOS(.v16, .v17), customize: spy)
#elseif os(tvOS)
.introspect(.navigationSplitView, on: .tvOS(.v16, .v17), customize: spy)
#elseif os(macOS)
.introspect(.navigationSplitView, on: .macOS(.v13, .v14), customize: spy)
#endif
}
}
func testNavigationSplitViewAsAncestor() throws {
guard #available(iOS 16, tvOS 16, macOS 13, *) else {
throw XCTSkip()
}
XCTAssertViewIntrospection(of: PlatformNavigationSplitView.self) { spies in
let spy = spies[0]
// NB: columnVisibility is explicitly set here for ancestor introspection to work, because initially on iPad the sidebar is hidden, so the introspection modifier isn't triggered until the user makes the sidebar appear. This is why ancestor introspection is discouraged for most situations and it's opt-in.
NavigationSplitView(columnVisibility: .constant(.all)) {
ZStack {
Color.red
Text("Sidebar")
#if os(iOS)
.introspect(.navigationSplitView, on: .iOS(.v16, .v17), scope: .ancestor, customize: spy)
#elseif os(tvOS)
.introspect(.navigationSplitView, on: .tvOS(.v16, .v17), scope: .ancestor, customize: spy)
#elseif os(macOS)
.introspect(.navigationSplitView, on: .macOS(.v13, .v14), scope: .ancestor, customize: spy)
#endif
}
} detail: {
Text("Detail")
}
}
}
}
#endif

View File

@ -0,0 +1,52 @@
#if !os(macOS) && !LEGACY_MACOS_SDK
import SwiftUI
import SwiftUIIntrospect
import XCTest
@available(iOS 16, tvOS 16, *)
final class NavigationStackTests: XCTestCase {
#if canImport(UIKit)
typealias PlatformNavigationStack = UINavigationController
#endif
func testNavigationStack() throws {
guard #available(iOS 16, tvOS 16, *) else {
throw XCTSkip()
}
XCTAssertViewIntrospection(of: PlatformNavigationStack.self) { spies in
let spy = spies[0]
NavigationStack {
ZStack {
Color.red
Text("Something")
}
}
#if os(iOS) || os(tvOS)
.introspect(.navigationStack, on: .iOS(.v16, .v17), .tvOS(.v16, .v17), customize: spy)
#endif
}
}
func testNavigationStackAsAncestor() throws {
guard #available(iOS 16, tvOS 16, *) else {
throw XCTSkip()
}
XCTAssertViewIntrospection(of: PlatformNavigationStack.self) { spies in
let spy = spies[0]
NavigationStack {
ZStack {
Color.red
Text("Something")
#if os(iOS) || os(tvOS)
.introspect(.navigationStack, on: .iOS(.v16, .v17), .tvOS(.v16, .v17), scope: .ancestor, customize: spy)
#endif
}
}
}
}
}
#endif

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