5

We have a web application which allows users to view SVGs. These SVGs usually contain images which are loaded using a relative url. As we want this to be an iPad 'web app' we also want it to be added to the iPad user's homescreen and for it to be cached by the HTML 5 application cache so the user can view these SVGs and associated images offline.

When our application is loaded from the iPad home page icon, the application cache manifest is read correctly and all of the referenced resources are cached. The issue occurs when the user starts to use the application offline. During the use of the application, SVGs are added and removed from the page DOM. During this, some of the images in the SVG fail to be loaded from the application cache, even though they are definitely present and cached. Instead, a request for the image is made to the server, which obviously fails because the user is no longer online.

Interestingly enough, this issue doesn't seem to occur when navigating to a page in safari on the iPad. It seems to be specific to the full page web app view, although I can't guarantee it.

I can reproduce this quite easily using this HTML page:

<!DOCTYPE html>
<html manifest="testfiles.manifest">
<head>
    <title>Test</title>

    <script src="Javascript/jquery-1.7.1.js" type="text/javascript"></script>

    <!-- Remove the browser chrome when the page is loaded from a homescreen icon -->
    <meta name="apple-mobile-web-app-capable" content="yes" />

</head>
    <body>

        <h1>Simple SVG caching test</h1>

        <h2>Basket ball SVG</h2>

        <p>
            <span id="remove">Remove</span> | <span id="add">Add</span>
        </p>

        <p>
            <span id="show">Show</span> | <span id="hide">Hide</span>
        </p>

        <p>
            <span id="reload">Reload</span>
        </p>

        <embed width='360' height='510' src='TestFiles/Basketball.svg' />

        <script type="text/javascript">
            $(function ()
            {
                $("#remove").click(function ()
                {
                    $("embed").remove();
                });

                $("#add").click(function ()
                {
                    $("<embed width='360' height='510' src='TestFiles/Basketball.svg' />").appendTo("body");
                });

                $("#show").click(function ()
                {
                    $("embed").show();
                });

                $("#hide").click(function ()
                {
                    $("embed").hide();
                });

                $("#reload").click(function ()
                {
                    location.reload(true);
                });

            })
        </script>

    </body>
</html>

This SVG:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg

   xmlns="http://www.w3.org/2000/svg"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   width="340"
   height="340">

  <image
     width="340"
     height="340"
     xlink:href="Basketball.png"
     x="0"
     y="0" />

</svg>

This image which is referenced by the SVG:

The image referenced by the SVG

And this manifest file:

CACHE MANIFEST
CACHE:
TestFiles/Basketball.svg
TestFiles/Basketball.png
Javascript/jquery-1.7.1.js

And by following these steps:

  1. Open safari and navigate to the location of the reproduction html file (we host it on a Windows 2008 / IIS server)
  2. Add the page to the home screen using the 'Add to Home Screen' button.
  3. Close safari and clean out the safari cache
  4. Load the page from the newly added bookmark
  5. Wait until the page is completely cached. Usually about 5-10 secs, but you could attach to some application cache events to log out progress is desired.
  6. Turn off wifi (or what ever means of connection you use)
  7. Load the page from the newly added bookmark
  8. Notice that the page looks correctly cached. Use the add and remove buttons. You should notice fairly quickly that when you add using the 'add' button on the page. The image resource in the SVG isn't always loaded from the application cache, even though it's clearly cached.

When the issue occurs, you should see something like the screen shot below

Screenshot of an SVG with an image failing to load the image from the application cache

I've already checked some obvious things:

  • Application cache manifest has the correct mime type
  • Manifest is downloaded
  • Resources referenced in the manifest are cached correctly

My questions are:

  • Does anyone know why this happens?
  • Are there any workarounds to this issue?

I've logged this as a bug with apple, so I'll update this question with any feedback I may get from them!

Thanks!

Andy.

Andy
  • 2,977
  • 2
  • 39
  • 71
  • Do the iOS home page apps still use a different version of WebKit to iOS Safari, or did they fix that? As for workarounds: are JS and/or HTML caching correctly? Could you encode your images as data URLs? – robertc Mar 21 '12 at 14:58
  • Hey, thanks for the suggestions, the JS and HTML are both caching correctly, just the image inside the SVG on the HTML page that isn't being retrieved from the application cache. Regarding the iOS homepage apps and iOS safari, I don't actually know. I was guessing that they used a slightly different version or there was something different between them, but I don't know what. Is there any way to find out (I'm not a massive iOS pro!). We'd want to try and avoid data URLs if possible as it would be inefficient if we have multiple SVGs with the same image (background or header maybe). – Andy Mar 22 '12 at 08:52
  • Actually, thinking about it, can we get the webkit version from the headers of the request?... I'll have to have a look when I have a bit of time! – Andy Mar 22 '12 at 08:53
  • There's some [old detection scripts here](http://trac.webkit.org/wiki/DetectingWebKit), not sure how well the work with iPad. – robertc Mar 22 '12 at 10:35
  • It seems the web kit version is the same, but if you compare user agent strings, the full screen view misses the 'safari/xxxxx' bit off the end. I guess maybe it's because it uses the UIWebView component instead of the 'Safari' application? Guess this must be the difference between the two. – Andy Mar 28 '12 at 09:22
  • Just as an update, it looks like SVG which is inline to the page doesn't exhibit the same issues... doesn't perform as well though as it means ajax calls to get the SVG and then more time to push it into the DOM. – Andy Apr 24 '12 at 15:37

1 Answers1

0

Just a small "head's up!" if you're pondering this issue, as I was until an hour ago...:

The manifest file is case-sensitive.

I have a website designed for use on both the desktop, tablets and smartphones. On Android (4.0.3) I noticed that after enabling the HTML 5 App Cache, my SVG icons started to fail, just as in your case above. They appear as broken links, when the user refreshes the page (and the icons are attempted fetched from the cache).

I had written a letter in the folder name to my SVG icons in the wrong case, and fixing this immediately fixed the issue.

Thomas
  • 558
  • 4
  • 14
  • 1
    This isn't the issue, I can tell the cache manifest is correct as it wouldn't be cached at all if the case was wrong (I've experienced this too in the past). My situation shows that the images in the SVG are cached, but not always taken from the cache. If I put all the SVG inline instead of in an embed, it works perfectly too. Guess it must be a bug with the embed tag. – Andy Jun 22 '12 at 12:31
  • Also, the cache manifest is programatically created, so the case really should be right! :) – Andy Jun 22 '12 at 12:33