Ultrashock Tutorials > Flash MX > SharedObjects  
 
by: Gregory Burch, http://radio.weblogs.com/0107886/


Source File

 
Using SharedObjects
 

What are Local SharedObjects

With the introduction of the Macromedia Flash 6 player, developers now have the ability to store data on the client, similar to cookies. SharedObjects exist in two forms, local and remote. This article will only discuss local SharedObjects. Remote SharedObjects are used with the Flash Communication Server. A local SharedObject requires nothing more than the Macromedia Flash 6 player. In many cases SharedObjects offer an interesting alternative to cookies that developers have not thought about.

Storing Simple Data

Storing simple data in a SharedObject is a fairly simple task. To start we will walk through storing a user-defined object in a SharedObject, then we will talk about storing instances of built-in classes in them. A user-defined object is created like this:

myObj = new Object(); //or
myObj = {};

Storing myObj in a SharedObject is simple. First you need to create the reference to the local SharedObject by doing the following:

myLSO = SharedObject.getLocal("foo");

The first parameter of the getLocal() method is the name the SharedObject is stored under-in this case, "foo". The getLocal() method also accepts an optional second argument, the path given as a string. Most of the time you will not use this argument unless you are using the same SharedObject among several SWFs Now to store data in the SharedObject you will need to access the data property of the SharedObject. This is where all persistent data goes. So start by creating the SharedObject reference, then the object you want to store, and finally storing that in the data property. An example is below:

myLSO = SharedObject.getLocal("foo");

myObj = {};
myObj.fname = "Jessica";
myObj.lname = "Smith";

myLSO.data.myObj = myObj

myObj will now be persistent. You can access it at a later time just by accessing the data property of the SharedObject. Another example is below. This example checks to see if any data is stored in the SharedObject; if there isn't, it puts some there. However, if data exists, it is traced to the Output window. After placing this code in frame 1 of an empty movie you should run it once, and then again. The second time you will see it trace the data you stored.

myLSO = SharedObject.getLocal("foo");

if(myLSO.data.myObj == undefined){
   trace("Storing Data, run me again");

   myObj = {};
   myObj.fname = "Jessica";
   myObj.lname = "Smith";
   myLSO.data.myObj = myObj;

}else{
   trace("Retrieving Data");

   var firstname = myLSO.data.myObj.fname;
   var lastname = myLSO.data.myObj.lname;

   trace("First Name: "+firstname);
   trace("Last Name: "+lastname);

}

Another example can be found in the SimpleData.fla file, which uses the same principle in the context of simple user authentication.

You can also store most built-in types: Array, Boolean, Date, Number, Object, String, XML, undefined, and null.

You do this just how you did above. You instantiate your object, and then store it in the data property. Below is an example in which a Date instance is stored in a SharedObject:

myLSO = SharedObject.getLocal("DateStorage");

myDate = new Date();
myLSO.data.myDate = myDate;

Later, when you read myDate back in, it will still be an instance of the Date class. You can find an example of storing built-in classes in the BuiltIn.fla file.

Storing Complex Data

When you are storing complex data that usually means you are storing custom class instances. In the first place, you have to realize that when you store instances of a custom class in a SharedObject you are not storing the class definition, just the data from that instance. This means that if you tried to call a method on the data at a later time, it would not work. However, with a little trickery, Flash is smart enough to make it look like you stored the entire class. To start, you will want to create your custom class. We will create a User class below that has some simple methods for getting and setting several properties.

function User(){}

User.prototype.username = "";
User.prototype.isAuthenticated = false;

User.prototype.setUsername = function(username){
   this.username = username;
}

User.prototype.getUsername = function(){
   return this.username;
}

User.prototype.setAuthenticated = function(isAuthenticated){
   this.isAuthenticated = isAuthenticated;
}

User.prototype.getAuthenticated = function(){
   return this.isAuthenticated;
}

robert = new User();
robert.setUsername(“rpenner”);
robert.setAuthenticated(false);

So as you can see this class will represent a user. The user has two properties, a username and whether or not they have been authenticated. Now you have to register this class so Flash knows what to do with the data the next time it reads it from the SharedObject.

Object.registerClass("User",User);

You don’t have to completely understand what this does behind the scenes. Just make sure that if you are storing a custom class for later use that you do not skip this step. It is always the same syntax, the first parameter is the string name of the class and the second is the class itself. If you have made components in Macromedia FlashMX than you will recognize this syntax.

