diff --git a/Sources/ViewTypes/WebView.swift b/Sources/ViewTypes/WebView.swift new file mode 100644 index 000000000..ad18914a0 --- /dev/null +++ b/Sources/ViewTypes/WebView.swift @@ -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 { + @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 { + @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 { + @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 { + @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 diff --git a/Tests/Tests.xcodeproj/project.pbxproj b/Tests/Tests.xcodeproj/project.pbxproj index 95e427fe8..2de80383f 100644 --- a/Tests/Tests.xcodeproj/project.pbxproj +++ b/Tests/Tests.xcodeproj/project.pbxproj @@ -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 */; }; @@ -101,6 +102,7 @@ D50FFE8D2A17E2A400C32641 /* ScrollViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewTests.swift; sourceTree = ""; }; D534D4DB2A4A596200218BFB /* WindowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowTests.swift; sourceTree = ""; }; D549D9732A66D876005D4FB5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + D55BAD132DFF2B050038443E /* WebViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewTests.swift; sourceTree = ""; }; D55F448C2A1FF209003381E4 /* ListTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTests.swift; sourceTree = ""; }; D57506772A27BBBD00A628E4 /* PickerWithSegmentedStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickerWithSegmentedStyleTests.swift; sourceTree = ""; }; D57506792A27BF6C00A628E4 /* PickerWithMenuStyleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickerWithMenuStyleTests.swift; sourceTree = ""; }; @@ -317,6 +319,7 @@ D503B2AB2A49BFE300027F5F /* VideoPlayerTests.swift */, D58119C52A227E930081F853 /* ViewTests.swift */, D5F26E032A56E74B001209E6 /* ViewControllerTests.swift */, + D55BAD132DFF2B050038443E /* WebViewTests.swift */, D534D4DB2A4A596200218BFB /* WindowTests.swift */, ); path = ViewTypes; @@ -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 */, diff --git a/Tests/Tests/ViewTypes/WebViewTests.swift b/Tests/Tests/ViewTypes/WebViewTests.swift new file mode 100644 index 000000000..98b02785d --- /dev/null +++ b/Tests/Tests/ViewTypes/WebViewTests.swift @@ -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