Skip to content

Commit eec80ac

Browse files
authored
Add WebView introspection (#460)
1 parent 5083262 commit eec80ac

File tree

3 files changed

+188
-0
lines changed

3 files changed

+188
-0
lines changed

Sources/ViewTypes/WebView.swift

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#if !os(watchOS)
2+
import SwiftUI
3+
4+
/// An abstract representation of the `WebView` type in SwiftUI.
5+
///
6+
/// ### iOS
7+
///
8+
/// ```swift
9+
/// struct ContentView: View {
10+
/// @State var url = URL(string: "https://example.com")!
11+
///
12+
/// var body: some View {
13+
/// WebView(url: url)
14+
/// .introspect(.webView, on: .iOS(.v26)) {
15+
/// print(type(of: $0)) // WKWebView
16+
/// }
17+
/// }
18+
/// }
19+
/// }
20+
/// ```
21+
///
22+
/// ### tvOS
23+
///
24+
/// ```swift
25+
/// struct ContentView: View {
26+
/// @State var url = URL(string: "https://example.com")!
27+
///
28+
/// var body: some View {
29+
/// WebView(url: url)
30+
/// .introspect(.webView, on: .tvOS(.v26)) {
31+
/// print(type(of: $0)) // WKWebView
32+
/// }
33+
/// }
34+
/// }
35+
/// }
36+
/// ```
37+
///
38+
/// ### macOS
39+
///
40+
/// ```swift
41+
/// struct ContentView: View {
42+
/// @State var url = URL(string: "https://example.com")!
43+
///
44+
/// var body: some View {
45+
/// WebView(url: url)
46+
/// .introspect(.webView, on: .macOS(.v26)) {
47+
/// print(type(of: $0)) // WKWebView
48+
/// }
49+
/// }
50+
/// }
51+
/// }
52+
/// ```
53+
///
54+
/// ### visionOS
55+
///
56+
/// ```swift
57+
/// struct ContentView: View {
58+
/// @State var url = URL(string: "https://example.com")!
59+
///
60+
/// var body: some View {
61+
/// WebView(url: url)
62+
/// .introspect(.webView, on: .visionOS(.v26)) {
63+
/// print(type(of: $0)) // WKWebView
64+
/// }
65+
/// }
66+
/// }
67+
/// }
68+
/// ```
69+
public struct WebViewType: IntrospectableViewType {}
70+
71+
#if canImport(WebKit)
72+
import WebKit
73+
74+
extension IntrospectableViewType where Self == WebViewType {
75+
public static var webView: Self { .init() }
76+
}
77+
78+
extension iOSViewVersion<WebViewType, WKWebView> {
79+
@available(*, unavailable, message: "WebView isn't available on iOS 13")
80+
public static let v13 = Self.unavailable()
81+
@available(*, unavailable, message: "WebView isn't available on iOS 14")
82+
public static let v14 = Self.unavailable()
83+
@available(*, unavailable, message: "WebView isn't available on iOS 15")
84+
public static let v15 = Self.unavailable()
85+
@available(*, unavailable, message: "WebView isn't available on iOS 16")
86+
public static let v16 = Self.unavailable()
87+
@available(*, unavailable, message: "WebView isn't available on iOS 17")
88+
public static let v17 = Self.unavailable()
89+
@available(*, unavailable, message: "WebView isn't available on iOS 18")
90+
public static let v18 = Self.unavailable()
91+
92+
public static let v26 = Self(for: .v26)
93+
}
94+
95+
extension tvOSViewVersion<WebViewType, WKWebView> {
96+
@available(*, unavailable, message: "WebView isn't available on tvOS 13")
97+
public static let v13 = Self.unavailable()
98+
@available(*, unavailable, message: "WebView isn't available on tvOS 14")
99+
public static let v14 = Self.unavailable()
100+
@available(*, unavailable, message: "WebView isn't available on tvOS 15")
101+
public static let v15 = Self.unavailable()
102+
@available(*, unavailable, message: "WebView isn't available on tvOS 16")
103+
public static let v16 = Self.unavailable()
104+
@available(*, unavailable, message: "WebView isn't available on tvOS 17")
105+
public static let v17 = Self.unavailable()
106+
@available(*, unavailable, message: "WebView isn't available on tvOS 18")
107+
public static let v18 = Self.unavailable()
108+
109+
public static let v26 = Self(for: .v26)
110+
}
111+
112+
extension macOSViewVersion<WebViewType, WKWebView> {
113+
@available(*, unavailable, message: "WebView isn't available on macOS 10.15")
114+
public static let v10_15 = Self.unavailable()
115+
@available(*, unavailable, message: "WebView isn't available on macOS 11")
116+
public static let v11 = Self.unavailable()
117+
@available(*, unavailable, message: "WebView isn't available on macOS 12")
118+
public static let v12 = Self.unavailable()
119+
@available(*, unavailable, message: "WebView isn't available on macOS 13")
120+
public static let v13 = Self.unavailable()
121+
@available(*, unavailable, message: "WebView isn't available on macOS 14")
122+
public static let v14 = Self.unavailable()
123+
@available(*, unavailable, message: "WebView isn't available on macOS 15")
124+
public static let v15 = Self.unavailable()
125+
126+
public static let v26 = Self(for: .v26)
127+
}
128+
129+
extension visionOSViewVersion<WebViewType, WKWebView> {
130+
@available(*, unavailable, message: "WebView isn't available on visionOS 1")
131+
public static let v1 = Self.unavailable()
132+
@available(*, unavailable, message: "WebView isn't available on visionOS 2")
133+
public static let v2 = Self.unavailable()
134+
135+
public static let v26 = Self(for: .v26)
136+
}
137+
#endif
138+
#endif

Tests/Tests.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
D503B2AC2A49BFE300027F5F /* VideoPlayerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D503B2AB2A49BFE300027F5F /* VideoPlayerTests.swift */; };
1111
D50FFE8E2A17E2A400C32641 /* ScrollViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D50FFE8D2A17E2A400C32641 /* ScrollViewTests.swift */; };
1212
D534D4DC2A4A596200218BFB /* WindowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D534D4DB2A4A596200218BFB /* WindowTests.swift */; };
13+
D55BAD142DFF2B050038443E /* WebViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55BAD132DFF2B050038443E /* WebViewTests.swift */; };
1314
D55F448D2A1FF209003381E4 /* ListTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55F448C2A1FF209003381E4 /* ListTests.swift */; };
1415
D57506782A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506772A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift */; };
1516
D575067A2A27BF6C00A628E4 /* PickerWithMenuStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506792A27BF6C00A628E4 /* PickerWithMenuStyleTests.swift */; };
@@ -101,6 +102,7 @@
101102
D50FFE8D2A17E2A400C32641 /* ScrollViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewTests.swift; sourceTree = "<group>"; };
102103
D534D4DB2A4A596200218BFB /* WindowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowTests.swift; sourceTree = "<group>"; };
103104
D549D9732A66D876005D4FB5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
105+
D55BAD132DFF2B050038443E /* WebViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewTests.swift; sourceTree = "<group>"; };
104106
D55F448C2A1FF209003381E4 /* ListTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTests.swift; sourceTree = "<group>"; };
105107
D57506772A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickerWithSegmentedStyleTests.swift; sourceTree = "<group>"; };
106108
D57506792A27BF6C00A628E4 /* PickerWithMenuStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickerWithMenuStyleTests.swift; sourceTree = "<group>"; };
@@ -317,6 +319,7 @@
317319
D503B2AB2A49BFE300027F5F /* VideoPlayerTests.swift */,
318320
D58119C52A227E930081F853 /* ViewTests.swift */,
319321
D5F26E032A56E74B001209E6 /* ViewControllerTests.swift */,
322+
D55BAD132DFF2B050038443E /* WebViewTests.swift */,
320323
D534D4DB2A4A596200218BFB /* WindowTests.swift */,
321324
);
322325
path = ViewTypes;
@@ -606,6 +609,7 @@
606609
D58547FA2A1D12270068ADF4 /* NavigationSplitViewTests.swift in Sources */,
607610
D5F8D5EF2A1E87950054E9AB /* NavigationViewWithColumnsStyleTests.swift in Sources */,
608611
D57506882A27CB9800A628E4 /* FormTests.swift in Sources */,
612+
D55BAD142DFF2B050038443E /* WebViewTests.swift in Sources */,
609613
D58119C82A22AC130081F853 /* ToggleTests.swift in Sources */,
610614
D58119D22A23A77C0081F853 /* StepperTests.swift in Sources */,
611615
D58119DA2A23B7700081F853 /* ColorPickerTests.swift in Sources */,
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#if compiler(>=6.2) && canImport(WebKit)
2+
import SwiftUI
3+
import SwiftUIIntrospect
4+
import Testing
5+
import WebKit
6+
7+
@MainActor
8+
@Suite
9+
struct WebViewTests {
10+
@available(iOS 26, tvOS 26, macOS 26, visionOS 26, *)
11+
@Test func webView() async throws {
12+
XCTAssertViewIntrospection(of: WKWebView.self) { spies in
13+
let spy0 = spies[0]
14+
let spy1 = spies[1]
15+
let spy2 = spies[2]
16+
17+
VStack {
18+
WebView(url: nil)
19+
.introspect(
20+
.webView,
21+
on: .iOS(.v26), .tvOS(.v26), .macOS(.v26), .visionOS(.v26),
22+
customize: spy0
23+
)
24+
25+
WebView(url: nil)
26+
.introspect(
27+
.webView,
28+
on: .iOS(.v26), .tvOS(.v26), .macOS(.v26), .visionOS(.v26),
29+
customize: spy1
30+
)
31+
32+
WebView(url: nil)
33+
.introspect(
34+
.webView,
35+
on: .iOS(.v26), .tvOS(.v26), .macOS(.v26), .visionOS(.v26),
36+
customize: spy2
37+
)
38+
}
39+
} extraAssertions: {
40+
#expect($0[safe: 0] !== $0[safe: 1])
41+
#expect($0[safe: 0] !== $0[safe: 2])
42+
#expect($0[safe: 1] !== $0[safe: 2])
43+
}
44+
}
45+
}
46+
#endif

0 commit comments

Comments
 (0)