Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions Sources/ViewTypes/WebView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#if !os(watchOS)
import SwiftUI

/// An abstract representation of the `WebView` type in SwiftUI.
///
/// ### iOS
///
/// ```swift
/// struct ContentView: View {
/// @State var url = URL(string: "https://example.com")!
///
/// var body: some View {
/// WebView(url: url)
/// .introspect(.webView, on: .iOS(.v26)) {
/// print(type(of: $0)) // WKWebView
/// }
/// }
/// }
/// }
/// ```
///
/// ### tvOS
///
/// ```swift
/// struct ContentView: View {
/// @State var url = URL(string: "https://example.com")!
///
/// var body: some View {
/// WebView(url: url)
/// .introspect(.webView, on: .tvOS(.v26)) {
/// print(type(of: $0)) // WKWebView
/// }
/// }
/// }
/// }
/// ```
///
/// ### macOS
///
/// ```swift
/// struct ContentView: View {
/// @State var url = URL(string: "https://example.com")!
///
/// var body: some View {
/// WebView(url: url)
/// .introspect(.webView, on: .macOS(.v26)) {
/// print(type(of: $0)) // WKWebView
/// }
/// }
/// }
/// }
/// ```
///
/// ### visionOS
///
/// ```swift
/// struct ContentView: View {
/// @State var url = URL(string: "https://example.com")!
///
/// var body: some View {
/// WebView(url: url)
/// .introspect(.webView, on: .visionOS(.v26)) {
/// print(type(of: $0)) // WKWebView
/// }
/// }
/// }
/// }
/// ```
public struct WebViewType: IntrospectableViewType {}

#if canImport(WebKit)
import WebKit

extension IntrospectableViewType where Self == WebViewType {
public static var webView: Self { .init() }
}

extension iOSViewVersion<WebViewType, WKWebView> {
@available(*, unavailable, message: "WebView isn't available on iOS 13")
public static let v13 = Self.unavailable()
@available(*, unavailable, message: "WebView isn't available on iOS 14")
public static let v14 = Self.unavailable()
@available(*, unavailable, message: "WebView isn't available on iOS 15")
public static let v15 = Self.unavailable()
@available(*, unavailable, message: "WebView isn't available on iOS 16")
public static let v16 = Self.unavailable()
@available(*, unavailable, message: "WebView isn't available on iOS 17")
public static let v17 = Self.unavailable()
@available(*, unavailable, message: "WebView isn't available on iOS 18")
public static let v18 = Self.unavailable()

public static let v26 = Self(for: .v26)
}

extension tvOSViewVersion<WebViewType, WKWebView> {
@available(*, unavailable, message: "WebView isn't available on tvOS 13")
public static let v13 = Self.unavailable()
@available(*, unavailable, message: "WebView isn't available on tvOS 14")
public static let v14 = Self.unavailable()
@available(*, unavailable, message: "WebView isn't available on tvOS 15")
public static let v15 = Self.unavailable()
@available(*, unavailable, message: "WebView isn't available on tvOS 16")
public static let v16 = Self.unavailable()
@available(*, unavailable, message: "WebView isn't available on tvOS 17")
public static let v17 = Self.unavailable()
@available(*, unavailable, message: "WebView isn't available on tvOS 18")
public static let v18 = Self.unavailable()

public static let v26 = Self(for: .v26)
}

extension macOSViewVersion<WebViewType, WKWebView> {
@available(*, unavailable, message: "WebView isn't available on macOS 10.15")
public static let v10_15 = Self.unavailable()
@available(*, unavailable, message: "WebView isn't available on macOS 11")
public static let v11 = Self.unavailable()
@available(*, unavailable, message: "WebView isn't available on macOS 12")
public static let v12 = Self.unavailable()
@available(*, unavailable, message: "WebView isn't available on macOS 13")
public static let v13 = Self.unavailable()
@available(*, unavailable, message: "WebView isn't available on macOS 14")
public static let v14 = Self.unavailable()
@available(*, unavailable, message: "WebView isn't available on macOS 15")
public static let v15 = Self.unavailable()

public static let v26 = Self(for: .v26)
}

extension visionOSViewVersion<WebViewType, WKWebView> {
@available(*, unavailable, message: "WebView isn't available on visionOS 1")
public static let v1 = Self.unavailable()
@available(*, unavailable, message: "WebView isn't available on visionOS 2")
public static let v2 = Self.unavailable()

public static let v26 = Self(for: .v26)
}
#endif
#endif
4 changes: 4 additions & 0 deletions Tests/Tests.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
D503B2AC2A49BFE300027F5F /* VideoPlayerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D503B2AB2A49BFE300027F5F /* VideoPlayerTests.swift */; };
D50FFE8E2A17E2A400C32641 /* ScrollViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D50FFE8D2A17E2A400C32641 /* ScrollViewTests.swift */; };
D534D4DC2A4A596200218BFB /* WindowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D534D4DB2A4A596200218BFB /* WindowTests.swift */; };
D55BAD142DFF2B050038443E /* WebViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55BAD132DFF2B050038443E /* WebViewTests.swift */; };
D55F448D2A1FF209003381E4 /* ListTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55F448C2A1FF209003381E4 /* ListTests.swift */; };
D57506782A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506772A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift */; };
D575067A2A27BF6C00A628E4 /* PickerWithMenuStyleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57506792A27BF6C00A628E4 /* PickerWithMenuStyleTests.swift */; };
Expand Down Expand Up @@ -101,6 +102,7 @@
D50FFE8D2A17E2A400C32641 /* ScrollViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewTests.swift; sourceTree = "<group>"; };
D534D4DB2A4A596200218BFB /* WindowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowTests.swift; sourceTree = "<group>"; };
D549D9732A66D876005D4FB5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
D55BAD132DFF2B050038443E /* WebViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewTests.swift; sourceTree = "<group>"; };
D55F448C2A1FF209003381E4 /* ListTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTests.swift; sourceTree = "<group>"; };
D57506772A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickerWithSegmentedStyleTests.swift; sourceTree = "<group>"; };
D57506792A27BF6C00A628E4 /* PickerWithMenuStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickerWithMenuStyleTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -317,6 +319,7 @@
D503B2AB2A49BFE300027F5F /* VideoPlayerTests.swift */,
D58119C52A227E930081F853 /* ViewTests.swift */,
D5F26E032A56E74B001209E6 /* ViewControllerTests.swift */,
D55BAD132DFF2B050038443E /* WebViewTests.swift */,
D534D4DB2A4A596200218BFB /* WindowTests.swift */,
);
path = ViewTypes;
Expand Down Expand Up @@ -606,6 +609,7 @@
D58547FA2A1D12270068ADF4 /* NavigationSplitViewTests.swift in Sources */,
D5F8D5EF2A1E87950054E9AB /* NavigationViewWithColumnsStyleTests.swift in Sources */,
D57506882A27CB9800A628E4 /* FormTests.swift in Sources */,
D55BAD142DFF2B050038443E /* WebViewTests.swift in Sources */,
D58119C82A22AC130081F853 /* ToggleTests.swift in Sources */,
D58119D22A23A77C0081F853 /* StepperTests.swift in Sources */,
D58119DA2A23B7700081F853 /* ColorPickerTests.swift in Sources */,
Expand Down
46 changes: 46 additions & 0 deletions Tests/Tests/ViewTypes/WebViewTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#if compiler(>=6.2) && canImport(WebKit)
import SwiftUI
import SwiftUIIntrospect
import Testing
import WebKit

@MainActor
@Suite
struct WebViewTests {
@available(iOS 26, tvOS 26, macOS 26, visionOS 26, *)
@Test func webView() async throws {
XCTAssertViewIntrospection(of: WKWebView.self) { spies in
let spy0 = spies[0]
let spy1 = spies[1]
let spy2 = spies[2]

VStack {
WebView(url: nil)
.introspect(
.webView,
on: .iOS(.v26), .tvOS(.v26), .macOS(.v26), .visionOS(.v26),
customize: spy0
)

WebView(url: nil)
.introspect(
.webView,
on: .iOS(.v26), .tvOS(.v26), .macOS(.v26), .visionOS(.v26),
customize: spy1
)

WebView(url: nil)
.introspect(
.webView,
on: .iOS(.v26), .tvOS(.v26), .macOS(.v26), .visionOS(.v26),
customize: spy2
)
}
} extraAssertions: {
#expect($0[safe: 0] !== $0[safe: 1])
#expect($0[safe: 0] !== $0[safe: 2])
#expect($0[safe: 1] !== $0[safe: 2])
}
}
}
#endif