swiftui-webkit — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited swiftui-webkit (Agent Skill) and scored it 45/100 (orange). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 2 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 2 flagged
A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Embed and manage web content in SwiftUI using the native WebKit-for-SwiftUI APIs introduced for iOS 26, iPadOS 26, macOS 26, and visionOS 26. Use this skill when the app needs an integrated web surface, app-owned HTML content, JavaScript-backed page interaction, or custom navigation policy control.
Use the narrowest tool that matches the job.
| Need | Default choice |
|---|---|
| Embedded app-owned web content in SwiftUI | WebView + WebPage |
| iOS/iPadOS modal browsing with Safari behavior | SFSafariViewController |
| macOS or visionOS browse-out behavior | openURL / default browser |
| OAuth or third-party sign-in | ASWebAuthenticationSession |
| Back-deploy below iOS 26 or use missing legacy-only WebKit features | WKWebView fallback |
Prefer WebView and WebPage for modern SwiftUI apps targeting iOS 26+ when the new API surface covers the feature. Apple’s WWDC25 guidance frames existing UIKit/AppKit WebKit wrappers in SwiftUI apps as good candidates to try migrating, not as a blanket mandate to delete every fallback.
Do not use embedded web views for OAuth. That stays an ASWebAuthenticationSession flow.
Use the simple WebView(url:) form when the app only needs to render a URL and SwiftUI state drives navigation.
import SwiftUI
import WebKit
struct ArticleView: View {
let url: URL
var body: some View {
WebView(url: url)
}
}Create a WebPage when the app needs to load requests directly, observe state, call JavaScript, or customize navigation behavior.
A WebPage can be associated with only one WebView at a time. Create separate WebPage instances for multiple visible web views.
@Observable
@MainActor
final class ArticleModel {
let page = WebPage()
func load(_ url: URL) async throws {
for try await _ in page.load(URLRequest(url: url)) {
}
}
}
struct ArticleDetailView: View {
@State private var model = ArticleModel()
let url: URL
var body: some View {
WebView(model.page)
.task {
try? await model.load(url)
}
}
}See references/loading-and-observation.md for full examples.
WebPage is an @MainActor observable type. Use it when you need page state in SwiftUI.
Common loading entry points:
load(URLRequest)load(URL)load(html:baseURL:)load(_:mimeType:characterEncoding:baseURL:)Common observable properties:
titleurlisLoadingestimatedProgresscurrentNavigationEventbackForwardListstruct ReaderView: View {
@State private var page = WebPage()
var body: some View {
WebView(page)
.navigationTitle(page.title ?? "Loading")
.overlay {
if page.isLoading {
ProgressView(value: page.estimatedProgress)
}
}
.task {
do {
for try await _ in page.load(URLRequest(url: URL(string: "https://example.com")!)) {
}
} catch {
// Handle load failure.
}
}
}
}When you need to react to every navigation, observe the navigation sequence rather than only checking a single property.
Task {
do {
for try await event in page.navigations {
// Handle started, redirect, committed, or finished events.
}
} catch {
// Handle WebPage.NavigationError or cancellation.
}
}See references/loading-and-observation.md for stronger patterns and the load-sequence examples.
Use WebPage.NavigationDeciding to allow, cancel, or customize navigations based on the request or response.
Typical uses:
openURLNavigationPreferences@MainActor
final class ArticleNavigationDecider: WebPage.NavigationDeciding {
var urlToOpenExternally: URL?
func decidePolicy(
for action: WebPage.NavigationAction,
preferences: inout WebPage.NavigationPreferences
) async -> WKNavigationActionPolicy {
guard let url = action.request.url else { return .allow }
if url.host == "example.com" {
return .allow
}
urlToOpenExternally = url
return .cancel
}
}Keep app-level deep-link routing in the navigation skill. This skill owns navigation that happens inside embedded web content.
See references/navigation-and-javascript.md for complete patterns.
Use callJavaScript(_:arguments:in:contentWorld:) to evaluate JavaScript functions against the page.
Pass a JavaScript function body, not a wrapped function declaration or call expression. Prefer arguments for Swift-provided values instead of interpolating untrusted strings into the script.
let script = """
const headings = [...document.querySelectorAll('h1, h2')];
return headings.map(node => ({
id: node.id,
text: node.textContent?.trim()
}));
"""
let result = try await page.callJavaScript(script)
let headings = result as? [[String: Any]] ?? []You can pass values through the arguments dictionary and cast the returned Any into the Swift type you actually need.
let result = try await page.callJavaScript(
"return document.getElementById(sectionID)?.getBoundingClientRect().top ?? null;",
arguments: ["sectionID": selectedSectionID]
)Handle empty and JavaScript null results deliberately: no explicit return produces nil, while an explicit JavaScript null returns NSNull.
Important boundary: the native SwiftUI WebKit API clearly supports Swift-to-JavaScript calls, but it does not expose an obvious direct replacement for WKScriptMessageHandler. If you need coarse JS-to-native signaling, a custom navigation or callback-URL pattern can work, but document it as a workaround pattern, not a guaranteed one-to-one replacement.
See references/navigation-and-javascript.md.
Use WebPage.Configuration and URLSchemeHandler when the app needs bundled HTML, offline documents, or app-provided resources under a custom scheme.
var configuration = WebPage.Configuration()
configuration.urlSchemeHandlers[URLScheme("docs")!] = DocsSchemeHandler(bundle: .main)
let page = WebPage(configuration: configuration)
for try await _ in page.load(URL(string: "docs://article/welcome")!) {
}
Use this for:
Do not overuse custom schemes for normal remote content. Prefer standard HTTPS for server-hosted pages.
See references/local-content-and-custom-schemes.md.
Use WebView modifiers to match the intended browsing experience.
Useful modifiers and related APIs:
webViewBackForwardNavigationGestures(_:)findNavigator(isPresented:)webViewScrollPosition(_:)webViewOnScrollGeometryChange(...)Apply them only when the user experience needs them.
Apple’s HIG also applies here: support back/forward navigation when appropriate, but do not turn an app web view into a general-purpose browser.
WKWebView wrappers by default in an iOS 26+ SwiftUI app instead of starting with WebView and WebPageASWebAuthenticationSessionWebPage only after building a plain WebView(url:) path that now needs state, JS, or navigation controlcallJavaScript as a direct replacement for WKScriptMessageHandlercallJavaScript instead of only the function bodypage.navigations without try/catch even though navigation failure terminates the sequence by throwingWebPage to multiple visible WebView valuesSFSafariViewController as the cross-platform browse-out answer on macOS or visionOS instead of using default-browser/openURL behaviorWebPage is main-actor-isolatedWebView and WebPage are the default path for iOS 26+ SwiftUI web contentASWebAuthenticationSession is used for auth flows instead of embedded web viewsWebPage is used whenever the app needs state observation, JS calls, or policy controlcallJavaScript uses a function body and passes Swift values through argumentspage.navigations loops use for try await and handle thrown navigation errorsWebView(page) owns a distinct WebPageSFSafariViewController is limited to iOS/iPadOS Safari-style modal browsing; macOS and visionOS browse-out flows use platform default browser behaviorWKWebView is justified by deployment target or missing API needs~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.