Skip to content

Commit 6a07494

Browse files
authored
Add view controller introspection (#298)
1 parent 8c1140f commit 6a07494

File tree

5 files changed

+124
-0
lines changed

5 files changed

+124
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Changelog
33

44
## master
55

6+
- Added: view controller introspection (#298)
67
- Added: page control introspection (#297)
78

89
## [0.8.0]

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ Introspection
134134
- [`Toggle` with `switch` style](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/togglewithswitchstyletype)
135135
- [`VideoPlayer`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/videoplayertype)
136136
- [`View`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/viewtype)
137+
- [`ViewController`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/viewcontrollertype)
137138
- [`Window`](https://swiftpackageindex.com/siteline/swiftui-introspect/master/documentation/swiftuiintrospect/windowtype)
138139

139140
**Missing an element?** Please [create an issue](https://github.com/timbersoftware/SwiftUI-Introspect/issues). As a temporary solution, you can [implement your own introspectable view type](#implement-your-own-view-type).
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import SwiftUI
2+
3+
/// An abstract representation of the receiving SwiftUI view's view controller,
4+
/// or the closest ancestor view controller if missing.
5+
///
6+
/// ### iOS
7+
///
8+
/// ```swift
9+
/// struct ContentView: View {
10+
/// var body: some View {
11+
/// NavigationView {
12+
/// Text("Root").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red)
13+
/// .introspect(.viewController, on: .iOS(.v13, .v14, .v15, .v16, .v17)) {
14+
/// print(type(of: $0)) // some subclass of UIHostingController
15+
/// }
16+
/// }
17+
/// .navigationViewStyle(.stack)
18+
/// .introspect(.viewController, on: .iOS(.v13, .v14, .v15, .v16, .v17)) {
19+
/// print(type(of: $0)) // UINavigationController
20+
/// }
21+
/// }
22+
/// }
23+
/// ```
24+
///
25+
/// ### tvOS
26+
///
27+
/// ```swift
28+
/// struct ContentView: View {
29+
/// var body: some View {
30+
/// NavigationView {
31+
/// Text("Root").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red)
32+
/// .introspect(.viewController, on: .tvOS(.v13, .v14, .v15, .v16, .v17)) {
33+
/// print(type(of: $0)) // some subclass of UIHostingController
34+
/// }
35+
/// }
36+
/// .navigationViewStyle(.stack)
37+
/// .introspect(.viewController, on: .tvOS(.v13, .v14, .v15, .v16, .v17)) {
38+
/// print(type(of: $0)) // UINavigationController
39+
/// }
40+
/// }
41+
/// }
42+
/// ```
43+
///
44+
/// ### macOS
45+
///
46+
/// Not available.
47+
///
48+
public struct ViewControllerType: IntrospectableViewType {
49+
public var scope: IntrospectionScope { [.receiver, .ancestor] }
50+
}
51+
52+
extension IntrospectableViewType where Self == ViewControllerType {
53+
public static var viewController: Self { .init() }
54+
}
55+
56+
#if canImport(UIKit)
57+
extension iOSViewVersion<ViewControllerType, UIViewController> {
58+
public static let v13 = Self(for: .v13)
59+
public static let v14 = Self(for: .v14)
60+
public static let v15 = Self(for: .v15)
61+
public static let v16 = Self(for: .v16)
62+
public static let v17 = Self(for: .v17)
63+
}
64+
65+
extension tvOSViewVersion<ViewControllerType, UIViewController> {
66+
public static let v13 = Self(for: .v13)
67+
public static let v14 = Self(for: .v14)
68+
public static let v15 = Self(for: .v15)
69+
public static let v16 = Self(for: .v16)
70+
public static let v17 = Self(for: .v17)
71+
}
72+
#endif

Tests/Tests.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
D5F0BE4D29C0DBE800AD95AB /* TestsHostApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F0BE4C29C0DBE800AD95AB /* TestsHostApp.swift */; };
113113
D5F0BE6A29C0DC4900AD95AB /* PlatformVersionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F0BE6729C0DC4900AD95AB /* PlatformVersionTests.swift */; };
114114
D5F26E022A561130001209E6 /* PageControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F26E012A561130001209E6 /* PageControlTests.swift */; };
115+
D5F26E042A56E74B001209E6 /* ViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F26E032A56E74B001209E6 /* ViewControllerTests.swift */; };
115116
D5F8D5ED2A1E7B490054E9AB /* NavigationViewWithStackStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F8D5EC2A1E7B490054E9AB /* NavigationViewWithStackStyleTests.swift */; };
116117
D5F8D5EF2A1E87950054E9AB /* NavigationViewWithColumnsStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F8D5EE2A1E87950054E9AB /* NavigationViewWithColumnsStyleTests.swift */; };
117118
/* End PBXBuildFile section */
@@ -198,6 +199,7 @@
198199
D5F0BE5D29C0DC0000AD95AB /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
199200
D5F0BE6729C0DC4900AD95AB /* PlatformVersionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlatformVersionTests.swift; sourceTree = "<group>"; };
200201
D5F26E012A561130001209E6 /* PageControlTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageControlTests.swift; sourceTree = "<group>"; };
202+
D5F26E032A56E74B001209E6 /* ViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerTests.swift; sourceTree = "<group>"; };
201203
D5F8D5EC2A1E7B490054E9AB /* NavigationViewWithStackStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationViewWithStackStyleTests.swift; sourceTree = "<group>"; };
202204
D5F8D5EE2A1E87950054E9AB /* NavigationViewWithColumnsStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationViewWithColumnsStyleTests.swift; sourceTree = "<group>"; };
203205
/* End PBXFileReference section */
@@ -297,6 +299,7 @@
297299
D575068B2A27D40500A628E4 /* ToggleWithSwitchStyleTests.swift */,
298300
D503B2AB2A49BFE300027F5F /* VideoPlayerTests.swift */,
299301
D58119C52A227E930081F853 /* ViewTests.swift */,
302+
D5F26E032A56E74B001209E6 /* ViewControllerTests.swift */,
300303
D534D4DB2A4A596200218BFB /* WindowTests.swift */,
301304
);
302305
path = ViewTypes;
@@ -630,6 +633,7 @@
630633
D575068C2A27D40500A628E4 /* ToggleWithSwitchStyleTests.swift in Sources */,
631634
D58119C42A211B8A0081F853 /* ListCellTests.swift in Sources */,
632635
D57506A22A281B9C00A628E4 /* SearchFieldTests.swift in Sources */,
636+
D5F26E042A56E74B001209E6 /* ViewControllerTests.swift in Sources */,
633637
D58119C62A227E930081F853 /* ViewTests.swift in Sources */,
634638
D575067E2A27C43400A628E4 /* ListWithGroupedStyleTests.swift in Sources */,
635639
D575069C2A27F68700A628E4 /* ProgressViewWithCircularStyleTests.swift in Sources */,
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#if os(iOS) || os(tvOS)
2+
import SwiftUI
3+
import SwiftUIIntrospect
4+
import XCTest
5+
6+
final class ViewControllerTests: XCTestCase {
7+
func testViewController() {
8+
XCTAssertViewIntrospection(of: PlatformViewController.self) { spies in
9+
let spy0 = spies[0]
10+
let spy1 = spies[1]
11+
let spy2 = spies[2]
12+
13+
TabView {
14+
NavigationView {
15+
Text("Root").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red)
16+
.introspect(
17+
.viewController,
18+
on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17),
19+
customize: spy2
20+
)
21+
}
22+
.navigationViewStyle(.stack)
23+
.tabItem {
24+
Image(systemName: "1.circle")
25+
Text("Tab 1")
26+
}
27+
.introspect(
28+
.viewController,
29+
on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17),
30+
customize: spy1
31+
)
32+
}
33+
.introspect(
34+
.viewController,
35+
on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17),
36+
customize: spy0
37+
)
38+
} extraAssertions: {
39+
XCTAssert($0[safe: 0] is UITabBarController)
40+
XCTAssert($0[safe: 1] is UINavigationController)
41+
XCTAssert(String(describing: $0[safe: 2]).contains("UIHostingController"))
42+
XCTAssert($0[safe: 1] === $0[safe: 2]?.parent)
43+
}
44+
}
45+
}
46+
#endif

0 commit comments

Comments
 (0)