iOS Question [Solved] WKWebview - messages from Javascript to B4I without _OverrideURL

roumei

Active Member
Licensed User
I'm using a WKWebview to display 3D models with Three.js. Calling Javascript functions from B4I is simple with Webview.EvaluateJavaScript and works flawlessly.

It is recommended to use OverrideURL to send messages the other way around from Javascript to B4I. While this works, it is too slow for my use case. I'd like to get the 3D orientation of the model at 60 Hz. In B4A I use warwound's WebViewExtras.addJavascriptInterface for that purpose.

Emme Developer posted a very interesting solution which uses the Webview's userController: WKWebview custom js
This solution works great for sending messages in 'realtime' but the standard events are not raised anymore.

Does anyone have an idea how to use Emme Developer's solution and keep the standard events (most importantly _PageFinished and _JSComplete) at the same time?

I tried to combine the userController with the navigationDelegate from Erel's post here but I couldn't get the events to be raised.
 

roumei

Active Member
Licensed User
Thank you very much! I added a regular WebView (iUI8) and your code enables the sending of messages via the userController and the standard events are now also fired.
The problem now is that the page is no longer displayed although the PageFinished event is fired with Success = True and the correct URL.
If I remove the line:
B4X:
WebView1.InitializeWithCustomConfiguration("WebView1", conf)
the page is displayed, but of course, the JS callback doesn't work.

I attached a small project that demonstrates the issue. I tried with different webpages and also with a local HTML file but none of these pages are displayed. Here's part of the code of the attached example:
B4X:
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")

    Dim conf As NativeObject
    conf = conf.Initialize("WKWebViewConfiguration").RunMethod("new", Null)
    Dim NativeMe As NativeObject = Me
    conf = NativeMe.RunMethod("getconfig:", Array(conf))
    
    ' With this line, JSCallback works but the page is not displayed (although PageFinished fires with the correct URL and Success = True)
    ' Without this line, the page is displayed but JSCallback can't work
    WebView1.InitializeWithCustomConfiguration("WebView1", conf)
        
    WebView1.LoadUrl("https://get.webgl.org/")
    'WebView1.LoadUrl("https://www.google.com/")
    
End Sub

Private Sub WebView1_PageFinished (Success As Boolean, Url As String)
    Log("PageFinished: " & Success & " " & Url)
End Sub

Private Sub WebView1_OverrideUrl (Url As String) As Boolean
    Log("OverrideUrl: " & Url)
    Return False
End Sub

Private Sub WebView1_JSComplete (Success As Boolean, Result As String)
    Log("JSComplete " & Success)
End Sub

Private Sub JSCallback(body As Object) 'ignore
    Log("JSCallback: " & body)
End Sub

Private Sub Button1_Click
    WebView1.EvaluateJavaScript("window.webkit.messageHandlers.callback.postMessage('postMessage successful " & DateTime.Now & "');")
End Sub

#If OBJC
#import <Foundation/Foundation.h>

- (WKWebViewConfiguration *) getconfig: ( WKWebViewConfiguration *)config
{
       WKUserContentController* userController = [[WKUserContentController alloc]init];
       [userController addScriptMessageHandler:self name:@"callback"];
    config.userContentController = userController;
 
  return config;
}

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message     {
if ([message.name isEqualToString:@"callback"]) {
        [self.bi raiseEvent:nil event:@"jscallback:"params:@[message.body]];
}
}
#End If
 

Attachments

  • TestWebViewJSCallback.zip
    9.7 KB · Views: 58
Upvote 0

Semen Matusovskiy

Well-Known Member
Licensed User
It seems to me a problem is inside InitializeWithCustomConfiguration method.
Log shows zero frame for WebView1. That's why a webpage is invisible.

If to use initWithFrame:configuration: method with correct frame, all works fine. But in this case we need to set navigationDelegate to handle events (not very comfortable).
 
Upvote 0

Johan Hormaza

Well-Known Member
Licensed User
Apple no longer accepts WebView for being very obsolete, yesterday I was rejected because WebView is obsolete and what I should use is WKWebview

1615576931508.png
 
Upvote 0

roumei

Active Member
Licensed User
Many thanks to you all! Based on Erel's code and Semen's tip about the WebView's frame I found a work-around: removing the WebView programmatically and adding it again in B4XPage_Created solves the issue with the page not being displayed. The standard events are firing and the JavaScript callbacks work too. I attached the updated test project in case anyone is interested.
B4X:
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")
    
    Dim conf As NativeObject
    conf = conf.Initialize("WKWebViewConfiguration").RunMethod("new", Null)
    Dim NativeMe As NativeObject = Me
    conf = NativeMe.RunMethod("getconfig:", Array(conf))
    WebView1.InitializeWithCustomConfiguration("WebView1", conf)
    
    ' Work-around: removing the webview and adding it again to a panel fixes
    ' the issue with the pages not being displayed
    Panel1.RemoveAllViews
    Panel1.AddView(WebView1, 0, 0, Panel1.Width, Panel1.Height)
            
    WebView1.LoadUrl("https://get.webgl.org/")
    'WebView1.LoadUrl("https://www.google.com/")
    
End Sub
 

Attachments

  • TestWebViewJSCallback.zip
    9.8 KB · Views: 65
Upvote 0
Top