Article
Learn Adobe AIR, Part I: Build A Note Storage App
Creating the Database
We’ll use a simple, single-table SQLite database to store our notes. We need four fields in our table:
idtitlecreateddata
The created field refers to when the note was created, and is stored in seconds (in UNIX time, which is the number of seconds that have elapsed since Jan 1, 1970). The data field will be a BLOB (Binary Large Object), so that we can fit in just about anything. We’ll make our id field an INTEGER PRIMARY KEY, our title will be a TEXT, and the created field will be an INTEGER (”NUMERIC” in SQLite Database Browser).
The theory behind using SQL databases in AIR applications is beyond the scope of this article, but Adobe provides some good documentation on strategies for working with SQL databases, which is definitely worth a read.
Running on AIR
Once we’ve got a base interface and a database, we’re ready to start adding actual functionality to our application.
If you haven't done so yet, download, and unzip the code archive containing the skeleton files for this article (air1-notesdb-base.zip).
In the .zip file you’ll see the following files:
- notes.html
- notes_base.db
- styles.css
- icons/delete.png
- lib/notes.js
Next, create a new AIR project in Aptana, and name it NotesTest. Specify the folder to which you extracted the files from the code archive as a Location. When you click Next, you’ll be presented with a dialog to enter the Application Descriptor properties. Choose an appropriate ID (I’ve used com.sitepoint.example.NotesTest), click Next again, then set your default window size to 800 x 600 pixels. Click Next again, and from the Import JavaScript Library dialog, select jQuery, and finally click Finish.
Aptana will create a few sample files for you to try out your environment; you can safely delete some of them though, including NotesTest.html, sample.css, LocalFile.txt, jquery_sample.html, and jquery_sample.js. We do need to tell Aptana that the root of our AIR application should be our new notes.html file. Open up the application.xml file and locate the following (it should be on or around line 36):
<!-- The main HTML file of the application. Required. -->
<content>NotesTest.html</content>
Change NotesTest.html to notes.html and save the file. From the Run menu, select Run…. Select your NotesTest project in the left pane, and hit the Run button:
![]()
Your AIR application will display in its own window, complete with the prototype HTML that we created earlier. It should look something like this:
![]()
It’s a good start! Let’s make this application a bit more useful.
Menus
We’ll first put the interface in place. We’ll store our JavaScript code in lib/notes.js. Here’s what our template looks like at the moment:
// Bootstrap
$(document).ready(function(){
BindEvents();
});
function BindEvents() {
$("#new_note_form").hide();
$("a.notes_list").click(HideNewNote);
}
function HideNewNote() {
$("#new_note_form").hide();
}
function ShowNewNote() {
$("#new_note_form").show();
}
This jQuery snippet waits until the DOM is ready to be manipulated, then hides the note creation form, and binds to the click event of the Cancel link inside that same form. If you need a primer on jQuery, check out the “jQuery 101” section of this article.
AIR provides an extensive API for generating menus from intricate data structures. All we really want, however, is a basic menu that fires JavaScript events when certain menu items are selected. With AIR 1.1, we can make use of the AIR Menu Builder framework, which allows us to define menus in XML and just load them into the menu builder for them to be magically generated. Create a new file called notes_menus.xml inside your NotesTest project that looks like this:
<?xml version="1.0" encoding="utf-8"?>
<root>
<menuitem label="File">
<menuitem label="_New Note" onSelect="ShowNewNote" />
</menuitem>
</root>
Using XML, we can define each menu item as a menuitem node. If a node contains children (additional menuitems), then the parent node becomes a submenu.
Save notes_menus.xml and open up the file lib/notes.js. Let’s create a new function, CreateMenus, that contains the following code:
// UI
function CreateMenus() {
var menu = air.ui.Menu.createFromXML("notes_menus.xml");
air.ui.Menu.setAsMenu(menu);
}
Place this function right after our $(document).ready function. We’ll then make a call to it at the end of that function, as I’ve done here:
// Bootstrap
$(document).ready(function(){
BindEvents();
CreateMenus();
});
Now when we run our AIR application again, our menus appear in the application’s window (or in the OS X menu bar on a Mac).
Note: Aptana’s Time-saving Keyboard Shortcuts
Press Ctrl+F11 (Cmd-Shift-F11 on the Mac) to run your AIR application using the same configuration as the last time it ran.

There are a number of possible attributes that each of our menu nodes can take, but the only one we’re concerned with is onSelect. When a menu item is selected, this property defines which JavaScript function should be fired:
<menuitem label="_New Note" onSelect="ShowNewNote" />
function ShowNewNote() { ...
The XML snippet above defines that, when New Note is selected (either from a mouse click or via keyboard input), the ShowNewNote JavaScript function should be invoked.
We can also add accessibility features to our menus—for example, we can add keyboard shortcuts, Keyboard shortcuts are specified using the underscore character, as I’ve done in the label property:
<menuitem label="_New Note" onSelect="ShowNewNote" />
Exploring the File System
Now let’s look at storing and manipulating our data. We’re going to look at databases and file system APIs together, as AIR’s implementation of local SQL databases relies heavily on both. SQLite databases are independent files, and instead of being accessed through a database server, they are manipulated directly by the application. In this case, AIR plays the role of database server for us. However, to get started, we first need to tell AIR where to find the database file by specifying a .db file.
All of the file system APIs live under the air.File namespace. There are two important predefined paths for our AIR application: the application directory and application storage directory.
- The application directory is the folder in which the application itself is stored.
- The application storage directory is a folder created by AIR for this particular application (and this application only) to store data.
The application directory is actually read-only, so if we want to edit our database, we’ll need to place the database file in the storage directory. As we’re distributing our skeleton database as part of the application, we’ll need to copy our database template (notes_base.db) to the storage directory on-the-fly.
Connecting to the Database
To open a connection to the database, we use the following code:
var db = new air.SQLConnection();
try
{
db.open(air.File.applicationStorageDirectory.resolvePath("notes.db"));
}
catch (error)
{
air.trace("DB error:", error.message);
air.trace("Details:", error.details);
}
Note: Printing Trace Statements in AIR
To print to the AIR debug console, or to the console view in Aptana, use the function air.trace().
In the code above, we use the resolvePath function to open a file called notes.db in the application storage directory. However, before we can access this file, we first need to make a copy of our template SQLite database, contained in the notes_base.db file in our application directory. The following code achieves this:
var dbFile = air.File.applicationStorageDirectory.resolvePath("notes.db");
//In production, uncomment the if block to maintain the database.
//if (!dbFile.exists) {
var dbTemplate =
air.File.applicationDirectory.resolvePath("notes_base.db");
dbTemplate.copyTo(dbFile, true);
//}
When using resolvePath to resolve a path on the file system, it’s not necessary to first check whether the file exists—the exists property will indicate whether or not the file can be found. As you can see above, we check this property in the production-ready version of our app. It’s useful in testing mode to revert to our database template each time the application is launched—but we certainly wouldn’t want to replace the database with a blank template when dealing with real data!
Let’s pull all of these pieces together into a single function called SetupDB, which looks like this:
var db = new air.SQLConnection();
function SetupDB() {
var dbFile = air.File.applicationStorageDirectory.resolvePath("notes.db");
//In production, uncomment the if block to maintain the database.
//if (!dbFile.exists) {
var dbTemplate = air.File.applicationDirectory.resolvePath("notes_base.db");
dbTemplate.copyTo(dbFile, true);
//}
try
{
db.open(dbFile);
}
catch (error)
{
air.trace("DB error:", error.message);
air.trace("Details:", error.details);
}
}
Place this function at the end of your notes.js file, and while you’re there, add a call to this new method from within our $(document).ready() function (which lives at the top of the file).