Finally, you can test the process of saving your instance, then retrieving it and calling its methods. In the following example, two User instances are created, Greg and Jessica, saved to a local SharedObject, then retrieved. The methods of the User class function properly on the retrieved objects because we used Object.registerClass().

// include User class definition here
Object.registerClass("User",User);

lso = SharedObject.getLocal("complexUsers");

if(lso.data.users == undefined){
   lso.data.users = [];

   Greg = new User();
   Greg.setUsername("gburch");
   Greg.setAuthenticated(true);

   Jessica = new User();
   Jessica.setUsername("jsmith");
   Jessica.setAuthenticated(false);

   lso.data.users.push(Greg);
   lso.data.users.push(Jessica);

   trace("Created Users");

}else{

   var users = lso.data.users;
   var len = users.length;
  
   for(var i = 0; i<len; i++){
      var user = users[i];
  
      trace("****User "+i+"****");
      trace("Username: "+user.getUsername());
      trace("Authenticated: "+user.getAuthenticated());
      trace("");
   
   }
}

You can see the complete example in the ComplexData.fla

One Catch - Order of Initialization

There is one catch to storing custom class instances in a SharedObject, the order in which data is put in and in which the constructor is called. The stored data is put back into the instance before the constructor is called, so there is the possibility of overwriting variables in the constructor.

It is good practice to store all class members in the prototype. Of course you cannot do this with data types that are passed by reference and not by value, i.e. objects and arrays. In the User class sample this problem is not apparent because there is nothing done in the constructor. However if you had a class that managed users, and stored them in an array then you would run into this. The work-around for this is to store a variable that informs your constructor that this class is being deserialized. Below is an example:

// include User class definition here

function UserManagement(){
   if(this._deserialized) return;

   this._deserialized = true;
   this.users = [];
}

UserManagement.prototype.addUser = function(user){
   this.users.push(user);
}

UserManagement.prototype.getUsers = function(){
   return this.users;
}

Object.registerClass("UserManagement",UserManagement);

myLSO = SharedObject.getLocal("complexData");

if(!myLSO.data.users){
   var users = new UserManagement();

   var greg = new User();
   greg.setUsername("gburch");

   var robert = new User();
   robert.setUsername("rpenner");

   var jessica = new User();
   jessica.setUsername("jsmith");

   users.addUser(greg);
   users.addUser(robert);
   users.addUser(jessica);

   myLSO.data.users = users;

}else{
var users = myLSO.data.users.getUsers();

   for(var i = 0; i<users.length; i++){
      trace("User: "+users[i].getUsername());
   }

}

In the code above we use the variable _deserialized is used to track whether this class is being deserialized or not. If it is it returns immediately and does not create the users array. We use the addUser() method to add a few instances of the User class to the UserManagement instance. When the SWF is run the second time you will see it output the username of each user. You can find this example in the ConstructorCheck.fla.

The above example is only one way of doing things. Many in the community have looked into the best way to do this, but there is no definite answer. The above code will work without problem but in the future it would be nice to have an event fire on deserialization. Nigel Pegg mentioned using onDeserialize as the event name. I like this idea as it leaves more hooks for future expansion, such as when extending the class. To add this to the above code is very simple. Instead of simply returning you call a method first. Replace the Constructor code of the UserManagement class with the following.

function UserManagement(){

   if(this._deserialized){
      return this.onDeserialize();
   }

   this._deserialized = true;
   this.users = [];
}

You can also define the onDeserialize() method.

UserManagement.prototype.onDeserialize = function(){
   trace("Deserialized");
   
   for(var i = 0; i<this.users.length; i++){
      trace("User: "+this.users[i].getUsername());
   }

}

A complete example can be found in the ConstructorEvent.fla

When to use Local SharedObject’s instead of Cookies

The best way to figure out if you should store data in a local SharedObject is to figure out whether anything but Flash would ever need direct access to it. Direct access meaning without the help of Flash. The most common place to replace cookies with local SharedObject’s is storing UI customization data. UI customization data is defined as anything that the user inputs to make their UI unique. This could be their name, or the color of the background, or anything else that changes the UI just for them. You can see a very simple example in the file Greeting.fla

There are a couple of good reasons for this. First, usually when you store UI data it is specific to Flash. This means that you won’t have any problems in the future with other systems needing access to that data. Second, when setting up the UI you don’t want any network lag. If data is stored in a cookie most people use server-side scripts to access it so there will be some lag. You can use Javascript but it can become very messy, very quickly.

