8

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.

Dr.Kameleon
  • 22,532
  • 20
  • 115
  • 223
  • Can you check if the binary contains all the required resources? – Puneet Sharma Nov 20 '17 at 08:51
  • 1
    Is it relevant to include the error screenshot you mention? On a side note, I usually see that the reviews are done on iPads (in compatibility mode if the app is not universal), so make sure you've tested on them too. – paulvs Nov 20 '17 at 09:43
  • @PuneetSharma You sure have a point. But I have the binaries (namely the binaries within the archives I've been uploading) and it's all there – Dr.Kameleon Nov 20 '17 at 10:42
  • 1
    @paulvs The screenshot is just a loading icon with **4 dots** below it. Meaning the debugging stops after printing these four dots. Also, the app is a macOS one. – Dr.Kameleon Nov 20 '17 at 10:44
  • Are you sure you are covering all the edge cases with your tests? It's strange that all tests pass and everything works fine locally and everything and the appReview gusy having problem... Maybe some security problem? – Dominik Bucher Dec 04 '17 at 01:04
  • 1
    You should add the tag osx or macOS-sierra o high-sierra if your code must work only to one of these OS. It's really hard to reproduce your bug with your few informations. What version of Swift you use? Also, what is BridgeCommander?? It's a third part library? People should be able to reproduce your bug. – Alessandro Ornano Dec 04 '17 at 08:45
  • Reviewers often use iPads, even for iPhone apps. Test it on an iPad as well. Does it use http somewhere? – Lucas van Dongen Dec 05 '17 at 14:37
  • 1
    create external javascript files. you're better off, more often than not, finding cached .js files than cached .html files. –  Dec 05 '17 at 15:56
  • Guys, apologies! It should have been tagged with `macOS` or sth - you're right. (I included it in the text and thought it'd suffice... oh well...) – Dr.Kameleon Dec 09 '17 at 11:18
  • @AlessandroOrnano BridgeCommander is a little useful library to "bridge" the Swift backend with JS: https://github.com/tmarkovski/BridgeCommander – Dr.Kameleon Dec 09 '17 at 11:19
  • @Programmer This is an interesting input... (In my case, it's pretty much that I was just so desperate, that I ended up trying anything...) – Dr.Kameleon Dec 09 '17 at 11:21
  • @Dr.Kameleon Desperation leads to perspiration by design meanwhile javascript is no different when execution is an ordered concern. Javascript loads in order when external files are concerned otherwise it has no other way and defaults to "asynchronously" based on browser. See this article explaining the nuances, https://stackoverflow.com/questions/8996852/load-and-execute-order-of-scripts –  Dec 11 '17 at 20:58

1 Answers1

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.

Barkermn01
  • 6,781
  • 33
  • 83
  • Martin, thanks a lot for the answer. However, most of the cases you mention have already been tested. The debugging part is there to help when the guys from the review team send me their screenshot (a.k.a. that it's not working) - so I'm printing a few... harmless dots and x's to make something out of it. P.S. The app in question is not an iOS one. It's for macOS. – Dr.Kameleon Dec 09 '17 at 11:11
  • @Dr.Kameleon ok, you should still be able to turn on the remote debugging so you can connect the WebKit inspector to your app. does the Test app work correctly on your system if you run it from XCode then? If this is WebView only app app it might be worthwhile trying Cordova and wrapping your bridge code in a wrapper for Cordova it might just behave. – Barkermn01 Dec 11 '17 at 12:52