Tuesday, December 5, 2017

JavaScript not executed in WKWebView

Leave a Comment

Since I'm going a bit crazy with this one, I've decided to give it another try and post about it here.

So...

I have a simple Swift/Cocoa application with a WKWebView in it.

I load a local HTML file (which - along with the rest of the .css/.js dependencies - is being copied to the bundle inside a /web folder).

Here's the complete code:

<!DOCTYPE html> <html>     <head>         <title>IBAN Validator</title>         <meta name="viewport" content="width=device-width"/>         <meta charset="UTF-8">          <link href="style/font-awesome.min.css" type="text/css" rel="stylesheet"/>         <link href="style/electriq.css" type="text/css" rel="stylesheet"/>         <link href="style/custom.css" type="text/css" rel="stylesheet" />     </head>     <body>         <!-- window/ -->         <div class="window">             <div class="content" style="text-align: center">                 <div class="panel">                     <input id="iban" type="text" style="text-align:center;"><br/>                     <div style="position: relative; max-width: 150px; width: 100%; margin: 0 auto">                         <a id="validateButton" href="#" class="button" style="width:150px;">Validate</a>                         <span id="resultValid" style="position:absolute; left: calc(100% + 20px); top: 10%; color: green; font-size: 20px; display:none;"><i class="fa fa-check"></i></span>                         <span id="resultInvalid" style="position:absolute; left: calc(100% + 20px); top: 10%; color: red; font-size: 20px; display:none;"><i class="fa fa-close"></i></span>                     </div>                 </div>             </div>         </div>         <!-- /window -->          <div id="loader_overlay" style="padding-top:10%">             <i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i><br/>         </div>          <!-- scripts/ -->         <script>if (typeof module === 'object') {window.module = module; module = undefined;}</script>         <script src="jquery.min.js" type="text/javascript"></script>         <script>             if (typeof window.jQuery !== 'undefined') {                 window.document.getElementById("loader_overlay").innerHTML += ".";             } else {                 window.document.getElementById("loader_overlay").innerHTML += "x";             }         </script>         <script src="handlebars.min.js" type="text/javascript"></script>         <script>             if (typeof window.Handlebars !== 'undefined') {                 window.document.getElementById("loader_overlay").innerHTML += ".";             } else {                 window.document.getElementById("loader_overlay").innerHTML += "x";             }         </script>         <script src="bridgecommander.js" type="text/javascript"></script>         <script>             if (typeof window.BridgeCommander !== 'undefined') {                 window.document.getElementById("loader_overlay").innerHTML += ".";             } else {                 window.document.getElementById("loader_overlay").innerHTML += "x";             }         </script>         <script src="iban.js" type="text/javascript"></script>         <script>             if (typeof window.IBAN !== 'undefined') {                 window.document.getElementById("loader_overlay").innerHTML += ".";             } else {                 window.document.getElementById("loader_overlay").innerHTML += "x";             }         </script>         <!-- <script src="app.js" type="text/javascript"></script> -->         <script>              // Generated by CoffeeScript 2.0.2             var doValidate;              window.appLoaded = true;             window.document.getElementById("loader_overlay").innerHTML += ".";             BridgeCommander.call("echo", "Before: onload");             window.document.getElementById("loader_overlay").innerHTML += ".";              window.onload = function() {               BridgeCommander.call("echo", "Inside: onload");               document.getElementById("loader_overlay").style.display = 'none';               return $("#validateButton").on("click", doValidate);             };             window.document.getElementById("loader_overlay").innerHTML += ".";             BridgeCommander.call("echo", "After: onload");              doValidate = function() {               var iban, valid;               iban = $("#iban").val();               valid = IBAN.isValid(iban);               if (valid) {                 $("#resultValid").show();                 $("#resultInvalid").hide();                 $("#validateButton").removeClass("invalid").addClass("valid");                 BridgeCommander.call("echo", `Validating: ${iban}, Result: valid`);               } else {                 $("#resultValid").hide();                 $("#resultInvalid").show();                 $("#validateButton").removeClass("valid").addClass("invalid");                 BridgeCommander.call("echo", `Validating: ${iban}, Result: invalid`);               }               setTimeout(function() {                 $("#validateButton").removeClass("valid").removeClass("invalid");                 $("#resultValid").hide();                 return $("#resultInvalid").hide();               }, 3000);               return false;             };             window.document.getElementById("loader_overlay").innerHTML += ".";              if (typeof window.appLoaded !== 'undefined') {                 window.document.getElementById("loader_overlay").innerHTML += ".";             } else {                 window.document.getElementById("loader_overlay").innerHTML += "x";             }         </script>         <script>if (window.module) module = window.module;</script>         <!-- /scripts -->     </body>  </html> 

