iPhone Webapps 101: detecting essential information about your iPhone
Thursday, 24 September 2009
In this second instalment of iPhone Webapps 101 I’ll discuss some issues I ran into earlier this week during the development of my own iPhone Webapp. It deals with five separate issues, but all have something in common. Today we are detecting some essential information about your iPhone.
What version of the iPhone OS is your app running on?
If you have an application that requires certain features that are only present in recent versions of the iPhone OS you may want to prevent users of an earlier version from starting your Webapp.
For example your application might depend on CSS Animations or CSS Transforms. Of course you shouldn’t use this to determine whether or not your app should use these features. You can and should detect for the presence of that particular feature instead. But if you know your app only runs properly on certain versions you might want to give the user a friendly warning with advice to upgrade instead of letting the app break in their face.
You can detect which version the iPhone is running by using JavaScript and looking at the navigator
object; more specifically the userAgent
property of the navigator
object. If you are running an iPhone or iPod Touch it will mention iPhone or iPod in the userAgent
property, if you are running iPhone OS 2.0 or later it also tells you the exact version.
Below is a small example of how to use the navigator
object to detect the version of the iPhone OS. It will report version 1.0 for all versions prior to 2.0 and the exact version after 2.0:
function iPhoneOS() { var version = navigator.userAgent.match('iPod') || navigator.userAgent.match('iPhone') ? 1.0 : false; if (match = /iPhone OS (.*) like Mac OS X/.exec(navigator.userAgent)) { version = parseInt(match[1].replace('_', ''), 10) / 100; if (version < 1) { version *= 10; } } return version; } if (version = iPhoneOS()) { alert ('iPhone OS ' + version); } else { alert ('This is not an iPhone'); }
Is your app running full screen or in Safari?
In the previous tutorial Getting Safari out of the way I explained how to run your apps full screen. Personally I have no problem with the steps required, but I can image that it is a horrible install experience for most people. I also expect that most people don’t even know about the possibility to install a Webapp on the home screen of your iPhone. If we are able to detect whether or not we are running full screen we could display a helpful note explaining how to install the app to those users that are running the app in Safari.
There are also other uses for this information. If your app is running full screen you have more screen real-estate you could use. Based on this information you could position the elements of your app just a little differently. My own app uses a fixed toolbar at the bottom of the screen. If I run the app in Safari the toolbar will be obstructed by Safari’s own toolbar. So I detect if I am running in full screen and if not, I move the toolbar a little bit to the top, so that it is still visible.
You can detect if you are running full screen by using JavaScript and looking at the navigator.standalone
property. If it is true
you are running full screen. If false
you are running in Safari.
if (navigator.standalone) { alert ('Running full screen'); } else { alert ('Running in a browser'); }
What is the orientation of the iPhone?
It is possible to hold the iPhone horizontal or vertical and unlike native apps you can’t prevent the iPhone from changing the orientation of your Webapp along with it. I’m sure you can imagine that changing the orientation of the phone will have quite an effect on the layout of your app. If you app was designed to run in portrait mode it will not fit on the screen in landscape mode. There is nothing you can do to prevent that, but if you know how the user holds the phone you can change the layout of the app.
The simplest way to do this is to use two sets of CSS declarations: one set that determines the layout of your app in portrait mode and one that determines the layout in landscape mode. Using this technique you could easily change an app that displays blocks of information beneath each other to blocks that are shown besides each other. If that really doesn’t work for your app you could use a CSS transformation as a last resort and rotate your whole application back to the orientation you need.
It’s not difficult to check the orientation of your phone. Use JavaScript to look at the window.orientation
property. It will be 0
if the phone is held vertically and 90
or -90
if the phone is held horizontally. The iPhone will also dispatch an orientationchange
event every time the user changes the orientation of the phone.
function checkOrientation() { switch (window.orientation) { case 0: alert ('Orientation: Portrait'); break; case 90: case -90: alert ('Orientation: Landscape'); break; } } addEventListener("orientationchange", checkOrientation); checkOrientation();
Does my app have a network connection?
Thanks to its 3G data connection and Wi-Fi support the iPhone is supposed to be always connected to the internet. Unfortunately the reality is that in some areas cell reception is spotty at best and public Wi-Fi connections are not available everywhere. So while the iPhone is a great device for accessing the internet on the road, it doesn’t have an always-on internet connection.
So how can we detect that the app is connected to the internet? If we have an application that regularly fetches new data we need to know whether or not we can actually do so. And more importantly, if we are currently offline and our app needs to send information, we may want to queue those changes and wait until we have an internet connection.
The bad news is that it isn’t really possible to detect an internet connection. At most we can detect a network connection, but that isn’t really the same thing. Imagine being connected by Wi-Fi to your local network. If your ADSL or cable modem is turned off you may have a network connection, but you won’t have an internet connection. What use is detecting a network if you can’t detect if you’re connected to the internet? Detecting a network connection will at least tell you one thing: if you don’t have a network connection you can be sure there is also no internet connection. The only way to test if there is an internet connection is to actually try to connect to the server.
The exact details on how to build an app that queues outgoing data is beyond the scope of this article, but below you will find a small piece of JavaScript code that will detect if your app has a network connection. You can determine your current state by looking at the navigator.onLine
property.
function checkOnline() { if (navigator.onLine) { alert ('There is a network connection'); } else { alert ('There is no network connection'); } } checkOnline();
Was your app interrupted?
It is perfectly possible for your Webapp to be interrupted by a phone call or when the user deliberately locks the iPhone. There are certain cases when you want to know if your iPhone is being resumed.
For example in my own application I check a server regularly for updates. I do this using a simple interval timer executing a piece of code every 5 minutes. If the Webapp is interrupted all timers are paused. This means that if a user runs my app and is interrupted by a 30 minute call, the app isn’t updated during the call. And when the app resumes it has 30 minute old data. I want the app to update it right away, but it might actually take a couple of minutes before the interval timer executes the update code.
There isn’t an easy event that is triggered whenever the app is resumed and the window focus
event which could be used for this purpose isn’t supported on the iPhone. I decided to create one. Below you will find a small piece of code that dispatches a resume
event on the document:
(function() { var timestamp = new Date().getTime(); function checkResume() { var current = new Date().getTime(); if (current - timestamp > 4000) { var event = document.createEvent("Events"); event.initEvent("resume", true, true); document.dispatchEvent(event); } timestamp = current; } window.setInterval(checkResume, 1000); })(); addEventListener("resume", function() { alert('Resuming this webapp'); });
It works by detecting exactly the problem it tries to solve. It uses an interval timer that runs every second. The timer gets the current time and compares it to the time of the last interval. There should be approximately one second between the two times, because the interval is one second. However, if the app is interrupted, the timers are paused and the time between the current time and the time of the last interval is higher than that single second. I set the threshold to four seconds: if you app is interrupted for more than a couple of seconds it dispatches the event.
If you want to use this event to trigger an update you might want to use another timer. If the iPhone was locked by the user it will drop any Wi-Fi connections it has and it will take a couple of second to reconnect after the iPhone is turned back on. The event triggers immediately after it is turned on and there might not be a Wi-Fi connection yet.
addEventListener("resume", function() { alert('Resuming this webapp'); window.setTimeout(function() { alert('Updating information...'); }, 4000); });
More about iPhone webapp development
This is the second part of an ongoing series of articles called iPhone Webapps 101. The other installments are: