Thursday, February 18, 2016

iOS 9 blocks while loading resources

Update: Only happens using WiFi.
Update: Might not happen with 9.3 Beta (to be confirmed).

In devices running iOS 9+ (until iOS 9.2.1 - latest at the time of this writing), Apple changed the way web applications load resources (JavaScript, images, CSS, fonts). While retrieving all page resources, the browser "stalls" for around 10-30 seconds. This affects both Safari and applications using Web Views (webkit). This is non-deterministic and there are several ways to reproduce the problem (refer to some more examples at the end of the post), but here goes a simple and curious one:

Create an HTML page with the following content (you can open the HTML here or try it live here):

(click to enlarge)

Name it index.html. Place 6 images in the same folder as index.html. The images can be 1x1 pixel (same image, just different filenames). Create an empty file and name it EmptyJavaScript.js (if you want, put an alert or some JS code in it, but empty file will reproduce the problem). Place it in the same folder. Now host the page on any webserver you like (I tried IIS, WebLogic, JBoss and Apache). Open the URL in Safari and "flop":

iPhone Stalled
Image using an iPhone running iOS 9.2.1

Same problem happens with an iPad:

iPad Stalled
Image using an iPad running iOS 9.2.0

"Stalled". Exactly 30 seconds (it's local). If we inspect the network timeline, it's pretty obvious:

iPad Stalled
Already going for a hang here

iPad Stalled
30 seconds to load 6 resources

After the "block" it loads all resources and we have our page displayed:

iPhone Stalled

You might be thinking: "but... all those sites out there with dozens of resources don't stall". Well, yes. As I said, this is non-deterministic and I was unable to find the pattern, but some suffer from this issue, others don't. Some apps with webviews will suffer, others won't. My goal was to reduce the issue to the simplest example I could find. I can't figure out the rationale behind this behavior. I imagine it was either a security issue (some edge case they tried to solve) or just a poor performance design. As people update their iPhones, iPads and so on, this will become more obvious. I've reported the issue to Apple but no response or fix yet (nor have they given any prevision date).

Don't believe me? Try it here!
Do you have any information? Share it with me!

Some more info:
    – I was able to reproduce the problem using an iPhone, iPad and iPad mini;
    – It doesn't have anything to do with the server hosting the page; I've tried more than one server and I've confirmed with Wireshark that the request doesn't go out, the delay is not server-side;
    – The issue happens using both HTTP and HTTPS (with valid certificate);
    – If we add "n" more resources, the problem remains;
    – Adding extra HTML in the middle "might" solve the problem;
    – The issue might not happen all the time in this example due to cache hits. Just refresh a couple of times or clear the cache and you'll hit the issue.
    – Using RequireJS or JavaScript to inject script tags might reduce the probability of the issue happening;
    – Some more use cases: 6 empty Javascripts inside the head tag with one CSS using a @font-face or @import; Using one CSS with several fonts that need to be dowloaded and one JavaScript file; Anything that implies different resources and one JavaScript;
    – Only happens using WiFi. If you use your 3G or 4G connection, it won't happen.

Is the issue affecting you? Report to Apple