I’d like to use webview with html and javascript to render something. Below is the complete code to reproduce the problem. I expect TestViewPreview to first render text “hello testview”, and after 5 seconds it renders “hello2 testview”. However, only the 2nd one is rendered. For the first one it gives some error not sure if related:
evaluateJavaScript: render(‘hello testview’); TestView.updateUIView
error: Error Domain=WKErrorDomain Code=4 “A JavaScript exception
occurred” UserInfo={WKJavaScriptExceptionLineNumber=0,
WKJavaScriptExceptionMessage=TypeError: undefined is not a function,
WKJavaScriptExceptionColumnNumber=0, NSLocalizedDescription=A
JavaScript exception occurred}
Could you please take a look at the code below?
struct MyState {
var text: String = "abc"
}
struct TestView: UIViewRepresentable {
let htmlPath: String
@Binding var state: MyState
func makeUIView(context: Context) -> WKWebView {
if let htmlPath = Bundle.main.path(forResource: htmlPath, ofType: "html") {
do {
let htmlString = try String(contentsOfFile: htmlPath)
WebViewHelper.webView.loadHTMLString(htmlString, baseURL: Bundle.main.bundleURL)
WebViewHelper.webView.scrollView.showsHorizontalScrollIndicator = true
print("TestView.makeUIView: Finished inital loading.")
} catch {
print("TestView.makeUIView: Error loading HTML file: (error)")
}
}
return WebViewHelper.webView
}
func updateUIView(_ webView: WKWebView, context: Context) {
let script = "render('(state.text)');"
print("evaluateJavaScript: (script)")
webView.evaluateJavaScript(script) { _, error in
if let error = error {
print("TestView.updateUIView error: (error)")
}
}
}
}
struct TestViewPreview: View {
@State private var state = MyState(text: "hello testview")
var body: some View {
TestView(htmlPath: "test_html", state: self.$state)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
state.text = "hello2 testview"
}
}
}
}
#Preview {
TestViewPreview()
}
test_html.html
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id="output"></div>
<script>
function render(text) {
document.getElementById("output").innerHTML = text
}
</script>
</body>
</html>