Technetra

How to Deploy an iPhone Web Application

Robert Adkins,  December 23rd, 2008 at 3:45 am

This article shows you how to deploy a stand-alone iPhone web application. In the process, we’ll also create a basic installer for any iPhone web application. To provide a working example, we’ll adapt a variation of the RedGreenYellowBlue web app described in the earlier article Building a Simple iPhone Web Application - RedGreenYellowBlue. We’ll turn our new program, rg_rotate, into a self-reliant, stand-alone application and also empower it with its own icon on the iPhone home screen. Once installed, the program can be run off-line, independently of any network connection or server-side resources. rg_rotate will show only two colors (red/green) but will conveniently change color when the iPhone is merely rotated. In portrait orientation the iPhone will shine red and in landscape it’ll show green. Tapping the screen is no longer needed to switch color.

Getting Started

To make an iPhone web application stand-alone, we need to provide up-front all the resources required for it to run independently. This means that any images, stylesheets, and scripts that are part of the structure of the program must be included in the program at the time it is installed. The easiest way to accomplish this is to convert any external URL references in img, script, link and other HTML tags into self-contained data URLs. Data URLs are a way for tags to embed their own data directly. A data URL is used as the value of a reference or location attribute within a tag. For example, an ordinary image tag <img src="some.gif"> could be transformed into something like

<img src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUg...">

The source attribute of the img tag now holds a base 64 encoding of the GIF along with a simple descriptor which specifies the protocol identifier (”data”), the appropriate mime type and subtype (”image/gif”) and the encoding method (”base64″).

All of the resources required by a stand-alone iPhone web application are encoded as data URLs. The web application is also encoded as a data URL. The encoded program is just a string of numbers and letters but it is far too long to type into the URL Bar of Mobile Safari. The challenge is how to provide the encoded program conveniently to the iPhone. The HTML viewer within the iPhone’s Mail application will not resolve a data URL, so the program cannot simply be e-mailed in a link to the iPhone. Mobile Safari, however, fully supports data URLs. A straightforward solution therefore is to provision an HTML page with a link that contains the complete, encoded program. This HTML page can be dynamically generated or provided statically. A dynamic strategy is quite flexible and can easily support an interface for uploading associated resources such as scripts, icons, buttons, and HTML fragments. A generator could then assemble the parts and return a complete, encoded result in a page for Safari to load as a bookmark. Once loaded into a bookmark, the stand-alone program executes independently from where it was initially installed as well as independently of any specific network connection.

Our Approach

For the sake of simplicity, however, our approach in this article will be to build a static HTML page that incorporates the target web application program source and the resources it requires. In our simple example, we will supply two external files: an image file that will be used as the home screen icon and the JavaScript/HTML web application itself. Both files will be converted to data URLs. First, the image file will be encoded into base 64 and embedded into the web application within a link tag. Then, perhaps surprisingly, the web application will be converted to a data URL as well. The data URL of the encoded program is incorporated into a provisioning web page which will be provided to Mobile Safari running on the iPhone. Any web server that is accessible to your iPhone can host the static HTML provisioning page needed by Safari. Mobile Safari can then be instructed to install your program into a bookmark along with its embedded home screen icon. Note that this solution avoids depending on a proprietary application like iTunes to synchronize bookmarks.

Installation Steps

In detail, the steps we will accomplish are as follows.

  1. Identify files that need to be encoded into base 64. In our example, these are rg_rotate_touch_icon.png, containing the apple-touch-icon shortcut icon and rg_rotate.html, containing the JavaScript/HTML web application we want to provision.

  2. Encode rg_rotate_touch_icon.png into a base 64 representation. This can be accomplished in a variety of ways. Linux distributions, including Ubuntu and Fedora, have a base64 command line utility. So

    echo "data:image/png;base64,`/usr/bin/base64 -w 0 rg_rotate_touch_icon.png`" > \ rg_rotate_touch_icon.b64

    creates rg_rotate_touch_icon.b64 as a fully specified data URL that will become the apple-touch-icon resource. The “-w 0” argument to base64 disables default line wrapping. Turning off line wrapping is not strictly necessary but is convenient for handling the encoded image as a single element.

    On Linux as well as other systems, Ruby or Perl can provide a cross-platform capability. The Ruby version would be

    ruby -e 'print %{data:image/png;base64,#{[ARGF.read].pack("m").gsub("\n","")}\n}' \ rg_rotate_touch_icon.png > rg_rotate_touch_icon.b64

    where pack("m") base 64 encodes its Array target element and gsub("\n","") removes line wrapping (Ruby 1.9+ allows m0 as pack’s argument to remove line wrap).

  3. Install the base 64 encoded image in the web application file, rg_rotate.html. Copy and paste the content of the file created in Step 2, rg_rotate_touch_icon.b64, into the href attribute of the link tag that identifies the Apple touch icon to Safari. This link tag appears after the opening head tag of the web application file, rg_rotate.html:

    <link rel="apple-touch-icon" href="[copy content of rg_rotate_touch_icon.b64 here]" />
  4. Encode rg_rotate.html into base 64:

    echo "data:text/html;charset=utf-8;base64,`/usr/bin/base64 -w 0 rg_rotate.html`" > rg_rotate_html.b64

    In Ruby,

    ruby -e 'print %{data:text/html;charset=utf-8;base64,#{[ARGF.read].pack("m").gsub("\n","")}\n}' rg_rotate.html > rg_rotate_html.b64

    which encodes the entire web application, including any resources embedded as data URLs, as a data URL.

  5. Install the base 64 encoded web application in the application installer file, rg_rotate_installer.html. The source for rg_rotate_installer.html is shown in Listing 3. Copy and paste the content of the file created in Step 4, rg_rotate_html.b64, into the href attribute of the a anchor tag that will be used to install the data URL into the URL Bar on Mobile Safari. For example:

    <a href="[copy contents of rg_rotate_html.b64 here]">INSTALL RG-ROTATE</a>
  6. Figure 1: Installation Web Page

    Figure 1: Installation Web Page

  7. Provision the installation web page. Using a local or remote Web server, e.g, Apache, place the file rg_rotate_installer.html in a location that Mobile Safari on the iPhone can navigate to. A screen similar to Figure 1 will be displayed. Follow the installation steps shown. In the second step of Figure 1, when the “+” key on the iPhone Button Bar is pressed a menu rolls up. Select the menu item “Add to Home Screen”: a dialog screen, “Add to Home”, is activated. The application icon (encoded in Step 2 above) should appear on this dialog screen. Notice that the icon will have been enhanced by Safari with 3D shading and also will have been scaled to fit a 57×57 pixel rectangle. Next fill in the application name and press the Add button. The application can now be launched by simply tapping its icon on the iPhone home screen.

As a convenience, we have automated steps 1-5 in a Bash script (Listing 2) which you can download. This script will write an installer HTML file for rg_rotate by running the command: build_installer.sh rg_rotate. Other application installers can be built by providing different application names to the builder script: build_installer.sh [application name] creates the file [application name]_installer.html. Please see the next section, “Extending the Installer”, for more information about enhancing build_installer.sh to handle different applications.

The script prompts for a single image file to be used as the apple-touch-icon resource. The script determines the mime type of the image file from its extension and encodes the corresponding data URL appropriately.

Extending the Installer

Simplified for instructional purposes, the provided generator script prompts for and embeds only one external resource, However, it can easily be enhanced to prompt for multiple images and other resources that particular stand-alone web applications might need. As a first approach, one can add sed commands to replace additional target URL references with base 64 encoded resources. In build_installer.sh, each resource is identified in the source web application with a placeholder such as “RESOURCE_1“. An entry for this placeholder would be added to the script as a sed command of the form:

s~RESOURCE_1~"data:'$mime_type[1]';base64,'`$BASE64 -w 0 $resource_file[1]`'"~;

where the values for $mime_type[1] and $resource_file[1] must be set appropriately. See Listing 2 build_installer.sh for details. A much more robust web application installer could be built with a sophisticated micro-framework such as the Ruby-based Sinatra (see for example, Building a iPhone web app in under 50 lines with Sinatra and iUI), but this approach would require the installation and management of additional resources.

Listing 1: RG Rotate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<html>
<head>
<link rel="apple-touch-icon" href="TOUCH_ICON" />
<meta name="viewport" content="width=device-width; initial-scale=1.0;
  maximum-scale=1.0; user-scalable=0;"/>