Important Note: Here (meaning on my Mac - and everyone's Mac with 10.3.1 I've tried this on) everything works fine. When I upload the exact same binary to the App Store for review, I keep getting the same "error" screenshot, signifying none of the code within my last <script></script> block gets executed. (after the window.appLoaded = true part).


What could be going on? I've literally tried anything to debug this (hence, the numerous window.document.getElementById thing, adding dots to make sure everything worked), but still nothing.

As you can see, I'm loading several scripts (which according to my tests load fine), and I also have several pieces of inline JS code (which still work fine). Except for the last one! (which, no matter what, even from an external file, seems to refuse to load...)

Again, I thought about sth being cached, I don't know, but I remind you that it - apparently -- works everywhere apart from the Review team's machine :S

Any idea would be welcome!


P.S. In case something is not clear, please feel free to ask me anything


Update: (28/11/2017) Tried the whole thing with a simple - old-style - WebView (in case it had to do with the WKWebView) and still my app gets rejected. Or to be precise, my app (exact version, same everything) runs fine everywhere, except for the guy that reviews it.

1 Answers

Answers 1

This could be because the browser does not know how to parse the contents of the script tags

<script></script> tags require the type attribute on them most browsers will assume it's the same as the last one but as none of your code containing script tags specifies the type it might not know how to parse them the script support more than javascript for example VBScript to whenever you open a <script> for javascript it should be <script type="text/javascript">

The other problem could be the window.onload I would recommend you change it to the DOMContentLoaded event and use a closure to make sure it executes the code.

More so why are you not loading jQuery in the head tag where it's supposed to be loaded move <script src="jquery.min.js" type="text/javascript"></script> to inside the <head></head>.

Following on from this if you have jQuery why are you mixing jQuery and pure Javascript if you have jQuery use it's smaller code and cleaner

 (function($){      $(function(){          window.appLoaded = true;          $("loader_overlay").append(".");          BridgeCommander.call("echo", "Before: onload");          $("loader_overlay").append(".");           $("loader_overlay").append(".");          BridgeCommander.call("echo", "After: onload");           function doValidate() {              var iban, valid;              iban = $("#iban").val();              valid = IBAN.isValid(iban);              if (valid) {                  $("#resultValid").show();                  $("#resultInvalid").hide();                  $("#validateButton").removeClass("invalid")                                      .addClass("valid");                  BridgeCommander.call("echo", `Validating: ${iban}, Result: valid`);              } else {                  $("#resultValid").hide();                  $("#resultInvalid").show();                  $("#validateButton").removeClass("valid")                                      .addClass("invalid");                  BridgeCommander.call("echo", `Validating: ${iban}, Result: invalid`);              }               setTimeout(function() {                  $("#validateButton").removeClass("valid")                                      .removeClass("invalid");                  $("#resultValid").hide();                  return $("#resultInvalid").hide();              }, 3000);              return false;         };           (function(doValidate) {              BridgeCommander.call("echo", "Inside: onload");              $("loader_overlay").css("display",'none');              return $("#validateButton").on("click", doValidate);          })(doValidate);           window.document.getElementById("loader_overlay").innerHTML += ".";           if (typeof window.appLoaded !== 'undefined') {              window.document.getElementById("loader_overlay").innerHTML += ".";          } else {              window.document.getElementById("loader_overlay").innerHTML += "x";          }          if (window.module){ module = window.module; }     }); }); 

On another note please get rid of all your file loading checks. so all of the following code blocks

if (typeof window.Handlebars !== 'undefined') {     window.document.getElementById("loader_overlay").innerHTML += "."; } else {     window.document.getElementById("loader_overlay").innerHTML += "x"; } 

They are only needed for debugging and you know it's Web Kit if the file loads on one it loads on all. so you don't need these check's they are just using phone processing power and adding work to your app for no good reason. other than to put a . in the overlay...

and again all <script src=... should be inside the <head> tags, in this case, all of your code should be inside the script tags and using jQuery read as my above version does.

How are you testing this App on the Mac are you using the iPhone emulator? or a real iPhone to test i would always recommend the latter. and have you tested in on one of these as you don't seem to say you have, if not get a device registered for testing on your developer account create the keys to test and build a test version then use Safari or chromes remote debugging tools on the WebView and make sure it all works.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment