Interacting with Javascript in WKWebView

You can use the WKWebView class to embed web content in your app. To do so, create a WKWebView object, set it as the view, and send it a request to load web content. After creating a new WKWebView object using the initWithFrame:configuration: method, you need to load the web content. Use the loadHTMLString:baseURL: method to begin loading local HTML files or the loadRequest: method to begin loading web content.

In order to send a message from WKWebView to objective-c code, you would need to do add script message handler to the webview.

WKWebViewConfiguration* webConfiguration = [[WKWebViewConfiguration alloc] init];
_webview = [[WKWebView alloc] initWithFrame:CGRectZero configuration:webConfiguration];
[_webview.configuration.userContentController addScriptMessageHandler:self name:@"reloadAnalyticsViewHandler"];
[_webview.configuration.userContentController addScriptMessageHandler:self name:@"optionsCheckboxHandler"];
self.view = _webview;

In the above code, I initialized a new webview and added a script handler called reloadAnalyticsViewHandler to the webview configuration. Now the next question arises, how do I make use of this. You can send the message using the postMessage api available in javascript.

function reloadPage() {	 
window.webkit.messageHandlers.reloadAnalyticsViewHandler.postMessage("Reload page");
}
<button class=\"btn success\" onclick='reloadPage()'>Reload</button>

You can make use of the onclick method to send the message. Once this is send from javascript, you can use userContentController delegate to handle this message as shown below. Its that easy.

- (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message
{
	if([message.name isEqualToString:@"reloadAnalyticsViewHandler"]) {
		NSLog(@"body %@ name %@", message.body, message.name);
	}
	if([message.name isEqualToString:@"optionsCheckboxHandler"]) {
		NSLog(@"body %@ name %@", message.body, message.name);
	}
}

Moving on, lets consider you want to evaluate or call a javascript method inside the webview. e.g. when it loads. You can use the api evaluateJavaScript to help you with this.

[_webview evaluateJavaScript:@"fromApp();" completionHandler:nil];
function fromApp() {
window.webkit.messageHandlers.optionsCheckboxHandler.postMessage('whatsup');
}

For e.g. you can also use this to read the entire webpage.

[_webview evaluateJavaScript:@"document.documentElement.outerHTML.toString()" completionHandler:^(id _Nullable html, NSError * _Nullable error)
{
        NSString* str = html; // this contains the entire page

Lastly, webview can be used to behave like a webpage itself. You can write entire javascript functions inside the webpage, or include it as a separate file. I have pasted the sample source code here.



#import <WebKit/WebKit.h>
@interface RMWebview ()<WKScriptMessageHandler>
@property WKWebView* webview;
@property UIButton* clearButton;
@end

@implementation RMWebview

-(void)loadView
{	
	WKWebViewConfiguration* webConfiguration = [[WKWebViewConfiguration alloc] init];
	_webview = [[WKWebView alloc] initWithFrame:CGRectZero configuration:webConfiguration];
	[_webview.configuration.userContentController addScriptMessageHandler:self name:@"reloadAnalyticsViewHandler"];
	[_webview.configuration.userContentController addScriptMessageHandler:self name:@"optionsCheckboxHandler"];
	self.view = _webview;
	
	_clearButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 300, 100)];
	[_clearButton setBackgroundColor:[UIColor systemBlueColor]];
	[_clearButton setTitle:@"Clear" forState:UIControlStateNormal];
	[_clearButton addTarget:self action:@selector(clearWebView) forControlEvents:UIControlEventTouchUpInside];
	[_webview addSubview:_clearButton];
}

-(void)viewWillLayoutSubviews
{
	[_clearButton setFrame:CGRectMake(10, CGRectGetMaxY(_webview.frame) - 100, 100, 50)];
}

-(void)viewWillAppear:(BOOL)animated
{
	[self loadView];
}

-(void)viewDidAppear:(BOOL)animated {
	[_webview evaluateJavaScript:@"fromApp();" completionHandler:nil];
}
-(void) loadView
{
	NSString* head = @"<script>\
	function reloadPage() { \
		window.webkit.messageHandlers.reloadAnalyticsViewHandler.postMessage(\"Reload page\"); \
	}\
	function handleClick(clickedId) { \
		var elementId = clickedId.id + 'Text';\
		var elements = document.getElementsByClassName(elementId);\
		var names = '';\
		for(var i=0; i<elements.length; i++) {\
			if (clickedId.checked == true) {\
				elements[i].style.display = 'block';\
			} else { \
				elements[i].style.display = 'none';\
			} \
		} \
		if (clickedId.checked == true) { \
			window.webkit.messageHandlers.optionsCheckboxHandler.postMessage('elementID:'+clickedId.id); \
		} else { \
			window.webkit.messageHandlers.optionsCheckboxHandler.postMessage(\"False \"+clickedId.id); \
		}\
	}\
	function fromApp() {\
		window.webkit.messageHandlers.optionsCheckboxHandler.postMessage('whatsup');\
	}\
	</script>\
	<head>\
	<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\
	<style>\
	div.scrollmenu {\
	  background-color: white;\
	  overflow: auto;\
	  white-space: nowrap;\
	}\
	\
	.btn {\
	  font-size: 20px;\
	  border: none;\
	  color: white;\
	  padding: 14px 20px;\
	}\
	\
	.success { \
		background-color: #4CAF50;\
	}\
	</style>\
	</head>";
	[_webview loadHTMLString:[NSString stringWithFormat:@"<html>%@<body><div class=\"scrollmenu\">\
							  <button class=\"btn success\" onclick='reloadPage()'>Reload</button>\
							  <input type=\"checkbox\" checked=\"checked\" onclick='handleClick(this)' id='showDates'>Date\
							  <input type=\"checkbox\" checked=\"checked\" onclick='handleClick(this)' id='showGuid'>User ID\
							  </div>\
							  <div>event type was here <div class='showGuidText'>internal user id</div></div>\
							  <div class='showDatesText'>1showDates</div><div class='showDatesText'>2showDates</div><div class='showDatesText'>3showDates</div><div class='showGuidText'>Guid</div>3hello</body></html>",head] baseURL:nil];
}

-(void)clearWebView
{
	[_webview loadHTMLString:@"<html></html>" baseURL:nil];
}

- (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message
{
	if([message.name isEqualToString:@"reloadAnalyticsViewHandler"]) {
		NSLog(@"body %@ name %@", message.body, message.name);
	}
	if([message.name isEqualToString:@"optionsCheckboxHandler"]) {
		NSLog(@"body %@ name %@", message.body, message.name);
	}
}
@end

Leave a Reply

Your email address will not be published. Required fields are marked *