<style>
    body[orient="portrait"] {
        background-color: #f00;
    }
    body[orient="landscape"] {
        background-color: #0f0;
    }
    body[orient="portrait"] #content {
        width: 100%;
        height: 460px;
    }
    body[orient="landscape"] #content {
        width: 100%;
        height: 300px;
    }
</style>
<script>
  var currentWidth = 0;
  addEventListener("load", function() {
      setTimeout(orientationChange, 0);
  }, false);
  function orientationChange() {
      if (window.innerWidth != currentWidth) {
          currentWidth = window.innerWidth;
          document.body.setAttribute('orient', (currentWidth == 320) ? "portrait" : "landscape");
          setTimeout(scrollTo, 100, 0, 1);
      }
  }
  setInterval(orientationChange, 400);
</script>
</head>
 
  <body>
 
 
    <!--
      <img src="RESOURCE_1" alt="test jpg" />
      <p>TEST JPG: see build_installer.sh for details</p>
    -->
    <div id='content'></div>
  </body>
</html>

Listing 2: build_installer.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#!/bin/bash
#
# build_installer.sh
#
# This script is part of the tutorial about "How to Install
# an iPhone Web Application". The script is run with a
# single argument, the application name, which defaults to
# rg_rotate for the purposes of the tutorial.
# 
 
