tl;dr
do
WKUserContentController* ucc = self.webView.configuration.userContentController;
[ucc addScriptMessageHandler:self name:@"serverTimeCallbackHandler"]; // add
[ucc removeScriptMessageHandlerForName:@"serverTimeCallbackHandler"]; // remove
[ucc addScriptMessageHandler:self name:@"serverTimeCallbackHandler"]; // add
...and
window.webkit.messageHandlers.serverTimeCallbackHandler.postMessage(<messageBody>)
will not be called from JavaScript.description
I faced with issue while using
WebKit.framework
. It is either framework's fault or inappropriate usage of it.I have a
.js
file which interacts with a webpage. At the end of its interaction, the script calls window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
(function() {
var timeZoneCoockieName = 'cTz';
function getServerTimeWithTimeZone() {
PageMethods.GetServerTime(function(time) {
var serverTime = time;
var timezone = getCookie(timeZoneCoockieName);
var message = {'time': serverTime, 'timezone': timezone};
window.webkit.messageHandlers.serverTimeCallbackHandler.postMessage(message);
});
}
function getCookie(name) {
var value = "; " + document.cookie;
var parts = value.split("; " + name + "=");
if (parts.length == 2) return parts.pop().split(";").shift();
}
getServerTimeWithTimeZone();
})();
Basically, the script calls a page method, to retrieve time from the server and looks up for time zone stored in cookies upon the callback.
The native component is designed to wrap this call and expose convenient API to retrieve date/time zone info.
- (void)getServerTimeWithCompletion:((^)(id, NSError *))completion {
// Adding same message handler twice, leads to an exception.
// Removing message handler with the same name, just in case.
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"serverTimeCallbackHandler"];
[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"serverTimeCallbackHandler"];
self.getServerTimeCompleteBlock = completion;
NSString * jsPath = [[NSBundle mainBundle] pathForResource:@"GetServerTime" ofType:@"js"];
if (![jsPath isBlank]) {
NSError* encodingError = nil;
NSString* jsContent = [NSString stringWithContentsOfFile:jsPath
encoding:NSUTF8StringEncoding
error:&encodingError];
if (!encodingError) {
[self.webView evaluateJavaScript:jsContent completionHandler: ^(id _Nullable result, NSError * _Nullable evalError) {
if (evalError) {
if (completion) { completion(nil, evalError); }
}
}];
} else {
if (completion) { completion(nil, encodingError); }
}
}
else {
if (completion) { completion(nil, nil); }
}
}
Now, if this method is called twice by a user, second time the handler is not called from JavaScript.
Web Debugger attached to the app's web view, logs the error:
InvalidAccessError: DOM Exception 15: A parameter or an operation was not supported by the underlying object.