16 March 2009
One thing I immediately loved about Adobe AIR is just how easy it was to build an application for the desktop, especially if, like me, you came from a web developer background—you create an AIR project, write your code you've always done for the web, create the build, and you're done!
However, over time, I've also learned that building AIR applications can be a bit tricky. Desktop applications are differ from browser-based applications. Most notably, their deployment model has very important consequences.
Fortunately, the Adobe AIR runtime and its SDK already provide tools that facilitate the work of the developer, including the ability to upgrade your application, via the updating API and the update framework.
That said, unlike a web application, you can't just publish a new build without first thinking about the consequences on the user's system. Each build creates dependencies, a legacy you'll have to take account for during the entire lifetime of your software. If you don't take care of this legacy from the very start, you may run into problems. I guess it's true what they say, with great power comes great responsibilities.
The goal of this article is to identify the potential pitfalls you may encounter when building and deploying an Adobe AIR application update, and learn some tips and techniques on how to avoid them.
Note: I assume that you already have some basic knowledge about building and updating AIR applications. If you want to know how to use the update framework, refer to the Using the Adobe AIR update framework for Flash and Mihai Corlan's article, Using the Adobe AIR update framework.
When you start creating an AIR application, you don't have to decide whether or not you will update it in the future. The ability to update comes out of the box with any AIR application. However, it is the developer's responsibility to always assume that it will have to be updated at some point, and get ready for this before a public release.
One of the main reasons why this question really matters is security. You have to be able to provide security patches for your applications. This is all the more crucial as AIR applications wreak havoc on a user's file system if you're not careful. Nobody wants to be responsible for a security breach.
Besides security concerns, you can also easily imagine that many issues such as bugs, typos, or missing legal information (to name a few) may require you to provide an update pretty quickly. On the contrary, you can never know how well your work will be received. Maybe what was supposed to be a simple demo will become a huge success?
As a result, the real question to ask is not, "should I make it possible to update?" but "how should I manage the updating process?" As you cannot expect your users to check your website everyday to look for a new build, it is generally a good idea to provide some auto-update feature. Although you can use the AIR SDK update API to do so, the easiest way to go is probably to use the new update framework.
However, you'll still have to make some decisions such as when to check for new builds, or whether or not upgrading is mandatory. More importantly, you'll have to realize that each build is just a step in your application's life cycle. Therefore, it should be as less intrusive as possible.
As you probably know, AIR applications are identified by the runtime and the OS through an application ID, a string that you provide in your application descriptor XML file. This ID serves several purposes:
These are only some of the reasons why you should never change an application ID after you've published a build, unless you want it to be treated as a completely new application. Note that, in some rare cases, this may actually be what you want, in order to let users install and use each of these versions independently.
In most cases, though, you must choose your application ID before the first public release, and never change it. Using a reverse DNS naming scheme such as "com.mycompany.myapplication" used to be considered good practice in early beta versions of Adobe AIR. However, it is no longer necessary since the application ID now combines with the publisher ID to identify the application.
You don't have to worry if you have to change the commercial name of your software after a public release (for legal issues, or because the marketing said so, for example); the application ID is not related at all to the name under which the application will appear on the system while running, or during the install process—users of your application will never have to see this ID.
Note that the application ID is not the unique criteria for identifying an application. For security reasons, two applications with the same ID but built with different signatures will also be considered as different applications, unless a proper migration procedure has been put in place.
It is important to remember that AIR applications often use, and even create, files and directories on the user's system. Those files can include
This is of course a major feature of AIR. But you should be aware that those files can create dependencies. Your application may rely on some files to run or execute certain tasks.
To this regard, it's a good idea to keep a list describing each of these files, in order to remember its path, role, destination on the user's system, and what kind of dependency it has created. It will also be necessary for you to decide whether or not these files can or must be overwritten by the next builds. For example, it's not recommended to overwrite user preferences on each build.
When possible, store versioning information in your files. This will be very useful when you build future updates.
The application should always assume that these files can be missing. If those files are vital to the application, then it should provide a mechanism to (re)create them, or at least fail explicitly, with a proper error message. Remember that you do not have the choice as to where the embedded files are to be placed on the user's system during the installation process. All files embedded in the .air package will be placed into the Application Directory. It is the application's responsibility to copy them to their final destination (while the Application Storage Directory is the recommended place to store most of these files, you can choose to create them elsewhere).
Moreover, these files will probably change over time. For example, it can be edited directly by the user, using another software. Therefore, you should also assume that the files can be malformed, and again, provide a mechanism to fix them or re-create them.
As a consequence, your program will probably dedicate part of its startup sequence to checking the availability of the files it relies upon. It will create those files if missing, or overwrite out-of-date files. It may also need to delete obsolete files at this stage, but file deletion should always be seen as a last resort measure, and be treated with great caution.
Sometimes, you'll even have to provide a more complex, ad hoc mechanism for file migration. For example, if your database table structure has changed, you may need to translate all existing data from the previous database structure to the new one.
At this stage, you may need to check whether or not it is the first time the application is run on the system, or what was the last installed version. The Encrypted Local Store is the ideal place to store this information, since you know it will be consistent over builds. However, you should ensure that items are not stored with the
stronglyBound parameter of the
EncryptedLocalStore.setItem() method set to true, because in that case the data could not be read after an application update.
One thing to keep in mind when creating an AIR application is that an AIR release build is considered as a different application than its development build counterpart running in ADL. This has two very important consequences:
Remember, though, that both the Application Storage Directory and Encrypted Local Store remain the same after an update, with the notable exception of items stored with the strongly bound parameters. In other words, they're tied to the application ID, not the application file.
Besides, the Encrypted Local Store can never be emptied, unless of course its associated application explicitly does so. Even if you completely uninstall the application, the Encrypted Local Store remains. This is also expected, since it gives the developer the ability to store informations such as, "has the application already been installed on this computer? When? What version ?" This could help you manage your licensing model, for example.
Since you cannot reset the Encrypted Local Store, you could create an internal mechanism in your application to clear all entries of the Encrypted Local Store. Keep in mind that this could be dangerous. The best move is to always assume in your code that the Encrypted Local Store can be empty, or not provide you with the expected data.
Another key difference between the development builds and the release builds is the fact that the latter will be packaged into an AIR file. The development build is executed by ADL in the context of the bin-debug directory (if you're using Flex Builder, otherwise your default output folder), but the final application will be executed inside the resulting Application Directory.
It is your responsibility, as a developer, to determine the content of the .air package when creating the release build.
Whether you use ADT directly by command line or via an IDE like Flex Builder, packaging and signing can either be done in a single step, or by first packaging the application and then signing it at a later time.
Of course, don't forget to embed the files your application will require when executing. ADT will only require two files for the content of your package: the application SWF or HTML file, and the descriptor XML file. It won't be able to detect that some other files your application depend upon are missing. Again, it is your responsibility as a developer to make sure all of them are included in the final package.
Also, don't forget that some files can be specific to your release build, the classic example being the various native application and file association icons.
Note: If you're using Flex Builder for packaging your application, you'll probably do so using the Export for release build wizard panel. At first thought, you may think that the content of the release build .air package will be created from the bin-release directory. This is only partly true since the Export for release build wizard gives you the ability to choose what files and directories of this bin-release directory are invited to the party.
One of the known limitations of the current version of Flex Builder is that this wizard panel does not let you browse for external files or directories. You'll have to copy and paste external files to the source or to the bin directory in order to embed them.
Also, remember that some files inside the project's source directory cannot be found in the wizard panel and therefore cannot be chosen "as is" to be embedded in the air package. This is the case for ActionScript files and MXML files. Again you'll have to manually include these files inside the bin-release directory.
In those cases, if you don't use an automated build system, you'll have to build your application following these steps:
Code signing is a crucial step in packaging your software. In particular, two topics should be looked after very carefully.
First, you should keep in mind that, for security reasons, certificates are timestamped and will expire at some point. If your certificate expires, you will no longer be able to use it to sign new packages. However, previously signed packages will remain valid.
Second, if you're ever migrating from one certificate to another (for example, if you've bought a certificate during the development), don't forget to use the migration procedure. This will allow your users to seamlessly upgrade from your self-signed application to your new, trusted build.
Now is the time to test if your application behaves as expected. This step is sometimes referred to as functional testing. To do so, the main difficulty will be to ensure that the testing environment meets the necessary requirements. You'll have to assume that what works on your developer system will not necessary work on your users' systems.
Ideally, all those steps should be performed on Mac OS X, Linux, and Windows. Don't forget that, although AIR is cross-platform runtime, some features have OS-specific behaviors (for more on this, read Charles Ward's article on Developing cross-platform Adobe AIR applications. Also, if your application is localized, it should be tested for all different languages. Remember that the AIR runtime dialog boxes, such as the installation dialog boxes, can be localized through the application descriptor file.
The two main audiences to consider are existing users, which will upgrade from a previous version, and new users, which will install the application for the first time.
Assuming that you have installed previous release builds of this application on your test environment(s), you should first update the application, probably directly from the .air package, since your online install badge should not be updated at this stage. This ensures that (manual) updating works fine.
There is no easy way to test auto-updating, because the updater XML file and the .air package are shared by all your users through the network. You can put in place a debug updater file somewhere, but it would probably require to build a special "debug release build" to load the debug updater file. (Of course, you could change the online files for just the time to test it, and then change them back. However, that would be highly unprofessional.)
Next, you have to ensure that your software installs correctly for users who install the application for the first time. To do so, the best idea is to use a dedicated system image. But if you're testing from the same test environment as the one you used to test the update mechanism, then:
If you experience any crash or problem at this stage that you did not encounter with the development build, chances are that you failed to properly manage your file dependencies.
It can be hard to refrain from rushing in and just upload your application. But now that you have tested the application, you need to ensure that the environment surrounding your new build is up to date.
First, prepare your website's editorial content for the new version, but don't publish it yet. Think of its feature list, description, knowledge base, known issues and so on.
The typical procedure would then be to:
You may then want to inform your current and future users about the new release, with a blog post, a tweet, and anything you can think of.
Don't underestimate publishing time. It can be stressful, and it's definitely one of the most tedious parts of your application deployment. I'd recommend taking a look at simple automated build systems, like ANT tasks, to help you through this. At a minimum, write down those publishing steps, and make a check list.