swiftui-webkit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSwiftUI WebKit
SwiftUI WebKit
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.
利用为iOS 26、iPadOS 26、macOS 26和visionOS 26推出的原生WebKit-for-SwiftUI API,在SwiftUI中嵌入并管理网页内容。当应用需要集成式网页界面、应用自有HTML内容、基于JavaScript的页面交互或自定义导航策略控制时,可使用该技能。
Contents
目录
Choose the Right Web Container
选择合适的网页容器
Use the narrowest tool that matches the job.
| Need | Default choice |
|---|---|
| Embedded app-owned web content in SwiftUI | |
| Simple external site presentation with Safari behavior | |
| OAuth or third-party sign-in | |
| Back-deploy below iOS 26 or use missing legacy-only WebKit features | |
Prefer and for modern SwiftUI apps targeting iOS 26+. Apple’s WWDC25 guidance explicitly recommends migrating SwiftUI apps away from UIKit/AppKit WebKit wrappers when possible.
WebViewWebPageDo not use embedded web views for OAuth. That stays an flow.
ASWebAuthenticationSession选择最匹配需求的工具。
| 需求 | 默认选择 |
|---|---|
| 在SwiftUI中嵌入应用自有网页内容 | |
| 以Safari行为展示简单外部站点 | |
| OAuth或第三方登录 | |
| 部署到iOS 26以下版本,或使用仅旧版WebKit支持的功能 | |
针对iOS 26+的现代SwiftUI应用,优先使用和。苹果WWDC25的指导明确建议,尽可能将SwiftUI应用从UIKit/AppKit WebKit包装器迁移到该方案。
WebViewWebPage不要使用嵌入式网页视图处理OAuth,这类场景应使用流程。
ASWebAuthenticationSessionDisplaying Web Content
展示网页内容
Use the simple form when the app only needs to render a URL and SwiftUI state drives navigation.
WebView(url:)swift
import SwiftUI
import WebKit
struct ArticleView: View {
let url: URL
var body: some View {
WebView(url: url)
}
}Create a when the app needs to load requests directly, observe state, call JavaScript, or customize navigation behavior.
WebPageswift
@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 for full examples.
references/loading-and-observation.md当应用仅需渲染URL且由SwiftUI状态驱动导航时,使用简单的形式。
WebView(url:)swift
import SwiftUI
import WebKit
struct ArticleView: View {
let url: URL
var body: some View {
WebView(url: url)
}
}当应用需要直接加载请求、观察状态、调用JavaScript或自定义导航行为时,创建实例。
WebPageswift
@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)
}
}
}完整示例请参考。
references/loading-and-observation.mdLoading and Observing with WebPage
使用WebPage加载与观察
WebPage@MainActorCommon loading entry points:
load(URLRequest)load(URL)load(html:baseURL:)load(_:mimeType:characterEncoding:baseURL:)
Common observable properties:
titleurlisLoadingestimatedProgresscurrentNavigationEventbackForwardList
swift
struct 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.
swift
Task {
for await event in page.navigations {
// Handle finish, redirect, or failure events.
}
}See for stronger patterns and the load-sequence examples.
references/loading-and-observation.mdWebPage@MainActor常见加载入口:
load(URLRequest)load(URL)load(html:baseURL:)load(_:mimeType:characterEncoding:baseURL:)
常见可观察属性:
titleurlisLoadingestimatedProgresscurrentNavigationEventbackForwardList
swift
struct ReaderView: View {
@State private var page = WebPage()
var body: some View {
WebView(page)
.navigationTitle(page.title ?? "加载中")
.overlay {
if page.isLoading {
ProgressView(value: page.estimatedProgress)
}
}
.task {
do {
for try await _ in page.load(URLRequest(url: URL(string: "https://example.com")!)) {
}
} catch {
// 处理加载失败
}
}
}
}当你需要响应每次导航时,观察导航序列而非仅检查单个属性。
swift
Task {
for await event in page.navigations {
// 处理完成、重定向或失败事件
}
}更完善的模式和加载序列示例请参考。
references/loading-and-observation.mdNavigation Policies
导航策略
Use to allow, cancel, or customize navigations based on the request or response.
WebPage.NavigationDecidingTypical uses:
- keep app-owned domains inside the embedded web view
- cancel external domains and hand them off with
openURL - intercept special callback URLs
- tune
NavigationPreferences
swift
@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 for complete patterns.
references/navigation-and-javascript.md使用根据请求或响应允许、取消或自定义导航行为。
WebPage.NavigationDeciding典型用途:
- 将应用自有域名的内容保留在嵌入式网页视图内
- 取消外部域名导航,并通过跳转至外部
openURL - 拦截特殊回调URL
- 调整
NavigationPreferences
swift
@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
}
}应用级深度链接路由由导航技能负责,本技能仅处理嵌入式网页内容内部的导航。
完整模式请参考。
references/navigation-and-javascript.mdJavaScript Integration
JavaScript集成
Use to evaluate JavaScript functions against the page.
callJavaScript(_:arguments:in:contentWorld:)swift
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 dictionary and cast the returned into the Swift type you actually need.
argumentsAnyswift
let result = try await page.callJavaScript(
"return document.getElementById(sectionID)?.getBoundingClientRect().top ?? null;",
arguments: ["sectionID": selectedSectionID]
)Important boundary: the native SwiftUI WebKit API clearly supports Swift-to-JavaScript calls, but it does not expose an obvious direct replacement for . 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.
WKScriptMessageHandlerSee .
references/navigation-and-javascript.md使用方法在页面中执行JavaScript函数。
callJavaScript(_:arguments:in:contentWorld:)swift
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]] ?? []你可以通过字典传递参数,并将返回的类型转换为实际需要的Swift类型。
argumentsAnyswift
let result = try await page.callJavaScript(
"return document.getElementById(sectionID)?.getBoundingClientRect().top ?? null;",
arguments: ["sectionID": selectedSectionID]
)重要边界:原生SwiftUI WebKit API明确支持Swift调用JavaScript,但并未提供的直接替代方案。如果需要实现JavaScript到原生的粗略信号传递,自定义导航或回调URL模式可以作为临时方案,但需明确这是一种变通方法,而非一对一的替代方案。
WKScriptMessageHandler更多内容请参考。
references/navigation-and-javascript.mdLocal Content and Custom URL Schemes
本地内容与自定义URL Scheme
Use and when the app needs bundled HTML, offline documents, or app-provided resources under a custom scheme.
WebPage.ConfigurationURLSchemeHandlerswift
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:
- bundled documentation or article content
- offline HTML/CSS/JS assets
- app-owned resource loading under a custom scheme
Do not overuse custom schemes for normal remote content. Prefer standard HTTPS for server-hosted pages.
See .
references/local-content-and-custom-schemes.md当应用需要加载捆绑的HTML、离线文档或自定义Scheme下的应用自有资源时,使用和。
WebPage.ConfigurationURLSchemeHandlerswift
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")!) {
}
适用于以下场景:
- 捆绑的文档或文章内容
- 离线HTML/CSS/JS资源
- 自定义Scheme下的应用自有资源加载
不要将自定义Scheme过度用于常规远程内容,服务器托管页面优先使用标准HTTPS。
更多内容请参考。
references/local-content-and-custom-schemes.mdWebView Customization
WebView自定义
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.
- Enable back/forward gestures when people are likely to visit multiple pages.
- Add Find in Page when the content is document-like.
- Sync scroll position only when the app has a sidebar, table of contents, or other explicit navigation affordance.
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.
使用WebView修饰符匹配预期的浏览体验。
实用修饰符及相关API:
webViewBackForwardNavigationGestures(_:)findNavigator(isPresented:)webViewScrollPosition(_:)webViewOnScrollGeometryChange(...)
仅在用户体验需要时才应用这些修饰符。
- 当用户可能访问多个页面时,启用前进/后退手势
- 当内容为文档类时,添加“在页面中查找”功能
- 仅当应用包含侧边栏、目录或其他明确导航控件时,同步滚动位置
苹果人机界面指南(HIG)同样适用于此场景:在合适的情况下支持前进/后退导航,但不要将应用网页视图变成通用浏览器。
Common Mistakes
常见错误
- Using wrappers by default in an iOS 26+ SwiftUI app instead of starting with
WKWebViewandWebViewWebPage - Using embedded web views for OAuth instead of
ASWebAuthenticationSession - Reaching for only after building a plain
WebPagepath that now needs state, JS, or navigation controlWebView(url:) - Treating as a direct replacement for
callJavaScriptWKScriptMessageHandler - Keeping all links inside the app when external domains should open outside the embedded surface
- Building a browser-style app shell around WebView instead of a focused embedded experience
- Using custom URL schemes for content that should just load over HTTPS
- Forgetting that is main-actor-isolated
WebPage
- 在iOS 26+的SwiftUI应用中默认使用包装器,而非优先使用
WKWebView和WebViewWebPage - 使用嵌入式网页视图处理OAuth,而非
ASWebAuthenticationSession - 仅在构建了简单的路径后,因需要状态、JavaScript或导航控制才使用
WebView(url:)WebPage - 将视为
callJavaScript的直接替代方案WKScriptMessageHandler - 所有链接都保留在应用内,而外部域名应在嵌入式视图外打开
- 围绕WebView构建浏览器风格的应用外壳,而非聚焦的嵌入式体验
- 使用自定义URL Scheme加载本应通过HTTPS加载的内容
- 忽略是主线程隔离的特性
WebPage
Review Checklist
审核清单
- and
WebVieware the default path for iOS 26+ SwiftUI web contentWebPage - is used for auth flows instead of embedded web views
ASWebAuthenticationSession - is used whenever the app needs state observation, JS calls, or policy control
WebPage - Navigation policies only intercept the URLs the app actually owns or needs to reroute
- External domains open externally when appropriate
- JavaScript return values are cast defensively to concrete Swift types
- Custom URL schemes are used only for real app-owned resources
- Back/forward gestures or controls are enabled when multi-page browsing is expected
- The web experience adds focused native value instead of behaving like a thin browser shell
- Fallback to is justified by deployment target or missing API needs
WKWebView
- 针对iOS 26+的SwiftUI网页内容,默认使用和
WebViewWebPage - 认证流程使用而非嵌入式网页视图
ASWebAuthenticationSession - 当应用需要状态观察、JavaScript调用或策略控制时,使用
WebPage - 导航策略仅拦截应用实际拥有或需要重定向的URL
- 外部域名在合适时跳转至应用外打开
- 对JavaScript返回值进行安全类型转换,转为具体的Swift类型
- 自定义URL Scheme仅用于真正的应用自有资源
- 当用户可能浏览多页面时,启用前进/后退手势或控件
- 网页体验聚焦于原生价值,而非打造简易浏览器外壳
- 降级使用的场景有合理理由(如部署版本限制或缺失API需求)
WKWebView
References
参考资料
- Loading and observation:
references/loading-and-observation.md - Navigation and JavaScript:
references/navigation-and-javascript.md - Local content and custom schemes:
references/local-content-and-custom-schemes.md - Migration and fallbacks:
references/migration-and-fallbacks.md
- 加载与观察:
references/loading-and-observation.md - 导航与JavaScript:
references/navigation-and-javascript.md - 本地内容与自定义Scheme:
references/local-content-and-custom-schemes.md - 迁移与降级方案:
references/migration-and-fallbacks.md