Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WkWebKit - javascript on loaded page finds window.webkit is undefined

I'm experimenting with WkWebKit talking back and forth between app and page. I can get javaScript to execute fine using WkWebView evaluateJavascript method, but when I try to execute window.webkit.messageHandlers.myHandler.postMessage('hello world!') on the JavaScript page, I find that window.webkit is not defined.

Odd... I'm running in a simulator of iPad with ios 8.4. I thought this was available in original version 8, no?

I can't find anyone else posting about this, so perhaps I've done something wrong?

I've even attached my Safari Developer to the simulator's browser, and in the console I try to see what window.webkit is, and sure enough, it does not exist.

Note that I add an initial script to run when the page loads (I see this in the javascript editor - the message is logged). And I add a script message handler as well...

[EDIT: Adding more code details here] Here is my code:

- (void)viewDidLoad {
  [super viewDidLoad];
  // Do any additional setup after loading the view, typically from a nib.
  NSLog(@"main view Controller viewDidLoad called...");

// if we are running on an OLD ios (pre v8) WKWebView will not exist.  So don't create it if it doesn't exist...
if (NSClassFromString(@"WKWebView")) {
    // WKWebView cannot be dragged onto storyboard, so have to create it manually here.
    // We have a "ContainerView" called _webContainer that this browser view will live inside
    // First we create a web view configuration object...
    WKWebViewConfiguration *wbConfig = [WKWebViewConfiguration alloc];
    wbConfig.mediaPlaybackAllowsAirPlay = true;
    wbConfig.mediaPlaybackRequiresUserAction = false; 
    // inject some Javascript into our page before it even loads...
    NSString *scriptSource = @"console.log('Hi from the iOS app hosting this page...'); window.hostedByWkWebView=true;";
    WKUserScript *userScript = [[WKUserScript alloc]
                                initWithSource:scriptSource
                                injectionTime:WKUserScriptInjectionTimeAtDocumentStart
                                forMainFrameOnly:YES];

    [wbConfig.userContentController addUserScript:userScript];
    [wbConfig.userContentController addScriptMessageHandler:self name:@"myHandler"]; // javascript to use this would be: window.webkit.messageHandlers.myHandler.postMessage


    // Ok, the config is created, now create the WkWebView instance, passing in this config...
    _webView = [[WKWebView alloc] initWithFrame: [_webContainer bounds] configuration:wbConfig];
    _webView.autoresizesSubviews = true;
    _webView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
    _webView.navigationDelegate = self;

    // Add the web view to the container view, and tell the container to automatically resize its subviews, height and width.
    [_webContainer addSubview:_webView];
    _webContainer.autoresizesSubviews = true;
    _webContainer.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;

    // Set the URL for the webview and navigate there...
    NSString *fullURL = @"https://myurlgoeshere.com";

    NSURL *url = [NSURL URLWithString:fullURL];
    NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];

    [_webView loadRequest:requestObj];
}
else {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Cannot create Web View" message:@"The web view used requires iOS 8 or higher.  Sorry." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:@"", nil];
    [alert show];
}

//...
like image 776
Brian B Avatar asked Sep 24 '15 21:09

Brian B


5 Answers

I solved it and problem was that if you set userContentController to a new object, that userContentController's WKScriptMessageHandlers will not be registered correctly in Apple's internal code:

WKUserContentController *userContentController = [WKUserContentController new];
userContentController.addScriptMessageHandler(self, name: "jockey")
userContentController.addScriptMessageHandler(self, name: "observe")
webView.configuration.userContentController = userContentController

fixed by using the already instantiated userContentController by Apple:

webView.configuration.userContentController.addScriptMessageHandler(self, name: "jockey")
webView.configuration.userContentController.addScriptMessageHandler(self, name: "observe")
like image 181
Pascal Kaufmann Avatar answered Oct 04 '22 23:10

Pascal Kaufmann


The window.webkit namespace only appears in webview with script message handlers. Make sure that you have called addScriptMessageHandler method of WKUserContentController.

like image 20
soflare Avatar answered Oct 04 '22 23:10

soflare


I ran into this SO because I was experiencing the same issue and this is how I solved it using a custom CustomContentController (subclass of WKUserContentController) instance.

let userContentController = CustomContentController()
let webViewConfiguration = WKWebViewConfiguration()
webViewConfiguration.userContentController = userContentController
webView = WKWebView(frame: .zero, configuration: webViewConfiguration)

In my case the CustomContentController is a subclass of WKUserContentController in which the add(_ scriptMessageHandler: WKScriptMessageHandler, name: String) method is called, but I don't believe that is significant.

I believe that the WKUserContentController must be instantiated and applied to a WKWebViewConfiguration before the WKWebView is initialized via WKWebView(frame: .zero, configuration: webViewConfiguration)

If the WKWebView has been created and then you try change the WKWebViewConfiguration you will encounter window.webkit not being available in the JSContext.

like image 39
David Anderson Avatar answered Oct 05 '22 00:10

David Anderson


I met the same problem. And after wasting 2 hours, I found this below to work fine. But I don't know why.

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    WKUserContentController *userCctrl = [[WKUserContentController alloc] init];
    [userCctrl addScriptMessageHandler:self name:@"jscall"];
    WKWebViewConfiguration *wbConfiger = [[WKWebViewConfiguration alloc] init];
    wbConfiger.userContentController = userCctrl;
    CGSize size = [UIScreen mainScreen].bounds.size;
    WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 20, size.width, size.height - 20)  configuration:wbConfiger];
    [self.view addSubview:webView];
    webView.UIDelegate = self;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [webView evaluateJavaScript:@"if (window.webkit === undefined) { alert('未注册的 window.webkit'); } else { window.webkit.messageHandlers.jscall.postMessage({title:'标题'}); }" completionHandler:^(id obj, NSError *error) {
            NSLog(@"run js completion with error = %@", error);
    }];
    });
}
#pragma mark -
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
    NSLog(@"run js alert panel message = %@", message);
    completionHandler();
}
#pragma mark -
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    NSLog(@"wkwebview run javascript name = %@, body = %@", message.name, message.body);
}
like image 32
shaojunx Avatar answered Oct 04 '22 23:10

shaojunx


The solution for me at the end worked enabling the javascript on the webview with

webview?.configuration.preferences.javaScriptEnabled = true
like image 24
David Santana Avatar answered Oct 05 '22 01:10

David Santana