Saving class data to disk in AIR

For eBay Desktop we wrote a simple utility for reading and writing classes directly to disk. Using this method we’re able to load the users existing data from disk, or if its unavailable, create a new class with all the defaults.

First up is the utility class AIRUtils

package com.effectiveui.util
{
	import flash.filesystem.File;
	import flash.filesystem.FileMode;
	import flash.filesystem.FileStream;

	public class AIRUtils
	{
		public static function readFileObject(fil:File):Object{
			var amf:Object;
			if(fil.exists){
				var stream:FileStream = new FileStream();
				stream.open(fil, FileMode.READ);
				amf = stream.readObject();
				stream.close();
			}
			return amf;
		}
		public static function writeFileObject(fil:File, obj:Object):Object{
			var stream:FileStream = new FileStream();
			stream.open(fil, FileMode.WRITE);
			stream.writeObject(obj);
			stream.close();
			return obj;
		}
	}
}

These 2 static methods act as simple helper wrappers to read and write classes out to disk.

The example class we want to save below looks very similar to the type of class we use inside eBay Desktop.

package com.ebay.model
{
	import com.ebay.model.SearchPreferences;

	[Bindable]
	[RemoteClass(alias="com.ebay.model.UserPreferencesModel")]
	public class UserPreferencesModel
	{
		public static const STANDARD:String = "standard";
		public static const SKINNY:String = "skinny";

		public var logoutOnClose:Boolean = false;
		public var viewState = STANDARD;
		public var globalSearchFilters:SearchPreferences = new SearchPreferences();

		private var _maxHistoryItems:uint = 1000;
		public function set maxHistoryItems(max:uint):void{
		   _maxHistoryItems = max;
		}
		public function get maxHistoryItems():uint{
		   return _maxHistoryItems;
		}
	}
}

In order to write a class out to disk and read it back in, you have to add RemoteClass metadata to it. It doesn’t matter what the value is it just needs to be unique to the application. This provides Flash with an identifier for linking amf data to class definitions when its loaded back in. It doesn’t matter what value you put in the RemoteClass tag, but the best practice is to use the class name of the model you’re saving. Additionally, in this example we’d need to make sure both UserPreferencesModel and SearchPreferences model have RemoteClass metadata, since caching the UserPrefencesModel will automatically attempt to cache the SearchPreferences as a child model.
Only public variables will be cached to disk, so both the static values and the private var will be thrown out, but the public getter/setters will be cached.

Interacting with the cached data using AIRUtils looks like this.

private var fileRef:File = File.applicationStorageDirectory.resolvePath("UserPreferences.dat");
private var userPrefs:UserPreferencesModel;

private function loadPrefs():void{
	userPrefs = AIRUtils.readFileObject(fileRef) as UserPreferencesModel || new UserPreferencesModel();
}
private function savePrefs():void{
	AIRUtils.writeFileObject(fileRef, userPrefs);
}

When loadPrefs() is called, the cached class is read in and cast as a UserPreferencesModel. If no file exists in the users cache, we end up creating a new class and the defaults defined in the class will be used. This ensures we have something to work with, all within 1 tidy line of code. To save out the file we just pass the file reference and the class instance to AIRUtils.writeFileObject() and we’re done.