Note: You can take a look at the example file using CookieJavascriptExample.fla, which shows an example of using Javascript to get and set cookies. Notice that a class has been created to make it a little cleaner, but the innards are still very messy. Just changing the name of the movie and republishing will break it. If you dig into it, you will see that getting the cookie from Javascript is not immediate; the class must poll for the data to be returned. When this cookie is finally returned, you still have to parse it. Cookies are stored in key,value pairs separated by &’s. These are just a couple reasons that replacing Cookies with SharedObjects makes life easier.

Another, more complex example, would be storing data that can be cast (in a way) back to a class. For example you may have a User class that provides methods and properties for easily maintaining user info. You could have methods such as setUserName(), getUserName(), setAuthenticated(), getAuthenticated() etc. You can store the info from this class in a local SharedObject for later use. When you do need this data back you can easily cast it right back to the User class. This specific example was discussed in the previous section and a sample can be found in the ComplexData.fla.

Most of the Built-In classes already support serialization. So when a user registers you can store an actual Date object to represent the date they joined. There is a complete example of storing the built-in data types in the file BuiltIn.fla.

But of course, there are times when you should not use SharedObject’s instead of cookies.

When not to use Local SharedObject’s instead of Cookies

While there are lots of places to use local SharedObject’s there are also many situations that will do better off with cookies. The broadest example would be a hybrid Flash site that stores user data in sessions. If this data were moved into a SharedObject than it would be impossible to get that data from a full html page. It is not a terrible thing that you won’t be using SharedObject’s because most of its benefits are lost in this situation. You can still utilize them in some places, just be careful about where. When you have hybrid pages, you are going to be doing full-page refreshes, so you can pass the cookie values in through FlashVars. This will avoid the network lag with the UI data.

Note: More info on FlashVars can be found at http://www.ultrashock.com/tutorials/flashmx/FlashVars.html

Advantages of SharedObjects

  • Can be used on the client in an application that may not be run in a browser
  • Can store complex data
  • Can store any of the built-in objects.
  • Can be accessed directly from Flash
  • Simple straightforward syntax
  • Easy to manage>
  • User is prompted if more space is needed. While with cookies if they are off, too bad
  • If the user clears cookies, SharedObject’s are not removed with them
  • No network lag
  • Ability to have unlimited storage

Disadvantages of Shared Objects

  • No expiration
  • Cannot be accessed by anything other than Flash
  • Migration can be difficult

SharedObjects – Security,Privacy, and Management

When it comes to these three topics, SharedObjects are exactly like Cookies.

Domains
Of course to most, security is always a concern. SharedObjects are restricted by domain; domainA cannot read SharedObjects from domainB. SharedObjects can be accessed by other movies within the same domain and sub-domains.

Privacy
A SharedObject object is simply a storage mechanism. There is no way to read from a users hard drive or write outside of the SharedObject’s storage folder. Sensitive user information cannot be received through them such as email addresses, names, etc. Macromedia does not have access to any of this info unless Macromedia itself stored it.

Management
SharedObject’s are not cleared with cookies. Flash has its own management interface. You can get to the Local Storage settings by right clicking on any Flash movie and selecting “Settings..” The default allotted space is 100kb. On Windows2000 and XP local SharedObjects are stored under the folder:

C:\Documents and Settings\username\Application Data\Macromedia\Flash

If you want to remove data that a website has stored you can either remove them by hand from here or visit the site and open the Local Storage settings.

Examples

  • BuiltIn.fla – Example showing how to store built-in classes in a SharedObject
  • CookieJavascriptExample.fla – Example file that uses Javascript instead of SharedObjects to store cookies
  • SnippetOrganizer.fla – A sample application that allows you to organize and save code snippets
  • ComplexData.fla – Example showing how to store custom classes in local SharedObjects.
  • ConstructorCheck.fla - Example of checking for deserialization
  • ConstructorEvent.fla - Alternate example of checking for deserialization
  • Greeting.fla – Example showing how the UI changes when some customization data is set. In this case it’s a simple name input
  • SimpleData.fla – Example showing the most basic storage technique with local SharedObjects

Other Resources

The best resource is the Reference panel in FlashMX. To open this panel go to Window>Reference (or SHIFT+F1) You can find the reference on SharedObjects under the Communications folder on the left.

 
©2002 Ultrashock.com Inc. - All rights reserved