macOS and tvOS support (#12)

This commit is contained in:
Loïs Di Qual 2020-02-12 13:08:17 -08:00 committed by GitHub
parent b26f5cd067
commit 5d78ad7d17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1475 additions and 545 deletions

View File

@ -1,6 +1,6 @@
# Shared Xcode version between jobs
xcode-version: &xcode-version "11.2.1"
xcode-version: &xcode-version "11.3.1"
# This is the key that we use to store and restore our dependencies cache.
# It needs to be different anytime we add Pods, Gems, or npm packages.
@ -40,8 +40,20 @@ jobs:
name: Create reports directory
command: mkdir output
- run:
name: Test
command: bundle exec fastlane test
name: Test iOS/tvOS framework
command: bundle exec fastlane ios test
- run:
name: Test macOS framework
command: bundle exec fastlane mac test
- run:
name: Lint podspec
command: bundle exec pod lib lint
- run:
name: Build swift package
command: swift build
- run:
name: Test swift package
command: swift test
- store_test_results:
path: output

View File

@ -3,6 +3,8 @@ Changelog
## master
- Added `macOS` and `tvOS` support.
## [0.0.6]
- Added `.introspectSegmentedControl()`.

View File

@ -2,14 +2,18 @@ Pod::Spec.new do |spec|
spec.name = 'Introspect'
spec.version = '0.0.6'
spec.license = { type: 'MIT' }
spec.homepage = 'https://github.com/timbersoftware/SwiftUI-Introspect.git'
spec.authors = { 'Lois Di Qual' => 'lois@timber.so' }
spec.homepage = 'https://github.com/siteline/SwiftUI-Introspect.git'
spec.authors = { 'Lois Di Qual' => 'lois@siteline.com' }
spec.summary = 'Introspect the underlying UIKit element of a SwiftUI view.'
spec.source = {
git: 'https://github.com/timbersoftware/SwiftUI-Introspect.git',
git: 'https://github.com/siteline/SwiftUI-Introspect.git',
tag: spec.version
}
spec.source_files = 'Introspect/**/*.swift'
spec.platform = :ios, '13.0'
spec.source_files = 'Introspect/*.swift'
spec.swift_version = '5.1'
spec.ios.deployment_target = '13.0'
spec.tvos.deployment_target = '13.0'
spec.osx.deployment_target = '10.15'
end

View File

@ -10,15 +10,34 @@
C068701C238DE85D00DAFD3D /* Introspect.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0687012238DE85D00DAFD3D /* Introspect.framework */; };
C0687023238DE85D00DAFD3D /* Introspect.h in Headers */ = {isa = PBXBuildFile; fileRef = C0687015238DE85D00DAFD3D /* Introspect.h */; settings = {ATTRIBUTES = (Public, ); }; };
C068702D238DE8FD00DAFD3D /* Introspect.swift in Sources */ = {isa = PBXBuildFile; fileRef = C068702C238DE8FD00DAFD3D /* Introspect.swift */; };
C068702F238DF01200DAFD3D /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C068702E238DF01200DAFD3D /* TestUtils.swift */; };
C0687031238DF3C900DAFD3D /* IntrospectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0687030238DF3C900DAFD3D /* IntrospectTests.swift */; };
C0796E2723F3CD18002BF033 /* Introspect.swift in Sources */ = {isa = PBXBuildFile; fileRef = C068702C238DE8FD00DAFD3D /* Introspect.swift */; };
C0796E2823F3CD1D002BF033 /* UIKitIntrospectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341423F3A13C005FA859 /* UIKitIntrospectionView.swift */; };
C0796E2923F3CD1D002BF033 /* UIKitIntrospectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341623F3A149005FA859 /* UIKitIntrospectionViewController.swift */; };
C0796E3423F3CDA4002BF033 /* Introspect.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0796E1F23F3CCD2002BF033 /* Introspect.framework */; };
C0C6D68E238E006B00DA6285 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0C6D68D238E006B00DA6285 /* AppDelegate.swift */; };
C0C6D690238E006B00DA6285 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0C6D68F238E006B00DA6285 /* SceneDelegate.swift */; };
C0C6D692238E006B00DA6285 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0C6D691238E006B00DA6285 /* ContentView.swift */; };
C0C6D694238E007100DA6285 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C0C6D693238E007100DA6285 /* Assets.xcassets */; };
C0C6D697238E007100DA6285 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C0C6D696238E007100DA6285 /* Preview Assets.xcassets */; };
C0C6D69A238E007100DA6285 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C0C6D698238E007100DA6285 /* LaunchScreen.storyboard */; };
C0C6D6A0238E00D300DA6285 /* Introspect.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0687012238DE85D00DAFD3D /* Introspect.framework */; };
C0ED341123F39EA2005FA859 /* Introspect.swift in Sources */ = {isa = PBXBuildFile; fileRef = C068702C238DE8FD00DAFD3D /* Introspect.swift */; };
C0ED341523F3A13C005FA859 /* UIKitIntrospectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341423F3A13C005FA859 /* UIKitIntrospectionView.swift */; };
C0ED341723F3A149005FA859 /* UIKitIntrospectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341623F3A149005FA859 /* UIKitIntrospectionViewController.swift */; };
C0ED341B23F3A258005FA859 /* AppKitIntrospectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341A23F3A258005FA859 /* AppKitIntrospectionView.swift */; };
C0ED341D23F3A58B005FA859 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341C23F3A58B005FA859 /* ViewExtensions.swift */; };
C0ED343A23F3AC43005FA859 /* Introspect.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0ED340923F39E93005FA859 /* Introspect.framework */; };
C0ED344023F3AC7F005FA859 /* AppKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED342223F3AC14005FA859 /* AppKitTests.swift */; };
C0ED345D23F48534005FA859 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341C23F3A58B005FA859 /* ViewExtensions.swift */; };
C0ED345E23F48535005FA859 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341C23F3A58B005FA859 /* ViewExtensions.swift */; };
C0ED345F23F48671005FA859 /* AppKitIntrospectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341A23F3A258005FA859 /* AppKitIntrospectionView.swift */; };
C0ED346023F48672005FA859 /* AppKitIntrospectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341A23F3A258005FA859 /* AppKitIntrospectionView.swift */; };
C0ED346123F486D0005FA859 /* UIKitIntrospectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341423F3A13C005FA859 /* UIKitIntrospectionView.swift */; };
C0ED346223F48770005FA859 /* UIKitIntrospectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED341623F3A149005FA859 /* UIKitIntrospectionViewController.swift */; };
C0ED346323F48776005FA859 /* Introspect.h in Headers */ = {isa = PBXBuildFile; fileRef = C0687015238DE85D00DAFD3D /* Introspect.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0ED346423F48776005FA859 /* Introspect.h in Headers */ = {isa = PBXBuildFile; fileRef = C0687015238DE85D00DAFD3D /* Introspect.h */; settings = {ATTRIBUTES = (Public, ); }; };
C0ED346623F48992005FA859 /* UIKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0687030238DF3C900DAFD3D /* UIKitTests.swift */; };
C0ED346723F489D5005FA859 /* AppKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED342223F3AC14005FA859 /* AppKitTests.swift */; };
C0ED346823F489D5005FA859 /* UIKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0687030238DF3C900DAFD3D /* UIKitTests.swift */; };
C0ED346923F489D6005FA859 /* AppKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0ED342223F3AC14005FA859 /* AppKitTests.swift */; };
C0ED346A23F489D6005FA859 /* UIKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0687030238DF3C900DAFD3D /* UIKitTests.swift */; };
E0B11E0609FFA04A7B6A1418 /* Pods_IntrospectExamples.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5785749C354BCF848BC4EAD9 /* Pods_IntrospectExamples.framework */; };
/* End PBXBuildFile section */
@ -30,6 +49,20 @@
remoteGlobalIDString = C0687011238DE85D00DAFD3D;
remoteInfo = Introspect;
};
C0796E3523F3CDA4002BF033 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = C0687009238DE85D00DAFD3D /* Project object */;
proxyType = 1;
remoteGlobalIDString = C0796E1E23F3CCD2002BF033;
remoteInfo = "Introspect tvOS";
};
C0ED343B23F3AC43005FA859 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = C0687009238DE85D00DAFD3D /* Project object */;
proxyType = 1;
remoteGlobalIDString = C0ED340823F39E93005FA859;
remoteInfo = "Introspect macOS";
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
@ -39,19 +72,24 @@
C0687012238DE85D00DAFD3D /* Introspect.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Introspect.framework; sourceTree = BUILT_PRODUCTS_DIR; };
C0687015238DE85D00DAFD3D /* Introspect.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Introspect.h; sourceTree = "<group>"; };
C0687016238DE85D00DAFD3D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C068701B238DE85D00DAFD3D /* IntrospectTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = IntrospectTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
C068701B238DE85D00DAFD3D /* Introspect iOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Introspect iOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
C0687022238DE85D00DAFD3D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C068702C238DE8FD00DAFD3D /* Introspect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Introspect.swift; sourceTree = "<group>"; };
C068702E238DF01200DAFD3D /* TestUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestUtils.swift; sourceTree = "<group>"; };
C0687030238DF3C900DAFD3D /* IntrospectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntrospectTests.swift; sourceTree = "<group>"; };
C0687030238DF3C900DAFD3D /* UIKitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitTests.swift; sourceTree = "<group>"; };
C0796E1F23F3CCD2002BF033 /* Introspect.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Introspect.framework; sourceTree = BUILT_PRODUCTS_DIR; };
C0796E2F23F3CDA4002BF033 /* Introspect tvOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Introspect tvOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
C0C6D68B238E006B00DA6285 /* IntrospectExamples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IntrospectExamples.app; sourceTree = BUILT_PRODUCTS_DIR; };
C0C6D68D238E006B00DA6285 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
C0C6D68F238E006B00DA6285 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
C0C6D691238E006B00DA6285 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
C0C6D693238E007100DA6285 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
C0C6D696238E007100DA6285 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
C0C6D699238E007100DA6285 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
C0C6D69B238E007100DA6285 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C0ED340923F39E93005FA859 /* Introspect.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Introspect.framework; sourceTree = BUILT_PRODUCTS_DIR; };
C0ED341423F3A13C005FA859 /* UIKitIntrospectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitIntrospectionView.swift; sourceTree = "<group>"; };
C0ED341623F3A149005FA859 /* UIKitIntrospectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitIntrospectionViewController.swift; sourceTree = "<group>"; };
C0ED341A23F3A258005FA859 /* AppKitIntrospectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppKitIntrospectionView.swift; sourceTree = "<group>"; };
C0ED341C23F3A58B005FA859 /* ViewExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewExtensions.swift; sourceTree = "<group>"; };
C0ED342223F3AC14005FA859 /* AppKitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppKitTests.swift; sourceTree = "<group>"; };
C0ED343523F3AC43005FA859 /* Introspect macOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Introspect macOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -70,6 +108,21 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
C0796E1C23F3CCD2002BF033 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
C0796E2C23F3CDA4002BF033 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
C0796E3423F3CDA4002BF033 /* Introspect.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
C0C6D688238E006B00DA6285 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@ -79,6 +132,21 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
C0ED340623F39E93005FA859 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
C0ED343223F3AC43005FA859 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
C0ED343A23F3AC43005FA859 /* Introspect.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@ -107,8 +175,12 @@
isa = PBXGroup;
children = (
C0687012238DE85D00DAFD3D /* Introspect.framework */,
C068701B238DE85D00DAFD3D /* IntrospectTests.xctest */,
C068701B238DE85D00DAFD3D /* Introspect iOS Tests.xctest */,
C0C6D68B238E006B00DA6285 /* IntrospectExamples.app */,
C0ED340923F39E93005FA859 /* Introspect.framework */,
C0ED343523F3AC43005FA859 /* Introspect macOS Tests.xctest */,
C0796E1F23F3CCD2002BF033 /* Introspect.framework */,
C0796E2F23F3CDA4002BF033 /* Introspect tvOS Tests.xctest */,
);
name = Products;
sourceTree = "<group>";
@ -116,9 +188,12 @@
C0687014238DE85D00DAFD3D /* Introspect */ = {
isa = PBXGroup;
children = (
C0687015238DE85D00DAFD3D /* Introspect.h */,
C0687016238DE85D00DAFD3D /* Info.plist */,
C0ED341A23F3A258005FA859 /* AppKitIntrospectionView.swift */,
C068702C238DE8FD00DAFD3D /* Introspect.swift */,
C0ED341623F3A149005FA859 /* UIKitIntrospectionViewController.swift */,
C0ED341423F3A13C005FA859 /* UIKitIntrospectionView.swift */,
C0ED341C23F3A58B005FA859 /* ViewExtensions.swift */,
C0ED346523F4880B005FA859 /* Supporting Files */,
);
path = Introspect;
sourceTree = "<group>";
@ -126,9 +201,9 @@
C068701F238DE85D00DAFD3D /* IntrospectTests */ = {
isa = PBXGroup;
children = (
C0ED342223F3AC14005FA859 /* AppKitTests.swift */,
C0687030238DF3C900DAFD3D /* UIKitTests.swift */,
C0687022238DE85D00DAFD3D /* Info.plist */,
C0687030238DF3C900DAFD3D /* IntrospectTests.swift */,
C068702E238DF01200DAFD3D /* TestUtils.swift */,
);
path = IntrospectTests;
sourceTree = "<group>";
@ -139,22 +214,11 @@
C0C6D68D238E006B00DA6285 /* AppDelegate.swift */,
C0C6D68F238E006B00DA6285 /* SceneDelegate.swift */,
C0C6D691238E006B00DA6285 /* ContentView.swift */,
C0C6D693238E007100DA6285 /* Assets.xcassets */,
C0C6D698238E007100DA6285 /* LaunchScreen.storyboard */,
C0C6D69B238E007100DA6285 /* Info.plist */,
C0C6D695238E007100DA6285 /* Preview Content */,
);
path = IntrospectExamples;
sourceTree = "<group>";
};
C0C6D695238E007100DA6285 /* Preview Content */ = {
isa = PBXGroup;
children = (
C0C6D696238E007100DA6285 /* Preview Assets.xcassets */,
);
path = "Preview Content";
sourceTree = "<group>";
};
C0C6D69F238E00D300DA6285 /* Frameworks */ = {
isa = PBXGroup;
children = (
@ -163,6 +227,15 @@
name = Frameworks;
sourceTree = "<group>";
};
C0ED346523F4880B005FA859 /* Supporting Files */ = {
isa = PBXGroup;
children = (
C0687016238DE85D00DAFD3D /* Info.plist */,
C0687015238DE85D00DAFD3D /* Introspect.h */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@ -174,12 +247,28 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
C0796E1A23F3CCD2002BF033 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
C0ED346423F48776005FA859 /* Introspect.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
C0ED340423F39E93005FA859 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
C0ED346323F48776005FA859 /* Introspect.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
C0687011238DE85D00DAFD3D /* Introspect */ = {
C0687011238DE85D00DAFD3D /* Introspect iOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = C0687026238DE85D00DAFD3D /* Build configuration list for PBXNativeTarget "Introspect" */;
buildConfigurationList = C0687026238DE85D00DAFD3D /* Build configuration list for PBXNativeTarget "Introspect iOS" */;
buildPhases = (
C068700D238DE85D00DAFD3D /* Headers */,
C068700E238DE85D00DAFD3D /* Sources */,
@ -190,14 +279,14 @@
);
dependencies = (
);
name = Introspect;
name = "Introspect iOS";
productName = Introspect;
productReference = C0687012238DE85D00DAFD3D /* Introspect.framework */;
productType = "com.apple.product-type.framework";
};
C068701A238DE85D00DAFD3D /* IntrospectTests */ = {
C068701A238DE85D00DAFD3D /* Introspect iOS Tests */ = {
isa = PBXNativeTarget;
buildConfigurationList = C0687029238DE85D00DAFD3D /* Build configuration list for PBXNativeTarget "IntrospectTests" */;
buildConfigurationList = C0687029238DE85D00DAFD3D /* Build configuration list for PBXNativeTarget "Introspect iOS Tests" */;
buildPhases = (
C0687017238DE85D00DAFD3D /* Sources */,
C0687018238DE85D00DAFD3D /* Frameworks */,
@ -208,9 +297,45 @@
dependencies = (
C068701E238DE85D00DAFD3D /* PBXTargetDependency */,
);
name = IntrospectTests;
name = "Introspect iOS Tests";
productName = IntrospectTests;
productReference = C068701B238DE85D00DAFD3D /* IntrospectTests.xctest */;
productReference = C068701B238DE85D00DAFD3D /* Introspect iOS Tests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
C0796E1E23F3CCD2002BF033 /* Introspect tvOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = C0796E2423F3CCD2002BF033 /* Build configuration list for PBXNativeTarget "Introspect tvOS" */;
buildPhases = (
C0796E1A23F3CCD2002BF033 /* Headers */,
C0796E1B23F3CCD2002BF033 /* Sources */,
C0796E1C23F3CCD2002BF033 /* Frameworks */,
C0796E1D23F3CCD2002BF033 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "Introspect tvOS";
productName = "Introspect tvOS";
productReference = C0796E1F23F3CCD2002BF033 /* Introspect.framework */;
productType = "com.apple.product-type.framework";
};
C0796E2E23F3CDA4002BF033 /* Introspect tvOS Tests */ = {
isa = PBXNativeTarget;
buildConfigurationList = C0796E3723F3CDA4002BF033 /* Build configuration list for PBXNativeTarget "Introspect tvOS Tests" */;
buildPhases = (
C0796E2B23F3CDA4002BF033 /* Sources */,
C0796E2C23F3CDA4002BF033 /* Frameworks */,
C0796E2D23F3CDA4002BF033 /* Resources */,
);
buildRules = (
);
dependencies = (
C0796E3623F3CDA4002BF033 /* PBXTargetDependency */,
);
name = "Introspect tvOS Tests";
productName = "Introspect tvOS Tests";
productReference = C0796E2F23F3CDA4002BF033 /* Introspect tvOS Tests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
C0C6D68A238E006B00DA6285 /* IntrospectExamples */ = {
@ -232,13 +357,49 @@
productReference = C0C6D68B238E006B00DA6285 /* IntrospectExamples.app */;
productType = "com.apple.product-type.application";
};
C0ED340823F39E93005FA859 /* Introspect macOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = C0ED340E23F39E93005FA859 /* Build configuration list for PBXNativeTarget "Introspect macOS" */;
buildPhases = (
C0ED340423F39E93005FA859 /* Headers */,
C0ED340523F39E93005FA859 /* Sources */,
C0ED340623F39E93005FA859 /* Frameworks */,
C0ED340723F39E93005FA859 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "Introspect macOS";
productName = "Introspect macOS";
productReference = C0ED340923F39E93005FA859 /* Introspect.framework */;
productType = "com.apple.product-type.framework";
};
C0ED343423F3AC43005FA859 /* Introspect macOS Tests */ = {
isa = PBXNativeTarget;
buildConfigurationList = C0ED343D23F3AC43005FA859 /* Build configuration list for PBXNativeTarget "Introspect macOS Tests" */;
buildPhases = (
C0ED343123F3AC43005FA859 /* Sources */,
C0ED343223F3AC43005FA859 /* Frameworks */,
C0ED343323F3AC43005FA859 /* Resources */,
);
buildRules = (
);
dependencies = (
C0ED343C23F3AC43005FA859 /* PBXTargetDependency */,
);
name = "Introspect macOS Tests";
productName = "Introspect macOS Tests";
productReference = C0ED343523F3AC43005FA859 /* Introspect macOS Tests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
C0687009238DE85D00DAFD3D /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1120;
LastSwiftUpdateCheck = 1130;
LastUpgradeCheck = 1120;
ORGANIZATIONNAME = "Lois Di Qual";
TargetAttributes = {
@ -249,9 +410,21 @@
C068701A238DE85D00DAFD3D = {
CreatedOnToolsVersion = 11.2.1;
};
C0796E1E23F3CCD2002BF033 = {
CreatedOnToolsVersion = 11.3.1;
};
C0796E2E23F3CDA4002BF033 = {
CreatedOnToolsVersion = 11.3.1;
};
C0C6D68A238E006B00DA6285 = {
CreatedOnToolsVersion = 11.2.1;
};
C0ED340823F39E93005FA859 = {
CreatedOnToolsVersion = 11.3.1;
};
C0ED343423F3AC43005FA859 = {
CreatedOnToolsVersion = 11.3.1;
};
};
};
buildConfigurationList = C068700C238DE85D00DAFD3D /* Build configuration list for PBXProject "Introspect" */;
@ -267,8 +440,12 @@
projectDirPath = "";
projectRoot = "";
targets = (
C0687011238DE85D00DAFD3D /* Introspect */,
C068701A238DE85D00DAFD3D /* IntrospectTests */,
C0687011238DE85D00DAFD3D /* Introspect iOS */,
C068701A238DE85D00DAFD3D /* Introspect iOS Tests */,
C0ED340823F39E93005FA859 /* Introspect macOS */,
C0ED343423F3AC43005FA859 /* Introspect macOS Tests */,
C0796E1E23F3CCD2002BF033 /* Introspect tvOS */,
C0796E2E23F3CDA4002BF033 /* Introspect tvOS Tests */,
C0C6D68A238E006B00DA6285 /* IntrospectExamples */,
);
};
@ -289,13 +466,38 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
C0796E1D23F3CCD2002BF033 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
C0796E2D23F3CDA4002BF033 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
C0C6D689238E006B00DA6285 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C0C6D69A238E007100DA6285 /* LaunchScreen.storyboard in Resources */,
C0C6D697238E007100DA6285 /* Preview Assets.xcassets in Resources */,
C0C6D694238E007100DA6285 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
C0ED340723F39E93005FA859 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
C0ED343323F3AC43005FA859 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -348,6 +550,10 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C0ED341723F3A149005FA859 /* UIKitIntrospectionViewController.swift in Sources */,
C0ED345F23F48671005FA859 /* AppKitIntrospectionView.swift in Sources */,
C0ED341523F3A13C005FA859 /* UIKitIntrospectionView.swift in Sources */,
C0ED345E23F48535005FA859 /* ViewExtensions.swift in Sources */,
C068702D238DE8FD00DAFD3D /* Introspect.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -356,8 +562,29 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C0687031238DF3C900DAFD3D /* IntrospectTests.swift in Sources */,
C068702F238DF01200DAFD3D /* TestUtils.swift in Sources */,
C0ED346823F489D5005FA859 /* UIKitTests.swift in Sources */,
C0ED346723F489D5005FA859 /* AppKitTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
C0796E1B23F3CCD2002BF033 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C0796E2823F3CD1D002BF033 /* UIKitIntrospectionView.swift in Sources */,
C0ED346023F48672005FA859 /* AppKitIntrospectionView.swift in Sources */,
C0796E2923F3CD1D002BF033 /* UIKitIntrospectionViewController.swift in Sources */,
C0ED345D23F48534005FA859 /* ViewExtensions.swift in Sources */,
C0796E2723F3CD18002BF033 /* Introspect.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
C0796E2B23F3CDA4002BF033 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C0ED346A23F489D6005FA859 /* UIKitTests.swift in Sources */,
C0ED346923F489D6005FA859 /* AppKitTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -371,26 +598,46 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
C0ED340523F39E93005FA859 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C0ED341B23F3A258005FA859 /* AppKitIntrospectionView.swift in Sources */,
C0ED341D23F3A58B005FA859 /* ViewExtensions.swift in Sources */,
C0ED346123F486D0005FA859 /* UIKitIntrospectionView.swift in Sources */,
C0ED341123F39EA2005FA859 /* Introspect.swift in Sources */,
C0ED346223F48770005FA859 /* UIKitIntrospectionViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
C0ED343123F3AC43005FA859 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C0ED346623F48992005FA859 /* UIKitTests.swift in Sources */,
C0ED344023F3AC7F005FA859 /* AppKitTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
C068701E238DE85D00DAFD3D /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = C0687011238DE85D00DAFD3D /* Introspect */;
target = C0687011238DE85D00DAFD3D /* Introspect iOS */;
targetProxy = C068701D238DE85D00DAFD3D /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
C0C6D698238E007100DA6285 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
C0C6D699238E007100DA6285 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
C0796E3623F3CDA4002BF033 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = C0796E1E23F3CCD2002BF033 /* Introspect tvOS */;
targetProxy = C0796E3523F3CDA4002BF033 /* PBXContainerItemProxy */;
};
/* End PBXVariantGroup section */
C0ED343C23F3AC43005FA859 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = C0ED340823F39E93005FA859 /* Introspect macOS */;
targetProxy = C0ED343B23F3AC43005FA859 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
C0687024238DE85D00DAFD3D /* Debug */ = {
@ -445,12 +692,14 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.2;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TVOS_DEPLOYMENT_TARGET = 13.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
@ -502,11 +751,13 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.2;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TVOS_DEPLOYMENT_TARGET = 13.0;
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
@ -517,9 +768,9 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
CODE_SIGN_STYLE = Manual;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5CYVM7UEHW;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
@ -530,9 +781,11 @@
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.loisdiqual.Introspect;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Introspect;
PRODUCT_NAME = Introspect;
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
SUPPORTS_MACCATALYST = NO;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -543,9 +796,9 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
CODE_SIGN_STYLE = Manual;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5CYVM7UEHW;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
@ -556,9 +809,11 @@
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.loisdiqual.Introspect;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Introspect;
PRODUCT_NAME = Introspect;
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
SUPPORTS_MACCATALYST = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@ -602,20 +857,108 @@
};
name = Release;
};
C0796E2523F3CCD2002BF033 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Introspect/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Introspect;
PRODUCT_NAME = Introspect;
SDKROOT = appletvos;
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 13.2;
};
name = Debug;
};
C0796E2623F3CCD2002BF033 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Introspect/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Introspect;
PRODUCT_NAME = Introspect;
SDKROOT = appletvos;
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 13.2;
};
name = Release;
};
C0796E3823F3CDA4002BF033 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = IntrospectTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.siteline.Introspect-tvOS-Tests";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 13.2;
};
name = Debug;
};
C0796E3923F3CDA4002BF033 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = IntrospectTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.siteline.Introspect-tvOS-Tests";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 13.2;
};
name = Release;
};
C0C6D69C238E007100DA6285 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9BBE78DB32CCDC560004DB54 /* Pods-IntrospectExamples.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"IntrospectExamples/Preview Content\"";
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = IntrospectExamples/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.loisdiqual.IntrospectExamples;
MARKETING_VERSION = 0.0.6;
PRODUCT_BUNDLE_IDENTIFIER = com.siteline.IntrospectExamples;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -628,20 +971,116 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"IntrospectExamples/Preview Content\"";
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = IntrospectExamples/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.loisdiqual.IntrospectExamples;
MARKETING_VERSION = 0.0.6;
PRODUCT_BUNDLE_IDENTIFIER = com.siteline.IntrospectExamples;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
C0ED340F23F39E93005FA859 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Introspect/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Introspect;
PRODUCT_NAME = Introspect;
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
C0ED341023F39E93005FA859 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Introspect/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
PRODUCT_BUNDLE_IDENTIFIER = com.siteline.Introspect;
PRODUCT_NAME = Introspect;
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
};
name = Release;
};
C0ED343E23F3AC43005FA859 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = IntrospectTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@loader_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
PRODUCT_BUNDLE_IDENTIFIER = "com.siteline.Introspect-macOS-Tests";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
C0ED343F23F3AC43005FA859 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = IntrospectTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
"@loader_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
PRODUCT_BUNDLE_IDENTIFIER = "com.siteline.Introspect-macOS-Tests";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx;
SWIFT_VERSION = 5.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@ -654,7 +1093,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C0687026238DE85D00DAFD3D /* Build configuration list for PBXNativeTarget "Introspect" */ = {
C0687026238DE85D00DAFD3D /* Build configuration list for PBXNativeTarget "Introspect iOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C0687027238DE85D00DAFD3D /* Debug */,
@ -663,7 +1102,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C0687029238DE85D00DAFD3D /* Build configuration list for PBXNativeTarget "IntrospectTests" */ = {
C0687029238DE85D00DAFD3D /* Build configuration list for PBXNativeTarget "Introspect iOS Tests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C068702A238DE85D00DAFD3D /* Debug */,
@ -672,6 +1111,24 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C0796E2423F3CCD2002BF033 /* Build configuration list for PBXNativeTarget "Introspect tvOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C0796E2523F3CCD2002BF033 /* Debug */,
C0796E2623F3CCD2002BF033 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C0796E3723F3CDA4002BF033 /* Build configuration list for PBXNativeTarget "Introspect tvOS Tests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C0796E3823F3CDA4002BF033 /* Debug */,
C0796E3923F3CDA4002BF033 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C0C6D69E238E007100DA6285 /* Build configuration list for PBXNativeTarget "IntrospectExamples" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@ -681,6 +1138,24 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C0ED340E23F39E93005FA859 /* Build configuration list for PBXNativeTarget "Introspect macOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C0ED340F23F39E93005FA859 /* Debug */,
C0ED341023F39E93005FA859 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C0ED343D23F3AC43005FA859 /* Build configuration list for PBXNativeTarget "Introspect macOS Tests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C0ED343E23F3AC43005FA859 /* Debug */,
C0ED343F23F3AC43005FA859 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = C0687009238DE85D00DAFD3D /* Project object */;

View File

@ -16,7 +16,7 @@
BuildableIdentifier = "primary"
BlueprintIdentifier = "C0687011238DE85D00DAFD3D"
BuildableName = "Introspect.framework"
BlueprintName = "Introspect"
BlueprintName = "Introspect iOS"
ReferencedContainer = "container:Introspect.xcodeproj">
</BuildableReference>
</BuildActionEntry>
@ -33,8 +33,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C068701A238DE85D00DAFD3D"
BuildableName = "IntrospectTests.xctest"
BlueprintName = "IntrospectTests"
BuildableName = "Introspect iOS Tests.xctest"
BlueprintName = "Introspect iOS Tests"
ReferencedContainer = "container:Introspect.xcodeproj">
</BuildableReference>
</TestableReference>
@ -62,7 +62,7 @@
BuildableIdentifier = "primary"
BlueprintIdentifier = "C0687011238DE85D00DAFD3D"
BuildableName = "Introspect.framework"
BlueprintName = "Introspect"
BlueprintName = "Introspect iOS"
ReferencedContainer = "container:Introspect.xcodeproj">
</BuildableReference>
</MacroExpansion>

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1130"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C0ED340823F39E93005FA859"
BuildableName = "Introspect.framework"
BlueprintName = "Introspect macOS"
ReferencedContainer = "container:Introspect.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 = "C0ED343423F3AC43005FA859"
BuildableName = "Introspect macOS Tests.xctest"
BlueprintName = "Introspect macOS Tests"
ReferencedContainer = "container:Introspect.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">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C0ED340823F39E93005FA859"
BuildableName = "Introspect.framework"
BlueprintName = "Introspect macOS"
ReferencedContainer = "container:Introspect.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1130"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C0796E1E23F3CCD2002BF033"
BuildableName = "Introspect.framework"
BlueprintName = "Introspect tvOS"
ReferencedContainer = "container:Introspect.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 = "C0796E2E23F3CDA4002BF033"
BuildableName = "Introspect tvOS Tests.xctest"
BlueprintName = "Introspect tvOS Tests"
ReferencedContainer = "container:Introspect.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">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C0796E1E23F3CCD2002BF033"
BuildableName = "Introspect.framework"
BlueprintName = "Introspect tvOS"
ReferencedContainer = "container:Introspect.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -4,16 +4,26 @@
<dict>
<key>SchemeUserState</key>
<dict>
<key>Introspect.xcscheme_^#shared#^_</key>
<key>Introspect iOS.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
<key>IntrospectExamples.xcscheme_^#shared#^_</key>
<key>Introspect macOS.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>3</integer>
</dict>
<key>Introspect tvOS.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>4</integer>
</dict>
<key>IntrospectExamples.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>5</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
@ -27,6 +37,26 @@
<key>primary</key>
<true/>
</dict>
<key>C0796E1E23F3CCD2002BF033</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>C0796E2E23F3CDA4002BF033</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>C0ED340823F39E93005FA859</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>C0ED343423F3AC43005FA859</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@ -0,0 +1,66 @@
#if canImport(AppKit)
import SwiftUI
import AppKit
/// Introspection NSView that is inserted alongside the target view.
public class IntrospectionNSView: NSView {
required init() {
super.init(frame: .zero)
isHidden = true
}
public override func hitTest(_ point: NSPoint) -> NSView? {
return nil
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
/// Introspection View that is injected into the UIKit hierarchy alongside the target view.
/// After `updateNSView` is called, it calls `selector` to find the target view, then `customize` when the target view is found.
public struct AppKitIntrospectionView<TargetViewType: NSView>: NSViewRepresentable {
/// Method that introspects the view hierarchy to find the target view.
/// First argument is the introspection view itself, which is contained in a view host alongside the target view.
let selector: (IntrospectionNSView) -> TargetViewType?
/// User-provided customization method for the target view.
let customize: (TargetViewType) -> Void
public init(
selector: @escaping (IntrospectionNSView) -> TargetViewType?,
customize: @escaping (TargetViewType) -> Void
) {
self.selector = selector
self.customize = customize
}
public func makeNSView(context: NSViewRepresentableContext<AppKitIntrospectionView>) -> IntrospectionNSView {
let view = IntrospectionNSView()
view.setAccessibilityLabel("IntrospectionNSView<\(TargetViewType.self)>")
return view
}
/// When `updateNSView` is called after creating the Introspection view, it is not yet in the AppKit hierarchy.
/// At this point, `introspectionView.superview.superview` is nil and we can't access the target AppKit view.
/// To workaround this, we wait until the runloop is done inserting the introspection view in the hierarchy, then run the selector.
/// Finding the target view fails silently if the selector yield no result. This happens when `updateNSView`
/// gets called when the introspection view gets removed from the hierarchy.
public func updateNSView(
_ nsView: IntrospectionNSView,
context: NSViewRepresentableContext<AppKitIntrospectionView>
) {
DispatchQueue.main.async {
guard let targetView = self.selector(nsView) else {
return
}
self.customize(targetView)
}
}
}
#endif

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>0.0.6</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>

View File

@ -1,14 +1,28 @@
import SwiftUI
#if os(macOS)
public typealias PlatformView = NSView
#endif
#if os(iOS) || os(tvOS)
public typealias PlatformView = UIView
#endif
#if os(macOS)
public typealias PlatformViewController = NSViewController
#endif
#if os(iOS) || os(tvOS)
public typealias PlatformViewController = UIViewController
#endif
/// Utility methods to inspect the UIKit view hierarchy.
public enum Introspect {
/// Finds a subview of the specified type.
/// This method will recursively look for this view.
/// Returns nil if it can't find a view of the specified type.
public static func findChild<AnyViewType: UIView>(
public static func findChild<AnyViewType: PlatformView>(
ofType type: AnyViewType.Type,
in root: UIView
in root: PlatformView
) -> AnyViewType? {
for subview in root.subviews {
if let typed = subview as? AnyViewType {
@ -23,9 +37,9 @@ public enum Introspect {
/// Finds a child view controller of the specified type.
/// This method will recursively look for this child.
/// Returns nil if it can't find a view of the specified type.
public static func findChild<AnyViewControllerType: UIViewController>(
public static func findChild<AnyViewControllerType: PlatformViewController>(
ofType type: AnyViewControllerType.Type,
in root: UIViewController
in root: PlatformViewController
) -> AnyViewControllerType? {
for child in root.children {
if let typed = child as? AnyViewControllerType {
@ -40,9 +54,9 @@ public enum Introspect {
/// Finds a previous sibling that contains a view of the specified type.
/// This method inspects siblings recursively.
/// Returns nil if no sibling contains the specified type.
public static func previousSibling<AnyViewType: UIView>(
public static func previousSibling<AnyViewType: PlatformView>(
containing type: AnyViewType.Type,
from entry: UIView
from entry: PlatformView
) -> AnyViewType? {
guard let superview = entry.superview,
@ -64,9 +78,10 @@ public enum Introspect {
/// Finds a previous sibling that contains a view controller of the specified type.
/// This method inspects siblings recursively.
/// Returns nil if no sibling contains the specified type.
public static func previousSibling<AnyViewControllerType: UIViewController>(
@available(macOS, unavailable)
public static func previousSibling<AnyViewControllerType: PlatformViewController>(
containing type: AnyViewControllerType.Type,
from entry: UIViewController
from entry: PlatformViewController
) -> AnyViewControllerType? {
guard let parent = entry.parent,
@ -88,9 +103,9 @@ public enum Introspect {
/// Finds a previous sibling that is a view controller of the specified type.
/// This method does not inspect siblings recursively.
/// Returns nil if no sibling is of the specified type.
public static func previousSibling<AnyViewControllerType: UIViewController>(
public static func previousSibling<AnyViewControllerType: PlatformViewController>(
ofType type: AnyViewControllerType.Type,
from entry: UIViewController
from entry: PlatformViewController
) -> AnyViewControllerType? {
guard let parent = entry.parent,
@ -112,9 +127,9 @@ public enum Introspect {
/// Finds a next sibling that contains a view of the specified type.
/// This method inspects siblings recursively.
/// Returns nil if no sibling contains the specified type.
public static func nextSibling<AnyViewType: UIView>(
public static func nextSibling<AnyViewType: PlatformView>(
containing type: AnyViewType.Type,
from entry: UIView
from entry: PlatformView
) -> AnyViewType? {
guard let superview = entry.superview,
@ -134,7 +149,7 @@ public enum Introspect {
/// Finds an ancestor of the specified type.
/// If it reaches the top of the view without finding the specified view type, it returns nil.
public static func findAncestor<AnyViewType: UIView>(ofType type: AnyViewType.Type, from entry: UIView) -> AnyViewType? {
public static func findAncestor<AnyViewType: PlatformView>(ofType type: AnyViewType.Type, from entry: PlatformView) -> AnyViewType? {
var superview = entry.superview
while let s = superview {
if let typed = s as? AnyViewType {
@ -149,10 +164,10 @@ public enum Introspect {
/// Hosting views generally contain subviews for one specific SwiftUI element.
/// For instance, if there are multiple text fields in a VStack, the hosting view will contain those text fields (and their host views, see below).
/// Returns nil if it couldn't find a hosting view. This should never happen when called with an IntrospectionView.
public static func findHostingView(from entry: UIView) -> UIView? {
public static func findHostingView(from entry: PlatformView) -> PlatformView? {
var superview = entry.superview
while let s = superview {
if NSStringFromClass(type(of: s)).contains("UIHostingView") {
if NSStringFromClass(type(of: s)).contains("HostingView") {
return s
}
superview = s.superview
@ -163,7 +178,7 @@ public enum Introspect {
/// Finds the view host of a specific view.
/// SwiftUI wraps each UIView within a ViewHost, then within a HostingView.
/// Returns nil if it couldn't find a view host. This should never happen when called with an IntrospectionView.
public static func findViewHost(from entry: UIView) -> UIView? {
public static func findViewHost(from entry: PlatformView) -> PlatformView? {
var superview = entry.superview
while let s = superview {
if NSStringFromClass(type(of: s)).contains("ViewHost") {
@ -175,6 +190,22 @@ public enum Introspect {
}
}
enum TargetViewSelector {
public static func sibling<TargetView: PlatformView>(from entry: PlatformView) -> TargetView? {
guard let viewHost = Introspect.findViewHost(from: entry) else {
return nil
}
return Introspect.previousSibling(containing: TargetView.self, from: viewHost)
}
public static func ancestorOrSibling<TargetView: PlatformView>(from entry: PlatformView) -> TargetView? {
if let tableView = Introspect.findAncestor(ofType: TargetView.self, from: entry) {
return tableView
}
return sibling(from: entry)
}
}
/// Allows to safely access an array element by index
/// Usage: array[safe: 2]
private extension Array {
@ -186,245 +217,3 @@ private extension Array {
return self[index]
}
}
/// Introspection UIView that is inserted alongside the target view.
public class IntrospectionUIView: UIView {
required init() {
super.init(frame: .zero)
isHidden = true
isUserInteractionEnabled = false
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
/// Introspection View that is injected into the UIKit hierarchy alongside the target view.
/// After `updateUIView` is called, it calls `selector` to find the target view, then `customize` when the target view is found.
public struct IntrospectionView<TargetViewType: UIView>: UIViewRepresentable {
/// Method that introspects the view hierarchy to find the target view.
/// First argument is the introspection view itself, which is contained in a view host alongside the target view.
let selector: (IntrospectionUIView) -> TargetViewType?
/// User-provided customization method for the target view.
let customize: (TargetViewType) -> Void
public init(
selector: @escaping (UIView) -> TargetViewType?,
customize: @escaping (TargetViewType) -> Void
) {
self.selector = selector
self.customize = customize
}
public func makeUIView(context: UIViewRepresentableContext<IntrospectionView>) -> IntrospectionUIView {
let view = IntrospectionUIView()
view.accessibilityLabel = "IntrospectionUIView<\(TargetViewType.self)>"
return view
}
/// When `updateUiView` is called after creating the Introspection view, it is not yet in the UIKit hierarchy.
/// At this point, `introspectionView.superview.superview` is nil and we can't access the target UIKit view.
/// To workaround this, we wait until the runloop is done inserting the introspection view in the hierarchy, then run the selector.
/// Finding the target view fails silently if the selector yield no result. This happens when `updateUIView`
/// gets called when the introspection view gets removed from the hierarchy.
public func updateUIView(
_ uiView: IntrospectionUIView,
context: UIViewRepresentableContext<IntrospectionView>
) {
DispatchQueue.main.asyncAfter(deadline: .now()) {
guard let targetView = self.selector(uiView) else {
return
}
self.customize(targetView)
}
}
}
/// Introspection UIViewController that is inserted alongside the target view controller.
public class IntrospectionUIViewController: UIViewController {
required init() {
super.init(nibName: nil, bundle: nil)
view = IntrospectionUIView()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
/// This is the same logic as IntrospectionView but for view controllers. Please see details above.
public struct IntrospectionViewController<TargetViewControllerType: UIViewController>: UIViewControllerRepresentable {
let selector: (IntrospectionUIViewController) -> TargetViewControllerType?
let customize: (TargetViewControllerType) -> Void
public init(
selector: @escaping (UIViewController) -> TargetViewControllerType?,
customize: @escaping (TargetViewControllerType) -> Void
) {
self.selector = selector
self.customize = customize
}
public func makeUIViewController(
context: UIViewControllerRepresentableContext<IntrospectionViewController>
) -> IntrospectionUIViewController {
let viewController = IntrospectionUIViewController()
viewController.accessibilityLabel = "IntrospectionUIViewController<\(TargetViewControllerType.self)>"
viewController.view.accessibilityLabel = "IntrospectionUIView<\(TargetViewControllerType.self)>"
return viewController
}
public func updateUIViewController(
_ uiViewController: IntrospectionUIViewController,
context: UIViewControllerRepresentableContext<IntrospectionViewController>
) {
DispatchQueue.main.asyncAfter(deadline: .now()) {
guard let targetView = self.selector(uiViewController) else {
return
}
self.customize(targetView)
}
}
}
extension View {
public func inject<SomeView>(_ view: SomeView) -> some View where SomeView: View {
return overlay(view.frame(width: 0, height: 0))
}
/// Finds a `UITableView` from a `SwiftUI.List`, or `SwiftUI.List` child.
public func introspectTableView(customize: @escaping (UITableView) -> ()) -> some View {
return inject(IntrospectionView(
selector: { introspectionView in
// Search in ancestors
if let tableView = Introspect.findAncestor(ofType: UITableView.self, from: introspectionView) {
return tableView
}
guard let viewHost = Introspect.findViewHost(from: introspectionView) else {
return nil
}
// Search in siblings
return Introspect.previousSibling(containing: UITableView.self, from: viewHost)
},
customize: customize
))
}
/// Finds a `UIScrollView` from a `SwiftUI.ScrollView`, or `SwiftUI.ScrollView` child.
public func introspectScrollView(customize: @escaping (UIScrollView) -> ()) -> some View {
return inject(IntrospectionView(
selector: { introspectionView in
// Search in ancestors
if let tableView = Introspect.findAncestor(ofType: UIScrollView.self, from: introspectionView) {
return tableView
}
guard let viewHost = Introspect.findViewHost(from: introspectionView) else {
return nil
}
// Search in siblings
return Introspect.previousSibling(containing: UIScrollView.self, from: viewHost)
},
customize: customize
))
}
/// Finds a `UINavigationController` from any view embedded in a `SwiftUI.NavigationView`.
public func introspectNavigationController(customize: @escaping (UINavigationController) -> ()) -> some View {
return inject(IntrospectionViewController(
selector: { introspectionViewController in
// Search in ancestors
if let navigationController = introspectionViewController.navigationController {
return navigationController
}
// Search in siblings
return Introspect.previousSibling(containing: UINavigationController.self, from: introspectionViewController)
},
customize: customize
))
}
/// Finds the containing `UIViewController` of a SwiftUI view.
public func introspectViewController(customize: @escaping (UIViewController) -> ()) -> some View {
return inject(IntrospectionViewController(
selector: { $0.parent },
customize: customize
))
}
/// Finds a `UITabBarController` from any SwiftUI view embedded in a `SwiftUI.TabView`
public func introspectTabBarController(customize: @escaping (UITabBarController) -> ()) -> some View {
return inject(IntrospectionViewController(
selector: { introspectionViewController in
// Search in ancestors
if let navigationController = introspectionViewController.tabBarController {
return navigationController
}
// Search in siblings
return Introspect.previousSibling(ofType: UITabBarController.self, from: introspectionViewController)
},
customize: customize
))
}
/// Finds a `TargetView` from a `SwiftUI.View`
public func introspect<TargetView: UIView>(customize: @escaping (TargetView) -> ()) -> some View {
return inject(IntrospectionView(
selector: { introspectionView in
guard let viewHost = Introspect.findViewHost(from: introspectionView) else {
return nil
}
return Introspect.previousSibling(containing: TargetView.self, from: viewHost)
},
customize: customize
))
}
/// Finds a `UITextField` from a `SwiftUI.TextField`
public func introspectTextField(customize: @escaping (UITextField) -> ()) -> some View {
return introspect(customize: customize)
}
/// Finds a `UISwitch` from a `SwiftUI.Toggle`
public func introspectSwitch(customize: @escaping (UISwitch) -> ()) -> some View {
return introspect(customize: customize)
}
/// Finds a `UISlider` from a `SwiftUI.Slider`
public func introspectSlider(customize: @escaping (UISlider) -> ()) -> some View {
return introspect(customize: customize)
}
/// Finds a `UIStepper` from a `SwiftUI.Stepper`
public func introspectStepper(customize: @escaping (UIStepper) -> ()) -> some View {
return introspect(customize: customize)
}
/// Finds a `UIDatePicker` from a `SwiftUI.DatePicker`
public func introspectDatePicker(customize: @escaping (UIDatePicker) -> ()) -> some View {
return introspect(customize: customize)
}
/// Finds a `UISegmentedControl` from a `SwiftUI.Picker` with style `SegmentedPickerStyle`
public func introspectSegmentedControl(customize: @escaping (UISegmentedControl) -> ()) -> some View {
return introspect(customize: customize)
}
}

View File

@ -0,0 +1,62 @@
#if canImport(UIKit)
import UIKit
import SwiftUI
/// Introspection UIView that is inserted alongside the target view.
public class IntrospectionUIView: UIView {
required init() {
super.init(frame: .zero)
isHidden = true
isUserInteractionEnabled = false
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
/// Introspection View that is injected into the UIKit hierarchy alongside the target view.
/// After `updateUIView` is called, it calls `selector` to find the target view, then `customize` when the target view is found.
public struct UIKitIntrospectionView<TargetViewType: UIView>: UIViewRepresentable {
/// Method that introspects the view hierarchy to find the target view.
/// First argument is the introspection view itself, which is contained in a view host alongside the target view.
let selector: (IntrospectionUIView) -> TargetViewType?
/// User-provided customization method for the target view.
let customize: (TargetViewType) -> Void
public init(
selector: @escaping (IntrospectionUIView) -> TargetViewType?,
customize: @escaping (TargetViewType) -> Void
) {
self.selector = selector
self.customize = customize
}
public func makeUIView(context: UIViewRepresentableContext<UIKitIntrospectionView>) -> IntrospectionUIView {
let view = IntrospectionUIView()
view.accessibilityLabel = "IntrospectionUIView<\(TargetViewType.self)>"
return view
}
/// When `updateUiView` is called after creating the Introspection view, it is not yet in the UIKit hierarchy.
/// At this point, `introspectionView.superview.superview` is nil and we can't access the target UIKit view.
/// To workaround this, we wait until the runloop is done inserting the introspection view in the hierarchy, then run the selector.
/// Finding the target view fails silently if the selector yield no result. This happens when `updateUIView`
/// gets called when the introspection view gets removed from the hierarchy.
public func updateUIView(
_ uiView: IntrospectionUIView,
context: UIViewRepresentableContext<UIKitIntrospectionView>
) {
DispatchQueue.main.async {
guard let targetView = self.selector(uiView) else {
return
}
self.customize(targetView)
}
}
}
#endif

View File

@ -0,0 +1,53 @@
#if canImport(UIKit)
import SwiftUI
import UIKit
/// Introspection UIViewController that is inserted alongside the target view controller.
public class IntrospectionUIViewController: UIViewController {
required init() {
super.init(nibName: nil, bundle: nil)
view = IntrospectionUIView()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
/// This is the same logic as IntrospectionView but for view controllers. Please see details above.
public struct UIKitIntrospectionViewController<TargetViewControllerType: UIViewController>: UIViewControllerRepresentable {
let selector: (IntrospectionUIViewController) -> TargetViewControllerType?
let customize: (TargetViewControllerType) -> Void
public init(
selector: @escaping (UIViewController) -> TargetViewControllerType?,
customize: @escaping (TargetViewControllerType) -> Void
) {
self.selector = selector
self.customize = customize
}
public func makeUIViewController(
context: UIViewControllerRepresentableContext<UIKitIntrospectionViewController>
) -> IntrospectionUIViewController {
let viewController = IntrospectionUIViewController()
viewController.accessibilityLabel = "IntrospectionUIViewController<\(TargetViewControllerType.self)>"
viewController.view.accessibilityLabel = "IntrospectionUIView<\(TargetViewControllerType.self)>"
return viewController
}
public func updateUIViewController(
_ uiViewController: IntrospectionUIViewController,
context: UIViewControllerRepresentableContext<UIKitIntrospectionViewController>
) {
DispatchQueue.main.async {
guard let targetView = self.selector(uiViewController) else {
return
}
self.customize(targetView)
}
}
}
#endif

View File

@ -0,0 +1,166 @@
import SwiftUI
#if canImport(AppKit)
import AppKit
#elseif canImport(UIKit)
import UIKit
#endif
extension View {
public func inject<SomeView>(_ view: SomeView) -> some View where SomeView: View {
return overlay(view.frame(width: 0, height: 0))
}
}
#if canImport(UIKit)
extension View {
/// Finds a `TargetView` from a `SwiftUI.View`
public func introspect<TargetView: UIView>(
selector: @escaping (IntrospectionUIView) -> TargetView?,
customize: @escaping (TargetView) -> ()
) -> some View {
return inject(UIKitIntrospectionView(
selector: selector,
customize: customize
))
}
/// Finds a `UINavigationController` from any view embedded in a `SwiftUI.NavigationView`.
public func introspectNavigationController(customize: @escaping (UINavigationController) -> ()) -> some View {
return inject(UIKitIntrospectionViewController(
selector: { introspectionViewController in
// Search in ancestors
if let navigationController = introspectionViewController.navigationController {
return navigationController
}
// Search in siblings
return Introspect.previousSibling(containing: UINavigationController.self, from: introspectionViewController)
},
customize: customize
))
}
/// Finds the containing `UIViewController` of a SwiftUI view.
public func introspectViewController(customize: @escaping (UIViewController) -> ()) -> some View {
return inject(UIKitIntrospectionViewController(
selector: { $0.parent },
customize: customize
))
}
/// Finds a `UITabBarController` from any SwiftUI view embedded in a `SwiftUI.TabView`
public func introspectTabBarController(customize: @escaping (UITabBarController) -> ()) -> some View {
return inject(UIKitIntrospectionViewController(
selector: { introspectionViewController in
// Search in ancestors
if let navigationController = introspectionViewController.tabBarController {
return navigationController
}
// Search in siblings
return Introspect.previousSibling(ofType: UITabBarController.self, from: introspectionViewController)
},
customize: customize
))
}
/// Finds a `UITableView` from a `SwiftUI.List`, or `SwiftUI.List` child.
public func introspectTableView(customize: @escaping (UITableView) -> ()) -> some View {
return introspect(selector: TargetViewSelector.ancestorOrSibling, customize: customize)
}
/// Finds a `UIScrollView` from a `SwiftUI.ScrollView`, or `SwiftUI.ScrollView` child.
public func introspectScrollView(customize: @escaping (UIScrollView) -> ()) -> some View {
return introspect(selector: TargetViewSelector.ancestorOrSibling, customize: customize)
}
/// Finds a `UITextField` from a `SwiftUI.TextField`
public func introspectTextField(customize: @escaping (UITextField) -> ()) -> some View {
return introspect(selector: TargetViewSelector.sibling, customize: customize)
}
/// Finds a `UISwitch` from a `SwiftUI.Toggle`
@available(tvOS, unavailable)
public func introspectSwitch(customize: @escaping (UISwitch) -> ()) -> some View {
return introspect(selector: TargetViewSelector.sibling, customize: customize)
}
/// Finds a `UISlider` from a `SwiftUI.Slider`
@available(tvOS, unavailable)
public func introspectSlider(customize: @escaping (UISlider) -> ()) -> some View {
return introspect(selector: TargetViewSelector.sibling, customize: customize)
}
/// Finds a `UIStepper` from a `SwiftUI.Stepper`
@available(tvOS, unavailable)
public func introspectStepper(customize: @escaping (UIStepper) -> ()) -> some View {
return introspect(selector: TargetViewSelector.sibling, customize: customize)
}
/// Finds a `UIDatePicker` from a `SwiftUI.DatePicker`
@available(tvOS, unavailable)
public func introspectDatePicker(customize: @escaping (UIDatePicker) -> ()) -> some View {
return introspect(selector: TargetViewSelector.sibling, customize: customize)
}
/// Finds a `UISegmentedControl` from a `SwiftUI.Picker` with style `SegmentedPickerStyle`
public func introspectSegmentedControl(customize: @escaping (UISegmentedControl) -> ()) -> some View {
return introspect(selector: TargetViewSelector.sibling, customize: customize)
}
}
#endif
#if canImport(AppKit)
extension View {
/// Finds a `TargetView` from a `SwiftUI.View`
public func introspect<TargetView: NSView>(
selector: @escaping (IntrospectionNSView) -> TargetView?,
customize: @escaping (TargetView) -> ()
) -> some View {
return inject(AppKitIntrospectionView(
selector: selector,
customize: customize
))
}
/// Finds a `NSTableView` from a `SwiftUI.List`, or `SwiftUI.List` child.
public func introspectTableView(customize: @escaping (NSTableView) -> ()) -> some View {
return introspect(selector: TargetViewSelector.ancestorOrSibling, customize: customize)
}
/// Finds a `NSScrollView` from a `SwiftUI.ScrollView`, or `SwiftUI.ScrollView` child.
public func introspectScrollView(customize: @escaping (NSScrollView) -> ()) -> some View {
return introspect(selector: TargetViewSelector.ancestorOrSibling, customize: customize)
}
/// Finds a `NSTextField` from a `SwiftUI.TextField`
public func introspectTextField(customize: @escaping (NSTextField) -> ()) -> some View {
return introspect(selector: TargetViewSelector.sibling, customize: customize)
}
/// Finds a `NSSlider` from a `SwiftUI.Slider`
public func introspectSlider(customize: @escaping (NSSlider) -> ()) -> some View {
return introspect(selector: TargetViewSelector.sibling, customize: customize)
}
/// Finds a `NSStepper` from a `SwiftUI.Stepper`
public func introspectStepper(customize: @escaping (NSStepper) -> ()) -> some View {
return introspect(selector: TargetViewSelector.sibling, customize: customize)
}
/// Finds a `NSDatePicker` from a `SwiftUI.DatePicker`
public func introspectDatePicker(customize: @escaping (NSDatePicker) -> ()) -> some View {
return introspect(selector: TargetViewSelector.sibling, customize: customize)
}
/// Finds a `NSSegmentedControl` from a `SwiftUI.Picker` with style `SegmentedPickerStyle`
public func introspectSegmentedControl(customize: @escaping (NSSegmentedControl) -> ()) -> some View {
return introspect(selector: TargetViewSelector.sibling, customize: customize)
}
}
#endif

View File

@ -1,98 +0,0 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

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

View File

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>0.0.6</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>

View File

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

View File

@ -0,0 +1,202 @@
#if canImport(AppKit)
import XCTest
import SwiftUI
@testable import Introspect
enum TestUtils {
static func present<ViewType: View>(view: ViewType) {
let window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
window.center()
window.setFrameAutosaveName("Main Window")
window.contentView = NSHostingView(rootView: view)
window.makeKeyAndOrderFront(nil)
}
}
private struct ListTestView: View {
let spy1: () -> Void
let spy2: () -> Void
var body: some View {
List {
Text("Item 1")
Text("Item 2")
.introspectTableView { tableView in
self.spy2()
}
}
.introspectTableView { tableView in
self.spy1()
}
}
}
private struct ScrollTestView: View {
let spy1: () -> Void
let spy2: () -> Void
var body: some View {
HStack {
ScrollView {
Text("Item 1")
}
.introspectScrollView { scrollView in
self.spy1()
}
ScrollView {
Text("Item 1")
.introspectScrollView { scrollView in
self.spy2()
}
}
}
}
}
private struct TextFieldTestView: View {
let spy: () -> Void
@State private var textFieldValue = ""
var body: some View {
TextField("Text Field", text: $textFieldValue)
.introspectTextField { textField in
self.spy()
}
}
}
private struct SliderTestView: View {
let spy: () -> Void
@State private var sliderValue = 0.0
var body: some View {
Slider(value: $sliderValue, in: 0...100)
.introspectSlider { slider in
self.spy()
}
}
}
private struct StepperTestView: View {
let spy: () -> Void
var body: some View {
Stepper(onIncrement: {}, onDecrement: {}) {
Text("Stepper")
}
.introspectStepper { stepper in
self.spy()
}
}
}
private struct DatePickerTestView: View {
let spy: () -> Void
@State private var datePickerValue = Date()
var body: some View {
DatePicker(selection: $datePickerValue) {
Text("DatePicker")
}
.introspectDatePicker { datePicker in
self.spy()
}
}
}
private struct SegmentedControlTestView: View {
@State private var pickerValue = 0
let spy: () -> Void
var body: some View {
Picker(selection: $pickerValue, label: Text("Segmented control")) {
Text("Option 1").tag(0)
Text("Option 2").tag(1)
Text("Option 3").tag(2)
}
.pickerStyle(SegmentedPickerStyle())
.introspectSegmentedControl { segmentedControl in
self.spy()
}
}
}
class AppKitTests: XCTestCase {
func testList() {
let expectation1 = XCTestExpectation()
let expectation2 = XCTestExpectation()
let view = ListTestView(
spy1: { expectation1.fulfill() },
spy2: { expectation2.fulfill() }
)
TestUtils.present(view: view)
wait(for: [expectation1, expectation2], timeout: 1)
}
func testScrollView() {
let expectation1 = XCTestExpectation()
let expectation2 = XCTestExpectation()
let view = ScrollTestView(
spy1: { expectation1.fulfill() },
spy2: { expectation2.fulfill() }
)
TestUtils.present(view: view)
wait(for: [expectation1, expectation2], timeout: 1)
}
func testTextField() {
let expectation = XCTestExpectation()
let view = TextFieldTestView(spy: {
expectation.fulfill()
})
TestUtils.present(view: view)
wait(for: [expectation], timeout: 1)
}
func testSlider() {
let expectation = XCTestExpectation()
let view = SliderTestView(spy: {
expectation.fulfill()
})
TestUtils.present(view: view)
wait(for: [expectation], timeout: 1)
}
func testStepper() {
let expectation = XCTestExpectation()
let view = StepperTestView(spy: {
expectation.fulfill()
})
TestUtils.present(view: view)
wait(for: [expectation], timeout: 1)
}
func testDatePicker() {
let expectation = XCTestExpectation()
let view = DatePickerTestView(spy: {
expectation.fulfill()
})
TestUtils.present(view: view)
wait(for: [expectation], timeout: 1)
}
func testSegmentedControl() {
let expectation = XCTestExpectation()
let view = SegmentedControlTestView(spy: {
expectation.fulfill()
})
TestUtils.present(view: view)
wait(for: [expectation], timeout: 1)
}
}
#endif

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>0.0.6</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>

View File

@ -1,26 +0,0 @@
import Foundation
import UIKit
import SwiftUI
enum TestUtils {
static func present<ViewType: View>(view: ViewType) {
let hostingController = UIHostingController(rootView: view)
let application = UIApplication.shared
application.windows.forEach { window in
if let presentedViewController = window.rootViewController?.presentedViewController {
presentedViewController.dismiss(animated: false, completion: nil)
}
window.isHidden = true
}
let window = UIWindow(frame: UIScreen.main.bounds)
window.layer.speed = 10
hostingController.beginAppearanceTransition(true, animated: false)
window.rootViewController = hostingController
window.makeKeyAndVisible()
hostingController.endAppearanceTransition()
}
}

View File

@ -1,8 +1,32 @@
#if canImport(UIKit)
import XCTest
import SwiftUI
@testable import Introspect
enum TestUtils {
static func present<ViewType: View>(view: ViewType) {
let hostingController = UIHostingController(rootView: view)
let application = UIApplication.shared
application.windows.forEach { window in
if let presentedViewController = window.rootViewController?.presentedViewController {
presentedViewController.dismiss(animated: false, completion: nil)
}
window.isHidden = true
}
let window = UIWindow(frame: UIScreen.main.bounds)
window.layer.speed = 10
hostingController.beginAppearanceTransition(true, animated: false)
window.rootViewController = hostingController
window.makeKeyAndVisible()
hostingController.endAppearanceTransition()
}
}
private struct NavigationTestView: View {
let spy: () -> Void
var body: some View {
@ -126,6 +150,7 @@ private struct TextFieldTestView: View {
}
}
@available(tvOS, unavailable)
private struct ToggleTestView: View {
let spy: () -> Void
@State private var toggleValue = false
@ -137,6 +162,7 @@ private struct ToggleTestView: View {
}
}
@available(tvOS, unavailable)
private struct SliderTestView: View {
let spy: () -> Void
@State private var sliderValue = 0.0
@ -148,6 +174,7 @@ private struct SliderTestView: View {
}
}
@available(tvOS, unavailable)
private struct StepperTestView: View {
let spy: () -> Void
var body: some View {
@ -160,6 +187,7 @@ private struct StepperTestView: View {
}
}
@available(tvOS, unavailable)
private struct DatePickerTestView: View {
let spy: () -> Void
@State private var datePickerValue = Date()
@ -189,7 +217,7 @@ private struct SegmentedControlTestView: View {
}
}
class IntrospectTests: XCTestCase {
class UIKitTests: XCTestCase {
func testNavigation() {
let expectation = XCTestExpectation()
@ -210,16 +238,6 @@ class IntrospectTests: XCTestCase {
wait(for: [expectation], timeout: 1)
}
func testRootNavigation() {
let expectation = XCTestExpectation()
let view = NavigationRootTestView(spy: {
expectation.fulfill()
})
TestUtils.present(view: view)
wait(for: [expectation], timeout: 1)
}
func testTabView() {
let expectation = XCTestExpectation()
@ -274,6 +292,27 @@ class IntrospectTests: XCTestCase {
wait(for: [expectation], timeout: 1)
}
func testSegmentedControl() {
let expectation = XCTestExpectation()
let view = SegmentedControlTestView(spy: {
expectation.fulfill()
})
TestUtils.present(view: view)
wait(for: [expectation], timeout: 1)
}
#if os(iOS)
func testRootNavigation() {
let expectation = XCTestExpectation()
let view = NavigationRootTestView(spy: {
expectation.fulfill()
})
TestUtils.present(view: view)
wait(for: [expectation], timeout: 1)
}
func testToggle() {
let expectation = XCTestExpectation()
@ -313,14 +352,6 @@ class IntrospectTests: XCTestCase {
TestUtils.present(view: view)
wait(for: [expectation], timeout: 1)
}
func testSegmentedControl() {
let expectation = XCTestExpectation()
let view = SegmentedControlTestView(spy: {
expectation.fulfill()
})
TestUtils.present(view: view)
wait(for: [expectation], timeout: 1)
}
#endif
}
#endif

View File

@ -5,7 +5,7 @@ import PackageDescription
let package = Package(
name: "Introspect",
platforms: [
.iOS(.v13)
.macOS(.v10_15), .iOS(.v13), .tvOS(.v13)
],
products: [
.library(
@ -19,6 +19,11 @@ let package = Package(
name: "Introspect",
dependencies: [],
path: "Introspect"
),
.testTarget(
name: "IntrospectTests",
dependencies: ["Introspect"],
path: "IntrospectTests"
)
]
)

View File

@ -3,7 +3,7 @@ Introspect for SwiftUI
[![CircleCI](https://circleci.com/gh/siteline/SwiftUI-Introspect.svg?style=svg&circle-token=6f995f204d4d417d31f79e7257f6e1ecf430ae07)](https://circleci.com/gh/siteline/SwiftUI-Introspect)
Introspect allows you to get the underlying UIKit element of a SwiftUI view.
Introspect allows you to get the underlying UIKit or AppKit element of a SwiftUI view.
For instance, with Introspect you can access `UITableView` to modify separators, or `UINavigationController` to customize the tab bar.
@ -47,19 +47,19 @@ Introspection
### Implemented
SwiftUI | UIKit | Introspect | Target
--- | --- | --- | ---
List | UITableView | `.introspectTableView()` | List, or List child
ScrollView | UIScrollView | `.introspectScrollView()` | ScrollView, or ScrollView child
NavigationView | UINavigationController | `.introspectNavigationController()` | NavigationView, or NavigationView child
_Any embedded view_ | UIViewController | `.introspectViewController()` | View embedded in a view controller
TabView | UITabBarController | `.introspectTabBarController()` | TabView, or TabView child
TextField | UITextField | `.introspectTextField()` | TextField
Toggle | UISwitch | `.introspectSwitch()` | Toggle
Slider | UISlider | `.introspectSlider()` | Slider
Stepper | UIStepper | `.introspectStepper()` | Stepper
DatePicker | UIDatePicker | `.introspectDatePicker()` | DatePicker
Picker (SegmentedPickerStyle) | UISegmentedControl | `.introspectSegmentedControl()` | Picker
SwiftUI | UIKit | AppKit | Introspect
--- | --- | --- | --- | ---
List | UITableView | NSTableView | `.introspectTableView()`
ScrollView | UIScrollView | NSScrollView | `.introspectScrollView()`
NavigationView | UINavigationController | _N/A_ | `.introspectNavigationController()`
_Any embedded view_ | UIViewController | _N/A_ | `.introspectViewController()`
TabView | UITabBarController | _N/A_ | `.introspectTabBarController()`
TextField | UITextField | NSTextField | `.introspectTextField()`
Toggle | UISwitch | N/A | `.introspectSwitch()`
Slider | UISlider | NSSlider | `.introspectSlider()`
Stepper | UIStepper | NSStepper | `.introspectStepper()`
DatePicker | UIDatePicker | NSDatePicker | `.introspectDatePicker()`
Picker (SegmentedPickerStyle) | UISegmentedControl | NSSegmentedControl | `.introspectSegmentedControl()`
**Missing an element?** Please [create an issue](https://github.com/timbersoftware/SwiftUI-Introspect/issues). As a temporary solution, you can [implement your own selector](#implement-your-own-selector).
@ -148,3 +148,28 @@ You can use any of the following [methods](https://github.com/timbersoftware/Swi
- `Introspect.findAncestor(ofType:from:)`
- `Introspect.findHostingView(from:)`
- `Introspect.findViewHost(from:)`
Releasing
---------
- Increment version number:
```
$ bundle exec fastlane run increment_version_number bump_type:minor # major|minor|patch
```
- Update changelog with new version
- Bump version in `Introspect.podspec`
- Commit and push changes
- Tag new version:
```
$ git tag -a <VERSION> -m "<MESSAGE>"
$ git push origin --tags
```
- Push to cocoapods trunk:
```
$ bundle exec pod trunk push .
```

View File

@ -1,9 +1,24 @@
default_platform(:ios)
skip_docs
lane :test do
scan(
devices: ["iPhone 8"],
scheme: "Introspect"
)
platform :ios do
lane :test do
scan(
devices: ["iPhone 8"],
scheme: "Introspect iOS"
)
scan(
devices: ["Apple TV"],
scheme: "Introspect tvOS"
)
end
end
platform :mac do
lane :test do
scan(
scheme: "Introspect macOS"
)
end
end