ARTICLE AD BOX
I'm working on a SwiftUI project that embeds a couple WKWebviews in a tabview and uses flatbuffers for serialization. I've just added the second webview which acts as sort of 'details' sheet, which should be much easier than the other that includes an entire codemirror editor, but it refuses to run the setNoteDetails function only after the first function call succeeds.
I'm getting this error:
JavaScript execution returned a result of an unsupported typeI've tried wrapping the function in an iife, emitting an event in the function instead of calling a function attache to the window, and still I can't get the view to run the function successfully only after the first one runs successfully.
Also, when I copy and paste the generated function string into the safari devtools, everything works as expected. I've even tried serializing everything as a base64 string instead of a uInt8Array, and still... no success.
The Webview Struct
public struct NoteDetailWebviewInternal: UIViewRepresentable { @State private var show: Bool = false @State private var lastNoteId: String? = nil @Environment(\.colorScheme) private var colorScheme: ColorScheme public let url: URL = Bundle.main.url( forResource: "index", withExtension: "html", subdirectory: "note_detail_webview" )! @Binding public var note: NoteModel public let container: NoteDetailWebviewContainer public init(note: Binding<NoteModel>, container: NoteDetailWebviewContainer) { self._note = note self.container = container } public func makeUIView(context: Context) -> WKWebView { let webView = container.webView webView.navigationDelegate = context.coordinator webView.isHidden = true let editorContentControllers = [ NoteDetailWebviewActions.requestNoteDetailData.rawValue, NoteDetailWebviewActions.setWebviewLoaded.rawValue, ] // if colorScheme == .dark { // webView.evaluateJavaScript( // """ // document.body.classList.add("dark") // """) // } for controllerName in editorContentControllers { addUserContentController( controller: webView.configuration.userContentController, coordinator: context.coordinator, name: controllerName ) } webView.loadFileURL(url, allowingReadAccessTo: url) return webView } public func updateUIView(_ uiView: WKWebView, context: Context) { uiView.isHidden = !show } public func makeCoordinator() -> Coordinator { Coordinator(self) } } @MainActor extension NoteDetailWebviewInternal { public final class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler { var parent: NoteDetailWebviewInternal public init(_ parent: NoteDetailWebviewInternal) { self.parent = parent } public func webView( _ webView: WKWebView, didFinish navigation: WKNavigation! ) { // On Load parent.container.webView.isHidden = !self.parent.show } public func webView( _ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error ) { print( "WebView navigation failed with error: \(error.localizedDescription)" ) } @MainActor public func userContentController( _ userContentController: WKUserContentController, didReceive message: WKScriptMessage ) { switch message.name { case NoteDetailWebviewActions.setWebviewLoaded.rawValue: self.parent.show = true case NoteDetailWebviewActions.requestNoteDetailData.rawValue: self.parent.container.setNoteDetails(note: parent.note) default: return } } } } public struct NoteDetailWebview: View { @Environment(\.colorScheme) private var colorScheme: ColorScheme @Environment(ThemeManager.self) private var themeManager: ThemeManager @AppStorage(AppStorageKeys.theme.rawValue) private var webviewTheme: WebViewTheme = .fluster @AppStorage(AppStorageKeys.webviewFontSize.rawValue) private var webviewFontSize: WebviewFontSize = .base var container: NoteDetailWebviewContainer { return NoteDetailWebviewContainer(bounce: true, scrollEnabled: true) } @Binding var note: NoteModel public init(note: Binding<NoteModel>) { self._note = note } public var body: some View { NoteDetailWebviewInternal(note: $note, container: container) .onChange( of: note, { container.setNoteDetails(note: note) } ) // .onChange( // of: note.markdown.body, // { // container.setNoteDetails(note: note) // } // ) .onChange( of: colorScheme, { container.applyWebViewColorScheme(darkMode: colorScheme == .dark) } ) .onChange( of: webviewTheme, { container.setWebviewTheme(theme: webviewTheme) } ) .onChange( of: webviewFontSize, { container.setWebviewFontSize(fontSize: webviewFontSize) } ) .onAppear { print("Did appear") // container.setNoteDetails(note: note) // container.applyWebViewColorScheme(darkMode: colorScheme == .dark) // container.setWebviewTheme(theme: webviewTheme) // container.setWebviewFontSize(fontSize: webviewFontSize) } } }The Webview container
import Combine import FlatBuffers import SwiftUI import WebKit @MainActor public final class NoteDetailWebviewContainer: WebviewContainer< NoteDetailWebviewEvents > { public func setNoteDetails(note: NoteModel) { var builder = FlatBufferBuilder(initialSize: 1024) var noteIdOffset = builder.create(string: note.id) var titleOffset = builder.create( string: note.markdown.title ?? "No title found" ) var tagVectorOffset: [Offset] = [] for t in note.tags { let x = MdxSerialization_TagResultBuffer.createTagResultBuffer( &builder, bodyOffset: builder.create(string: t.value) ) tagVectorOffset.append(x) } let citationsVectorOffset: [Offset] = [] for citation in note.citations { let citationOffset = MdxSerialization_CitationResultBuffer.createCitationResultBuffer( &builder, citationKeyOffset: builder.create( string: citation.citationKey ), bodyOffset: builder.create(string: citation.data) ) } let dateFormatter = RelativeDateTimeFormatter() dateFormatter.unitsStyle = .full dateFormatter.dateTimeStyle = .named let details = MdxSerialization_NoteDetails_NoteDetailDataBuffer .createNoteDetailDataBuffer( &builder, noteIdOffset: noteIdOffset, titleOffset: titleOffset, summaryOffset: builder.create(string: note.markdown.summary), tagsVectorOffset: builder.createVector( ofOffsets: tagVectorOffset ), citationsVectorOffset: builder.createVector( ofOffsets: citationsVectorOffset ), lastModifiedStringOffset: builder.create( string: dateFormatter.localizedString( for: note.utime, relativeTo: .now ) ), lastReadStringOffset: builder.create( string: dateFormatter.localizedString( for: note.last_read, relativeTo: .now ) ) ) builder.finish(offset: details) self.runJavascript( """ window.setNoteDetails(\(builder.sizedByteArray)); """) } }Any help would be huge and I'd be forever grateful.