# The script demonstrates how to build a web page which can
# be used to provision an iPhone web application. The
# source file for the web application is expected to be named
# [application_name].html and the provisioning web page is
# written out as [application_name]_installer.html.
# 
# This script can be enhanced with prompts for additional
# resource files and mime types. Alternatively, additional
# resources can be hand coded where indicated.
# 
# The body of the provisioning web page holds the complete,
# encoded web application within a link that can be selected
# in the Mobile Safari browser. When this link is selected,
# the data URL that encodes the web application is placed
# in Safari's URL Bar. At this point the application can be
# installed with the + key of the Safari Button Bar. In order
# for Safari to access the provisioning web page, it must
# be placed on a preferably local web server that can be
# reached by the iPhone.
#
# The provisioning web page is generated to be run as a mini
# iPhone application itself. For example, it will respond to
# orientation changes and hides the URL Bar when loaded.
# 
# BEGIN RESOURCE MAPPINGS
# change the following dummy resource mappings as needed:
# Specifically, for each N define the mapping in this file
# as: mime_type[N]='type/subtype'; resource_file[N]='file_path'
# and the label RESOURCE_N as the corresponding URL reference
# in the web application file.
# E.g., mime_type[1]='image/jpg'; resource_file[1]='myface.jpg'
# and <img src="RESOURCE_1" alt="myface" /> in the web
# application file.
mime_type[1]='image/gif'; resource_file[1]='/dev/null'
mime_type[2]='image/gif'; resource_file[2]='/dev/null'
mime_type[3]='image/gif'; resource_file[3]='/dev/null'
# END RESOURCE MAPPINGS
MAX_RESOURCES=${#mime_type[*]} 
map_resource() {
 local ret=$1
 local id=$2 
 local resource='s~RESOURCE_${id}~data:'${mime_type[$id]}'\;base64,'\
`$BASE64 -w 0 ${resource_file[$id]}`'~\;'
 eval "$ret=$resource"
}
BASE64="/usr/bin/env base64"
XARGS="/usr/bin/env xargs"
TEMPFILE="/usr/bin/env tempfile"
application=${1:-rg_rotate}
touch_icon_image_file="${application}_touch_icon.png"
if [[ ! -e "${application}.html" ]]; then
  echo "Template web application file ${application}.html does not exist."
  echo "Please create and run application installer again -- exiting."
  exit
fi
read -p "Enter location of apple-touch-icon image file (./$touch_icon_image_file): " ipath
[[ "$ipath" ]] && touch_icon_image_file="$ipath"
if [[ ! -e "$touch_icon_image_file" ]]; then
  echo "$touch_icon_image_file does not exist, will create application with empty bookmark icon"
  touch_icon_image_file=/dev/null
fi
echo "Using ${application}.html as input template for web application"
touch_icon_mime_type="image/${touch_icon_image_file##*.}"
TFILE=`$TEMPFILE`
trap "rm -f $TFILE" 0 1 2 5 15
# TODO: hand code or prompt for additional resources
additional_resources_map=
for i in $(seq 1 $MAX_RESOURCES); do
  map_resource res $i
  additional_resources_map="$additional_resources_map$res"
done
cat <<EOM1 | $XARGS -I{} sed -e {} ${application}.html > $TFILE
's~TOUCH_ICON~data:'$touch_icon_mime_type';base64,'`$BASE64 -w 0 $touch_icon_image_file`'~; \
 $additional_resources_map \
'
EOM1
echo "Creating `pwd`/${application}_installer.html"
echo "Point Mobile Safari to the HTTP location of this file to install ${application}"
#
# Provisioning web page:
cat <<EOM2 > ${application}_installer.html
<html>
  <head>
    <meta name="viewport" content="width=device-width; initial-scale=1.0;
        maximum-scale=1.0; user-scalable=0;" />
    <style>
        body[orient="portrait"] {
          height: 460px;
 
        }
        body[orient="landscape"] {
          height: 300px;
        }
    </style>
    <script>
      var currentWidth = 0;
      addEventListener("load", function() {
          setTimeout(orientationChange, 0);
      }, false);
      function orientationChange() {
          if (window.innerWidth != currentWidth) {
              currentWidth = window.innerWidth;
              document.body.setAttribute('orient', (currentWidth == 320) ? \
                 "portrait" : "landscape");
              setTimeout(scrollTo, 100, 0, 1);
          }
      }
      setInterval(orientationChange, 400);
    </script>
  </head>
  <body style="background-color: #d0d0d0; font-size: 24px; height: 460px;">
      <h2 style="text-align: center;">
        <span style="background-color: #000; color: #fff;">"${application}"</span>
        <br />Installation Steps
      </h2>
      <ol>
          <li>Select
           <a style="background-color: #ff0; font-weight: bold;"
             href="data:text/html;charset=utf-8;base64,`$BASE64 $TFILE`">
             this link
           </a>
             to install "${application}" as a data URL in the URL Bar, then</li>
          <li>Press
            <span style="background-color: #ff0; font-weight: bold;">+</span>
            key on iPhone button bar to add "${application}" to home page.
          </li>
      </ul>
  </body>
</html>
EOM2

Listing 3: rg_rotate_installer.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<html>
  <head>
    <meta name="viewport" content="width=device-width; initial-scale=1.0; 
     maximum-scale=1.0; user-scalable=0;" />
    <style>
      body[orient="portrait"] {
        height: 460px;
      }
      body[orient="landscape"] {
        height: 300px;
      }
    </style>
    <script>
      var currentWidth = 0;
      addEventListener("load", function() {
        setTimeout(orientationChange, 0);
      }, false);
      function orientationChange() {
        if (window.innerWidth != currentWidth) {
          currentWidth = window.innerWidth;
          document.body.setAttribute('orient', (currentWidth == 320) ?
              "portrait" : "landscape");
          setTimeout(scrollTo, 100, 0, 1);
        }
      }
      setInterval(orientationChange, 400);
    </script>
  </head>
  <body style="background-color: #d0d0d0; font-size: 24px; height: 460px;">
      <h2 style="text-align: center;">
        <span style="background-color: #000; color: #fff;">"rg_rotate"</span>
        <br />Installation Steps
      </h2>
      <ol>
          <li>Select
            <a style="background-color: #ff0; font-weight: bold;"
             href="[copy contents of rg_rotate_html.b64 here]">this link
            </a>
            to install "rg_rotate" as a data URL in the URL Bar, then
          </li>
          <li>Press <span style="background-color: #ff0; font-weight: bold;">+</span>
              key on iPhone button bar to add "rg_rotate" to home page.
          </li>
      </ul>
  </body>
</html>

Copyright © 2008 Technetra. This code is covered by the MIT License. You can follow any responses to this entry through the RSS 2.0 feed. You can skip to the end and leave a response. Pings are currently closed.

Building a Remote Message Monitor for the iPhone Article Index Building a Simple iPhone Web App: RedGreen…YellowBlue

Comments

Be the first to post a comment.

Add a comment

Leave a comment or send a note
  1. (required)
  2. (valid email required)
  3. (required)
  4. Send
  5. Captcha
 

cforms contact form by delicious:days

© 2000-2009 Technetra. All rights reserved. Contact | Terms of Use

WordPress