Agent skills for iOS, iPadOS, Swift, SwiftUI, and modern Apple framework development.
90
90%
Does it follow best practices?
Impact
—
Average score across 248 eval scenarios
Advisory
Suggest reviewing before use
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.tessl-plugin
skills
accessorysetupkit
references
activitykit
references
adattributionkit
references
alarmkit
references
app-clips
app-intents
references
app-store-optimization
app-store-review
apple-on-device-ai
appmigrationkit
references
audioaccessorykit
references
authentication
references
avkit
references
background-processing
references
browserenginekit
references
callkit
references
carplay
references
cloudkit
references
contacts-framework
references
core-bluetooth
references
core-data
core-motion
references
core-nfc
references
coreml
references
cryptokit
references
cryptotokenkit
references
debugging-instruments
device-integrity
references
dockkit
references
energykit
references
eventkit
references
financekit
references
focus-engine
gamekit
references
healthkit
references
homekit
references
ios-accessibility
ios-localization
ios-networking
ios-simulator
references
mapkit
metrickit
references
musickit
references
natural-language
references
paperkit
references
passkit
references
pdfkit
references
pencilkit
references
permissionkit
references
photokit
push-notifications
realitykit
references
relevancekit
references
scenekit
references
sensorkit
references
speech-recognition
references
spritekit
references
storekit
swift-api-design-guidelines
swift-architecture
swift-charts
references
swift-codable
swift-concurrency
swift-formatstyle
swift-language
swift-security
references
swift-testing
swiftdata
swiftlint
swiftui-animation
swiftui-gestures
references
swiftui-layout-components
swiftui-liquid-glass
references
swiftui-patterns
swiftui-performance
swiftui-uikit-interop
swiftui-webkit
tabletopkit
references
tipkit
references
vision-framework
weatherkit
references
widgetkit
references