Windows 8 apps for the PhoneGap developer-Files

(This is part 5 in my series on Windows 8 apps for the PhoneGap developer.)

Most applications save and read data. That can be just something as simple as a counter, the users credentials, or game high score. Or it can be complex sets of data the user manipulates as “documents” or all the data your application manages as a big set. At some point, you have to persist that data to the user’s device, whether it’s as a cache of cloud stored information or not.

PhoneGap provides file access in a few different ways. Let’s start with the simplest scenario and work up from there.

If you are managing application data in a PhoneGap app, chances are you are writing to and reading to a file on the local file system. The name and location of the file are controlled by you, and you’re saving that file in an application specific location on the system. You might be serializing JSON data to it or otherwise saving name/value pairs as settings for your app.

With Windows 8 JavaScript apps, you can store settings in a couple of different ways. The dead easy way is to just to do this.

 var localSettings = Windows.Storage.ApplicationData.current.localSettings;
localSettings.values["my setting"] = "this is a setting";

That’s it! Now you have a setting stored for the app locally. If the app is uninstalled and reinstalled, the setting is cleared. No other app can access this app’s settings. You can store strings and integers and such this way. Reading it is easy too.

 
// Simple setting

var value = localSettings.values["my setting"];
        
if (!value)
{
    // No data
}
else
{
    // Access data in value
}

You can also collect several values together as a composite value to make them easy to pass around as arguments if needed.

 // Composite setting

var composite = new Windows.Storage.ApplicationDataCompositeValue();
composite["loginAttemptCount"] = 1;
composite["lastUserName"] = "johndoe123";

localSettings.values["loginAttempts"] = composite;

You can also have settings containers for more complex scenarios.

In addition to local settings, you can have roaming settings. These are tied to a combination of the user’s Microsoft account (formerly Live ID) and your application. So if I run your app on multiple computers on which I have an account, anything you save to roaming settings is automatically synchronized between computers. This is great for high scores and furthest level reached in games – it shouldn’t matter which machine I unlock the berzerker level on, I should have access to that on every machine with my credentials and your app. These are so simple to use, and you don’t have to set up any cloud services or anything! Here are the example above changed to be roaming.

 var roamingSettings = Windows.Storage.ApplicationData.current.roamingSettings;
roamingSettings.values["my setting"] = "this is a setting";

There is a pretty non-intrusive synching engine running to handle roaming profile. But sometimes you need stuff to sync very quickly. To do that, simply create a roaming setting (or composite) named HighPriority. That will sync within a minute!

If your app is running and you want to see if new roaming data is available that was saved on another machine, you can subscribe to the data updated event. Then you can update your app without the user even having to hit a refresh button.

 var applicationData = Windows.Storage.ApplicationData.current;
 
function initialize() 
{
    applicationData.addEventListener("datachanged", datachangeHandler);
}

function dataChangeHandler(eventArgs)
{
    // TODO: Refresh your data
}

Be sure to read the guidelines for roaming application data for best practices on size, the user experience, size limits, and more.

Another great feature about setting is you can version them. That is, if version 1 of  your app uses a certain set of settings data, and in version 2 of your app, you change that around (add or remove settings, change data types, whatever) you can have your app check the version of the settings on the user’s machine. That way you know if you can just rely on them as is or if you need to update them.

If you have more than simple settings to save, say, files like pictures or something, you can use the localFolder (or roamingFolder if you want that) property of application data. This saves in a specific location for each app. You write and read files using  methods in the Windows.Storage.FileIO class, like so.

 function writeSpeech() {
   localFolder.createFileAsync("gettysburg.txt", Windows.Storage.CreationCollisionOption.replaceExisting)
      .then(function (sampleFile) {
         var speechText = "Four score and seven years ago, etc...";
         return Windows.Storage.FileIO.writeTextAsync(sampleFile, speechText );
      }).done(function () {      
      });
}

Note the writing is asynchronous using the Promise pattern with .then syntax.

Reading is easy too.

 function readSpeech() {
   localFolder.getFileAsync("gettysburg.txt")
      .then(function (sampleFile) {
         return Windows.Storage.FileIO.readTextAsync(sampleFile);
      }).done(function (speechText) {
         // Data is contained in speechText
      }, function () {
         // speechText not found, grab an envelope and start writing again!
      });
}

These are the same reading and writing techniques used for another option for saving data, which is managing things as files the user interacts with. This could be an image file the make with your app and want to save, or a checking account register that’s saved as a complete data file. The point is, in this technique, the user specifies the file name when saving or opening. Just like when working with Word, Excel or PowerPoint for example. You can access these files either directly with code, or with a File picker.

In code, you can use classes in the Windows.Storage namespace. This is probably most similar to a PhoneGap application in that you can request directories and/or files, create them, read them, move them, etc. For example, if you want to get to files the user has stored in their My Documents folder, you can use the documentsLibrary property of the Windows.Storage.KnownFolders class to get to those. That is, assuming you’ve specified the right capability in your app’s manifest. You did remember to do that. Right?

DocumentsLibraryAccess

You use methods like getItemsAsync to get a collection you can .forEach over and process. You can search for files and create folder queries with some interesting options as well, like grouping pictures by month, grouping music by artist, etc.

If you don’t want to iterate, list and manage all the file names yourself, take a look at the file picker technique. With that approach, the user uses a Windows 8 common set of screens, selection and navigation paradigms to specify a file name or folder name to your app for it to save to, or to open. Here’s an example letting the user pick a file and then having your app write data to it.

 // Create the picker object and set options
var savePicker = new Windows.Storage.Pickers.FileSavePicker();
savePicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.documentsLibrary;
// Dropdown of file types the user can save the file as
savePicker.fileTypeChoices.insert("Plain Text", [".txt"]);
// Default file name if the user does not type one in or select a file to replace
savePicker.suggestedFileName = "New Document";

savePicker.pickSaveFileAsync().then(function (file) {
    if (file) {
        // Prevent updates to the remote version of the file until we finish making changes and call CompleteUpdatesAsync.
        Windows.Storage.CachedFileManager.deferUpdates(file);
        // write to file
        Windows.Storage.FileIO.writeTextAsync(file, "windows 8 rocks!").done(function () {
            // Let Windows know that we're finished changing the file so the other app can update the remote version of the file.
            // Completing updates may require Windows to ask for user input.
            Windows.Storage.CachedFileManager.completeUpdatesAsync(file).done(function (updateStatus) {
                if (updateStatus === Windows.Storage.Provider.FileUpdateStatus.complete) {
                  //success!
                } else {
                  //epic fail
                }
            });
        });
    } else {
        //user cancelled
    }
});
   }

Interestingly, with this approach you don’t need the Documents Library Access capability enabled, because it’s the user who ultimately chooses the file. Here’s what the file picker looks like.

filepicker 

What you might not realize is that in addition to being able to use the file pickers to allow the user to choose files on their computer, they can also choose files for reading or saving to that are provided by other apps on the system. For example, I have SkyDrive app installed on mine, so I can tell the app to save there.

skydrive picker

Your app just receives a reference to the file to read/write from. It doesn’t need to have any knowledge of how SkyDrive works or what my credentials are in order to have access to the file. All the user has to do is have the SkyDrive app (or any other app that provides files) installed on their system and have logged into it previously. Quite a powerful capability!

Browse through all these quickstarts for working with data and files, and you’ll uncover great programming capabilities enabling scenarios you’ve never even thought about!