'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];
}
//...
Solution 1:[1]
I solved it and problem was that if you set userContentController
to a new object, that userContentController
's WKScriptMessageHandler
s 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")
Solution 2:[2]
The window.webkit namespace only appears in webview with script message handlers. Make sure that you have called addScriptMessageHandler method of WKUserContentController.
Solution 3:[3]
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.
Solution 4:[4]
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);
}
Solution 5:[5]
The solution for me at the end worked enabling the javascript on the webview with
webview?.configuration.preferences.javaScriptEnabled = true
Solution 6:[6]
According apple's documentation:
https://developer.apple.com/documentation/webkit/wkusercontentcontroller
You need to create your userContentController, and add script message handlers
userContentController = WKUserContentController()
userContentController.addScriptMessageHandler(foo, name: "foo")
BEFORE creating the webview
let configuration = WKWebViewConfiguration()
configuration.userContentController = userContentController
let webview = WKWebView(frame: .zero, configuration: configuration)
doing it in the AFTER would result in error like:
undefined is not an object (evaluating 'window.webkit.messageHandlers.foo.postMessage')
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | keaukraine |
Solution 2 | soflare |
Solution 3 | David Anderson |
Solution 4 | |
Solution 5 | David Santana |
Solution 6 | Dylan Liu |