Fix iOS WebKit crash by shipping external app.js instead of inlined HTML.
Stop embedding the 1.4MB bundle in index.html to avoid std::overflow_error on launch, validate the iOS asset build, and surface load failures in the native shell. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6,6 +6,7 @@ final class WebViewStore: ObservableObject {
|
||||
@Published var isLoading = true
|
||||
@Published var canGoBack = false
|
||||
@Published var pageTitle = "WordLoop"
|
||||
@Published var loadError: String?
|
||||
weak var webView: WKWebView?
|
||||
var schemeHandler: BundleWebSchemeHandler?
|
||||
}
|
||||
@@ -92,26 +93,23 @@ struct WordLoopWebView: UIViewRepresentable {
|
||||
loadedEntryURL = entryURL
|
||||
loadedAPIBaseURL = apiBaseURL
|
||||
pendingAPIBaseURL = apiBaseURL
|
||||
store.isLoading = true
|
||||
Task { @MainActor in
|
||||
store.isLoading = true
|
||||
store.loadError = nil
|
||||
}
|
||||
|
||||
if usesBundledFrontend, let bundleEntry = Self.bundledHTMLPayload() {
|
||||
// loadHTMLString 比 wordloop:// 自定义协议更可靠(WKWebView 对内联/外链脚本执行更稳定)
|
||||
webView.loadHTMLString(bundleEntry.html, baseURL: bundleEntry.baseURL)
|
||||
if usesBundledFrontend,
|
||||
let wwwRoot = ServerSettings.bundledWWWDirectory,
|
||||
let indexURL = ServerSettings.bundledHomeURL {
|
||||
let readAccess = ServerSettings.bundledReadAccessURL() ?? wwwRoot
|
||||
webView.loadFileURL(indexURL, allowingReadAccessTo: readAccess)
|
||||
} else if let remoteURL = ServerSettings.homeURL(for: apiBaseURL) {
|
||||
webView.load(URLRequest(url: remoteURL, cachePolicy: .reloadIgnoringLocalCacheData))
|
||||
} else {
|
||||
webView.load(URLRequest(url: entryURL, cachePolicy: .reloadIgnoringLocalCacheData))
|
||||
}
|
||||
}
|
||||
|
||||
private static func bundledHTMLPayload() -> (html: String, baseURL: URL)? {
|
||||
guard let wwwRoot = ServerSettings.bundledWWWDirectory,
|
||||
let indexURL = ServerSettings.bundledHomeURL,
|
||||
let html = try? String(contentsOf: indexURL, encoding: .utf8),
|
||||
!html.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
return (html, wwwRoot)
|
||||
}
|
||||
|
||||
func updateAPIBase(_ apiBaseURL: String, in webView: WKWebView) {
|
||||
guard loadedAPIBaseURL != apiBaseURL else { return }
|
||||
loadedAPIBaseURL = apiBaseURL
|
||||
@@ -126,26 +124,43 @@ struct WordLoopWebView: UIViewRepresentable {
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
||||
store.isLoading = true
|
||||
store.canGoBack = webView.canGoBack
|
||||
Task { @MainActor in
|
||||
store.isLoading = true
|
||||
store.canGoBack = webView.canGoBack
|
||||
}
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
|
||||
store.isLoading = false
|
||||
store.canGoBack = webView.canGoBack
|
||||
Task { @MainActor in
|
||||
store.isLoading = false
|
||||
store.canGoBack = webView.canGoBack
|
||||
store.loadError = error.localizedDescription
|
||||
}
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
|
||||
store.isLoading = false
|
||||
store.canGoBack = webView.canGoBack
|
||||
Task { @MainActor in
|
||||
store.isLoading = false
|
||||
store.canGoBack = webView.canGoBack
|
||||
store.loadError = error.localizedDescription
|
||||
}
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
||||
store.isLoading = false
|
||||
store.canGoBack = webView.canGoBack
|
||||
store.pageTitle = webView.title ?? "WordLoop"
|
||||
if let apiBaseURL = pendingAPIBaseURL {
|
||||
updateAPIBase(apiBaseURL, in: webView)
|
||||
Task { @MainActor in
|
||||
store.isLoading = false
|
||||
store.canGoBack = webView.canGoBack
|
||||
store.pageTitle = webView.title ?? "WordLoop"
|
||||
if let apiBaseURL = pendingAPIBaseURL {
|
||||
updateAPIBase(apiBaseURL, in: webView)
|
||||
}
|
||||
}
|
||||
webView.evaluateJavaScript("document.getElementById('app')?.innerHTML?.length ?? 0") { [weak self] result, _ in
|
||||
let appLen = (result as? NSNumber)?.intValue ?? 0
|
||||
guard appLen <= 0 else { return }
|
||||
Task { @MainActor in
|
||||
self?.store.loadError = "页面未渲染,请删除 App 后重新安装"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,7 +212,24 @@ struct WordLoopWebContainer: View {
|
||||
)
|
||||
.ignoresSafeArea(edges: .bottom)
|
||||
|
||||
if store.isLoading {
|
||||
if let loadError = store.loadError {
|
||||
VStack(spacing: 12) {
|
||||
Text("页面加载失败")
|
||||
.font(.headline)
|
||||
Text(loadError)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
Button("重新加载") {
|
||||
store.loadError = nil
|
||||
store.webView?.reload()
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
}
|
||||
.padding(24)
|
||||
.background(.ultraThinMaterial)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||
} else if store.isLoading {
|
||||
ProgressView("加载中…")
|
||||
.padding(20)
|
||||
.background(.ultraThinMaterial)
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user