The Story of State, Part 1: App Data and Settings - Programming Windows Store Apps with HTML CSS and JavaSript (2014)

Programming Windows Store Apps with HTML CSS and JavaSript(2014)

Chapter 10
The Story of State, Part 1: App Data and Settings

Imagine when you travel if every hotel room you stayed in was automatically configured exactly how you like it—the right pillows and sheets, the right chairs, and the right food in the minibar rather than atrociously expensive and obscenely small snack tins. If you’re sufficiently wealthy, of course, you can send people ahead of you to arrange these things, but such luxury remains naught but a dream for most of us.

Software isn’t so bound by these limitations, fortunately. Sending agents on ahead doesn’t involve booking airfare for them, providing for their income and healthcare, and contributing to their retirement plans. All it takes is a little connectivity, some cloud services, and—voila!—all of your settings can automatically travel with you—that is, between the different devices you’re using.

This experience of statefulness, as it’s called, is built right into Windows. You automatically expect that systemwide settings persist from session to session, so you don’t have to reconfigure your profile picture, start screen preferences, Internet favorites, desktop theme, saved credentials, wireless network connections, printers, and so forth. But statefulness is not limited to one device. When you use a Microsoft account to log into Windows on a trusted PC, these settings are securely stored in the cloud and automatically transferred to other trusted devices where you use the same account (you can control them through PC Settings > OneDrive > Sync Settings). I was pleasantly surprised during the development of Windows 8 that I no longer needed to manually transfer all this data when I updated my machine from one release preview to another! Indeed, I was very pleased when I got a new laptop, turned it on for the first time at my in-law’s house, and found that it had already connected to their WiFi access point using automatically roamed information.

With such an experience in place for system settings, users expect similar stateful behavior from apps. To continue the analogy, when we travel to new places and stay in hotels, most of us accept that we’ll spend a little time upon arrival unpacking our things and setting up the room to our tastes. On the other hand, we expect the complete opposite from our homes: we expect continuity, which is to say, statefulness. Having moved twice in one year myself while writing the first edition of this book (once to a temporary home while our permanent home was being completed), I deeply appreciate the virtues of statefulness. Imagine that everything in your home got repacked into boxes every time you left, such that you had to spend hours, days, or weeks unpacking it all again! No, home is the place where we expect things to stay put, even if we do leave for a time. (I think this is exactly why many people enjoy traveling in a motor home.)

Windows Store apps should maintain a sense of continuity across sessions, across devices, and across process lifecycle events such as when an app is suspended, terminated by the system, and later restarted. In this way, apps feel more like a home than a temporary resting place; they become a place where users come to relax with the content they care about. And the less work users need to do to enjoy that experience, the better.

For stateful behavior across devices, a consistent experience means that app-specific settings on one device will appropriately roam to the same app installed on other devices. I say “appropriately” because some settings don’t make sense to roam, especially those that are particular to the hardware in the device. On the other hand, if I configure email accounts in an app on one machine, I would certainly hope those show up on others! (I can’t tell you how many times I’ve had to repeatedly set up my four active email accounts in Outlook on the desktop—ack!) In short, as a user I want my transition between devices—on the system level and with apps—to be both transparent and seamless, such that even newly installed apps that I’ve used on another device start up in an already-initialized state.

Managing statefulness in an app means a number of things. It means deciding what information is local to a device and what roams between devices. It means understanding when state is restored and when an app starts afresh. It means understanding the difference between app data—settings and configurations that are tied to the existence of an app—and user data—which lives independently. It also includes knowing how best to save certain kinds of state (such as credentials and file access permissions) and how to use state to provide a good offline experience and to improve performance through caching. We’ll explore all of these aspects in this chapter, and the effort you invest in these can make a real difference in how users perceive your app and the ratings and reviews they’ll give it in the Windows Store.

Many such settings will be completely internal to an app’s code, but others can and should be directly configurable by the user. In the past, user configuration has given rise to an oft-bewildering array of nested dialog boxes with multiple tabs, each of which is adorned with buttons, popup menus, and long hierarchies of check boxes and radio buttons. As a result, there’s been little consistency in configuration UI. Even the simple matter of where such options are located on menus has varied between Tools/Options, Edit/Preferences, and File/Info commands, among others!

Fortunately, the designers of Windows 8 recognized that most apps have settings of some kind—in fact, Windows guarantees this for all apps you acquire from the Windows Store. Thus they included Settings on the Charms bar alongside the other near-ubiquitous Search, Share, and Devices charms. For one thing, the Settings charm eliminates the need for users to remember where a particular app’s settings are located, and app designers don’t need to wonder how, exactly, to integrate settings into their overall content flow and navigation hierarchy. By being placed in the Settings charm, settings are effectively removed from an app’s content structure, thereby simplifying the app’s overall design. The app needs only to provide distinct pages or panes that are displayed when the user invokes the charm.

Clearly, an app’s state and its Settings UI are intimately connected, as we’ll see in this chapter. Along the way, we’ll also have the opportunity to look a bit at the storage and file APIs in WinRT, along with some of the WinJS file I/O helpers and other storage options. Working with files, though, is much more relevant to user data, so we’ll wait to complete the subject in Chapter 11, “The Story of State, Part 2: User Data, Files, and OneDrive.”

The Story of State

An app’s state—by which I mean persistent local and roaming state together—is clearly the kingpin of a stateful experience. State has a much longer lifetime than the app itself: it remains persistent, as it should, when an app isn’t running and persists across different versions of the app. The state version is, in fact, managed separately from the app version, and roaming state will also persist in the cloud for some time even if the user doesn’t have the app installed on any of their devices.

For all these reasons, it’s helpful when telling the story of stateful apps to take the perspective of the state itself and ask questions like these:

• What kinds of state do we need to concern ourselves with?

• Where does state live?

• What affects and modifies that state?

To clearly understand the first question, let’s first briefly revisit user data again. User data like documents, pictures, drawings, designs, music, videos, playlists, and so forth are things that a user creates and consume with an app but are not dependent on the app itself. User data implies that any number of apps might be able to load and manipulate it, and such data always remains on a system irrespective of the existence of apps. For this reason, user data is not part of an app’s state. For example, while the paths or URIs of documents and other files might be remembered in a list of favorites or recently opened documents, the actual contents of those files are separate from that state.

User data doesn’t have a strong relationship to app lifecycle events either: it’s typically saved explicitly through a user-invoked command or implicitly on events like visibilitychange, rather than within a suspending handler. Again, the app might remember which file is currently loaded as part of its session state during suspending, but the file contents itself should be saved outside of this event, especially considering that you have only five seconds to complete whatever work is necessary!

Excluding whatever falls into the category of user data, whatever is left that’s needed for an app to run and maintain its statefulness is what we refer to as app state. Such state is maintained on a per-user basis, is tied to the existence of a specific app, and is accessible by that app exclusively. As we’ve seen earlier in this book, app state is typically stored in user-specific folders that are wholly removed from the file system when an app is uninstalled (though of course roaming state still persists in the cloud and is downloaded again if the app is reinstalled). For this reason, never store anything in app state that the user might want outside your app. Similarly, avoid using document and media libraries to store state that wouldn’t be meaningful to the user if the app is uninstalled.

App state falls into three basic categories:

Transient session state State that normally resides in memory but is saved when the app is suspended in order to restore it after a possible termination. This includes unsaved form data, page navigation history, selection state, and so forth (but not window size, as that’s always refreshed when an app is reactivated). As we saw in Chapter 3, “App Anatomy and Performance Fundamentals,” being restarted after suspend/terminate is the only case in which an app restores transient session state. Session state is typically saved incrementally (as the state changes) or within the suspending or checkpoint events.

Local state State that is typicallyloaded when an app is launched and that is specific to a device (and therefore not roamed). Local state includes lists of recently viewed items, temporary files and caches, and various behavioral settings that appear in the Settings panel like display units, preferred video formats, device-specific configurations, and so on. Local state is typically saved when it’s changed because it’s not directly tied to lifecycle events.

Roaming state State that is shared between the same app running on multiple Windows devices where the same user is logged in, such as favorites, viewing position within videos, account configurations, game scores and progress, URIs for important files on cloud storage locations, perhaps some saved searches or queries, etc. Like local state, these might be manipulated through the Settings panel, but roaming state is subject to an overall quota (and, when exceeded, behaves like local state). Roaming state is also best saved when values are changed; we’ll see more details on how this works later.

All this state is stored in the app data folders created when your app package is installed (roaming state is synced to the cloud from there). In these folders you can use settings containers for key-value properties (through the Windows.Storage.ApplicationData APIs) or create files with whatever structure you want (using the WinJS or Windows.Storage APIs).

At the same time, some types of state live elsewhere in the system, specifically those managed by other APIs. System-managed HTML5 features like IndexedDB and AppCache don’t necessarily use your app data folders but are automatically cleaned up when the app is uninstalled. Permissions to programmatically access files and folders is another case. By default, an app can access only those files and folders in its app data or in those libraries for which it has declared a capability in its manifest. For all other arbitrary locations, permission is obtained only through user interaction with the file picker, because that implies user consent. To preserve programmatic access across app sessions, however, you need to save those permissions along with the file path, which is the purpose of the Windows.-Storage.AccessCache APIs. Your app’s section of the access cache, in other words, is considered part of your overall local state. (File pickers and the access cache are covered in Chapter 11.)

The other concern are credentials that you’ve collected from a user and would like to retrieve in the future. Never directly save credentials in your app data. Instead, use the credential locker API in Windows.Security.Credentials.PasswordVault, which we’ve already seen in Chapter 4, “Web Content and Services.” The contents of the locker are isolated between apps and are roamed between a user’s trusted PCs, so this constitutes part of roaming state. (Users can elect to not roam credentials by turning off PC Settings > OneDrive > Sync Settings > Other Settings > Passwords, in which case the credential locker still maintains them locally.)

Taken altogether, the different APIs I’ve just mentioned are those that you (or a third-party library) use to save, modify, and manage state, both from the running app and from background tasks.

Beyond this, two other events not under an app’s control (that is, outside of the running app or a background task) can affect app state:

Disk Cleanup If the user runs this tool and elects to clean up Temporary Files, older files in the app’s TemporaryFolder might be deleted if disk space is low. The exact policy for when files get deleted is not documented, but the idea is that an app should always be ready to regenerate temp files if they’ve disappeared. Windows does this kind of lazy cleanup to avoid just blowing away newer and smaller app caches when it’s not really necessary to reclaim the space.

Roaming from the cloud If newer roaming state has been uploaded to the cloud from another device and then synced with the local device (in the app data RoamingState folder), a running app will receive a Windows.Storage.ApplicationData.ondatachanged event.

To bring all of this together, Figure 10-1 illustrates the different kinds of state, where they are located, what affects them, and the app lifecycle boundaries across which they persist. The APIs that we use to work with these forms of state is what makes up the bulk of this chapter. A good portion of this chapter, starting with “Settings Pane and UI,” is concerned with how to surface those parts of your state that are user-configurable.

images

FIGURE 10-1 Different forms and locations of app data, how they persist across app lifecycle events, and the APIs that modify them.

App Data Locations

Now that we understand what kinds of information make up app state, let’s delve deeper into the question, “Where does state live?” To review, when Windows installs an app for a user (and all Windows Store apps are accessible to only the user who installed them), it automatically creates a folder for the app in the current user’s AppData folder (the one that gets deleted when you uninstall an app). It then creates LocalState, TempState, and RoamingState folders within that app folder. On the file system, if you point Windows Explorer to %localappdata%\packages, you’ll see a bunch of folders for the different apps on your system. If you navigate into any of these, you’ll see these folders along with one called “Settings,” as shown in Figure 10-2 for the built-in Sports app. The figure also shows the varied contents of these folders.

In the LocalState folder of Figure 10-2 you can see a file named _sessionState.json. This is the file where WinJS saves and loads the contents of the WinJS.Application.sessionState object, as we saw in Chapter 3. Because it’s just a text file in JSON format, you can easily open it in Notepad or some other JSON viewer to examine its contents. In fact, if you look at this file for the Sports app (that is, at the JSON file shown in the figure), you’ll see a value like {"lastSuspendTime":1340057531501}. The Sports, News, Weather, and other apps show time-sensitive content, so they save when they were suspended and check elapsed time when they’re resumed. If that time exceeds their refresh intervals, they can go get new data from their associated service. In the case of the Sports app, one of its Settings specifically lets the user set the refresh period.

images

FIGURE 10-2 The Sports app’s AppData folders and their contents.

Note If you look carefully at Figure 10-2, you’ll see that all the app data–related folders, including RoamingState, are in the user’s overall AppData/Local folder. There is also a sibling AppData/Roaming folder, but this applies only to roaming user account settings on intranets, such as when a domain-joined user logs in to another machine on a corporate network. This AppData/Roaming folder has no relationship to the AppData/Local…/RoamingState folder for Windows Store apps.

Programmatically, you refer to these locations in several ways. First, you can use the ms-appdata:/// URI scheme as we saw in Chapter 3, where ms-appdata:///local, ms-appdata:///roaming, andms-appdata:///temp refer to the individual folders and their contents. (Note the triple slashes: it’s a shorthand allowing you to omit the package name.) You can also use the object returned from the Windows.Storage.ApplicationData.current method, which contains all the APIs you need to work with state, as we’ll see.

By the way, you might have some read-only state directly in file in your app package. You can reference these with URIs that just start with /(meaning ms-appx:///). You can also get to them through the StorageFolder object from the Windows.ApplicationModel.Package.current.-installedLocation property. We’ll come back to the StorageFolder class a little later.

App Data APIs (WinRT and WinJS)

We’ve answered the questions of “What kinds of state do we need to concern ourselves with?” and “Where does state live?” Now we can answer the third question, “What affects and modifies that state?”—a subject that will occupy the next 25 pages!

Much of the answer begins with the ApplicationData object that you get from the Windows.Storage.ApplicationData.current property, which is completely configured for your particular app. This object contains the following, where other object types are also found in theWindows.Storage namespace:

• localFolder, temporaryFolder, and roamingFolder Each of these properties is a StorageFolder object that allows you to create whatever files and additional folder structures you want in these locations (but note the roamingStorageQuota below).

• localSettings and roamingSettings These properties are ApplicationDataContainer objects that provide for managing a hierarchy of key-value settings pairs or composite groups of such pairs. All these are stored in the AppData/Settings folder in the settings.dat file.

• roamingStorageQuota This property contains the number of kilobytes that Windows will automatically roam for the app (typically 100). If the total data stored in roamingFolder and roamingSettings exceeds this amount, roaming will be suspended until the amount falls below the quota. You have to track how much data you store yourself if you think you might risk exceeding the quota.

• dataChanged An event indicating the contents of theroamingFolder or roamingSettings have been synchronized from the cloud, in which case an app should re-read its roaming state. It also indicates that some other part of the app (running code or a background task) has called thesignalDataChanged method. (Note: dataChanged is a WinRT event; use removeEvent-Listener as described in Chapter 3 in “WinRT Events and removeEventListener.”)

• signalDataChanged A method that triggers a dataChanged event. This allows you to consolidate local and roaming updates in a single handler for the dataChanged event, including changes that occur in background tasks (that is, calling this method from a background task will trigger the event in the app if the app is not currently suspended.)

• version property and setVersionAsync method These provide for managing the version stamp on your app state. This version applies to the whole of your app state (local, temp, and roaming together); there are not separate versions for each. Note again that state version is a separate matter from app version, because multiple versions of an app can all use the same version of its state (which is to say, the same state structure).

• clearAsync A method that clears out the contents of all AppData folders and settings containers. Use this when you want to reinitialize your default state, which can be especially helpful if you’ve restarted the app because of corrupt state.

• clearAsync(<locality>) A variant of clearAsync that is limited to one locality (local, temp, or roaming), specified by a value from the ApplicationDataLocalityenumeration, such as ApplicationDataLocality.local. In the case of local and roaming, the contents of both the folders and settings containers are cleared; temp affects only the TempState folder.

Let’s now see how to use the APIs here to manage the different kinds of app data, which includes a number of WinJS helpers for the same purpose.

Hint App state APIs generate events in the Event Viewer if you’ve enabled the channel as described in Chapter 3 in “Debug Output, Error Reports, and the Event Viewer.” To summarize, make sure that View > Show Analytics and Debug Logs menu item is checked. Then navigate to Application and Services Log, expand Microsoft/Windows/AppModel-State, and you’ll find Debug and Diagnostic groups. Right-click either or both of these and select Enable Log to record those events.

Settings Containers

For starters, let’s look at the localSettings and roamingSettings properties, which are typically referred to as settings containers. You work with these through the ApplicationDataContainer API, which is relatively simple. Each container has four read-only properties: a name (a string), alocality (again from ApplicationDataLocality, with local and roaming being the only values here), and collections called values and containers.

The top-level containers have empty names; the property will be set for child containers that you create with the createContainer method (and remove with deleteContainer). Those child containers can have other containers as well, allowing you to create a whole settings hierarchy. That said, these settings containers are intended to be used for small amounts of data, like user configurations; any individual setting is limited to 8K and any composite setting (see below) to 64K. With these limits, going beyond about a megabyte of total settings implies a somewhat complex hierarchy, which will be difficult to manage and will certainly slow to access. So don’t be tempted to think of app data settings as a kind of database; other mechanisms like IndexedDB and SQLite are much better suited for that purpose, and you can write however much data you like as files in the various AppData folders (remembering the roaming quota when you write to roamingFolder).

For whatever container you have in hand, its containers collection is an MapView object through which you can enumerate its contents. The values collection, on the other hand, is a WinRT PropertySet object that you can more or less treat as an array. (For details on both of these types, refer to Chapter 6, “Data Binding, Templates, and Collections,” in “Maps and Property Sets.”) The values property in any container is itself read-only, meaning that you can’t assign some other arbitrary array or property set to it but you can still manipulate its contents however you like.

We can see this in the Application data sample, which is a good reference for many of the core app data operations. Scenario 2, for example (js/settings.js), shows the simple use of the localSettings.values array:

var localSettings = Windows.Storage.ApplicationData.current.roamingSettings;

var settingName = "exampleSetting";

var settingValue = "Hello World";

function settingsWriteSetting() {

roamingSettings.values[settingName] = settingValue;

}

function settingsDeleteSetting() {

roamingSettings.values.remove(settingName);

}

Many settings, like that shown above, are just simple key-value pairs, but other settings will be objects with multiple properties. This presents a particular challenge: although you can certainly write and read the individual properties of that object within the values array, what happens if a failure occurs with one of them? That would cause your state to become corrupt.

To guard against this, the app data APIs provide for composite settings, which are groups of individual properties (again limited to 64K) that are guaranteed to be managed as a single unit. It’s like the perfect group consciousness: either we all succeed or we all fail, with nothing in between! That is, if there’s an error reading or writing any part of the composite, the whole composite fails; with roaming, either the whole composite roams or none of it roams.

A composite object is created using Windows.Storage.ApplicationDataCompositeValue, as shown in scenario 4 of the Application data sample (js/compositeSettings.js):

var roamingSettings = Windows.Storage.ApplicationData.current.roamingSettings;

var settingName = "exampleCompositeSetting";

var settingName1 = "one";

var settingName2 = "hello";

function compositeSettingsWriteCompositeSetting() {

var composite = new Windows.Storage.ApplicationDataCompositeValue();

composite[settingName1] = 1; // example value

composite[settingName2] = "world"; // example value

roamingSettings.values[settingName] = composite;

}

function compositeSettingsDeleteCompositeSetting() {

roamingSettings.values.remove(settingName);

}

function compositeSettingsDisplayOutput() {

var composite = roamingSettings.values[settingName];

// ...

}

The ApplicationDataCompositeValue object has, as you can see in the documentation, some additional methods and events to help you manage it, such as clear, insert, and mapchanged.

Composites are, in many ways, like their own kind of settings container, but they just cannot contain additional containers. It’s important to not confuse the two. Child containers within settings are used only to create a hierarchy (refer to scenario 3 in the sample, js/settingsContainer.js). Composites, on the other hand, specifically exist to create more complex groups of settings that act like a single unit, a behavior that is not guaranteed for settings containers themselves.

As noted earlier, these settings are all written to the settings.dat file in your app data Settings folder. It’s also good to know that changes you make to settings containers are automatically saved, though some built-in batching occurs to prevent excessive disk activity when you quickly change a number of values. In any case, you don’t need to worry about the details—the system will make sure they’re always saved before an app is suspended, before the system is shut down, and before roaming settings get synced to the cloud.

State Versioning

As a whole, everything you create in your local, roaming, and temp folders, as well as local and roaming settings, all constitute a version of your app’s state structure. If you change that structure, you’ve created a new version of it.

The version of your state structure is set with ApplicationData.setVersionAsync, the value of which you can retrieve through ApplicationData.version (a read-only property). Windows primarily uses this version especially to manage copies of your app’s roaming state in the cloud—it specifically maintains copies of each version separately, because the user could have different versions of the app that each use a distinct version of the state.79

As I mentioned before, though, remember that state version (controlled through setVersionAsync) is entirely separate from app version (as set in the manifest). You can have versions 1.0.0.0 through 4.3.9.3 of the app use version 1.0.0.0 of app data, or maybe version 3.2.1.9 of the app shifts to version 1.0.1.0 of the app data, and version 4.1.1.3 moves to 1.2.0.0 of the app data. It doesn’t really matter, so long as you keep it all straight and can migrate old versions of the app data to new versions!

Migration happens as part of the setVersionAsync call, whose second parameter is a function to handle the conversion. That is, when an updated app is first activated, it must check the version of its state because the contents of the app data folders and settings containers will have been carried forward from the previous app version. If the app finds an older version of state than it expects, it should call setVersionAsync with its conversion function. That function receives a SetVersionRequest object that contains currentVersion and desiredVersion properties, thereby instructing your function as to what kind of conversion is actually needed. Your code then goes through all your app state and migrates the individual settings and files accordingly. Once you return from the conversion handler, Windows will assume the migration is complete, meaning that it can resync roaming settings and files with the cloud. Of course, because the migration process will often involve asynchronous file I/O operations, you have a deferral mechanism like that we’ve seen with activation. Call the SetVersionRequest.getDeferral method to obtain the deferral object (a SetVersionDeferral), and call its complete method when all your async operations are done. Examples of this can be found in scenario 9 of the Application data sample.

It is also possible to migrate app data as soon as an app update has been installed. For this you use a background task for the servicingComplete trigger. See Chapter 16, “Alive with Activity,” specifically “Background Tasks and Lock Screen Apps.”

Folders, Files, and Streams

The local, roaming, and temp folders of your app data are where you can create whatever "unstructured" state you want, which means creating files and folders with whatever information you want to maintain. It’s high time, then, that we start looking more closely at the file I/O APIs for Windows Store apps, bits and pieces of which we’ve already seen in earlier chapters. Here we'll round out the basics, and then Chapter 11 will provide the rest of the intricate details.

First, know that some APIs like URL.createObjectURL—which work with what are known as blobs—make it possible to do many things in an app without having to descend to the level of file I/O at all! We’ve already seen how to use this to set the src of an img element, and the same works for other elements like audio and video. The file I/O operations involved with such elements are encapsulated within createObjectURL. You can use blobs in other ways as well. You can convert a canvas element with canvas.msToBlob into something you can assign to an img element; similarly, you can obtain a binary blob from an HTTP request, save it to a file, and then source an img from that. We’ll see some more of this in Chapter 13, “Media,” and you can refer to the Using a blob to save and load content sample for more. Also, see “Q&A on Files, Buffers, Streams, and Blobs” later in this chapter.

For working directly with files, let’s get a bearing on what we have at our disposal. The core WinRT APIs for files live within the Windows.Storage namespace. The key players are the StorageFolder and StorageFile classes, which clearly represent folder and file entities, respectively. These have a number of very important aspects:

• Both classes are best understood as rich abstractions for pathnames. Wherever you’d normally think to use a pathname to refer to some file system entity, you’ll typically use one of these objects instead.

• As abstractions for pathnames, neither class maintains any kind of open file handles or such. Reading from or writing to a file requires a stream object instead, and it is the existence of a stream, not a StorageFile, that holds a file open and enforces access and sharing permissions.

• StorageFolder and StorageFile both derive from IStorageItem, a generic interface that defines common members like name, path, dateCreated, and attributes properties and deleteAsync and renameAsync methods. For this reason, both classes together are generically referred to as “storage items.”

• Both classes include static methods alongside their instance-specific methods, specifically those that return StorageFolder or StorageFile instances for specific pathnames or URIs.

The key reason why we have these abstractions is that the “file system” in Windows includes anything that can appear as part of the file system. This includes cloud storage locations, removable storage, and even other apps that present their contents in a file system–like manner, especially through the file picker. Pathnames by themselves simply cannot refer to entities that don’t exist on a physical file system device, so we need objects like StorageFolder and StorageFile to represent them. Such abstractions allow the file and folder pickers to reach outside the file system and also make it possible to share such references between apps, as through the Share contract. We’ll see more of this in later chapters.

The basic operations of StorageFolder and StorageFileobjects are shown in Figure 10-3. As you’d expect, a StorageFolder has methods to create, retrieve, and enumerate folders; methods to create, retrieve, and enumerate files; methods to enumerate files and folders together; and methods to delete or rename itself. A StorageFile, similarly, has methods to move, copy, delete, and or rename itself. Most important, though, are those methods that open a file—resulting in a stream—through which you can then read or write data.

Tip Some file extensions are reserved by the system and won’t be enumerated, such as .lnk, .url, and others; a complete list is found on the How to handle file activation topic. Also note that the ability to access UNC pathnames requires the Private Networks (Client & Server) andEnterprise Authentication capabilities in the manifest along with declarations of the file types you want to access.

images

FIGURE 10-3 Core properties and methods of the StorageFolder and StorageFileclasses and the objects they produce.

Given the relationship between the StorageFolder and StorageFile classes, nearly all file I/O in a Windows Store app starts by obtaining a StorageFolder object and then acquiring a StorageFile from it. Obtaining that first StorageFolder happens through one of the methods below (in a few cases you can also get to a StorageFile directly):

In-package contents Windows.ApplicationModel.Package.current.installedLocation gets a StorageFolder through which you can load data from files in your package (all files therein are read-only).

App data folders Windows.Storage.ApplicationData.current.localFolder, roamingFolder, or temporaryFolder provides StorageFolder objects for your app data locations (read-write).

Arbitrary file system locations An app can allow the user to select a folder or file directly using the file pickers invoked through Windows.Storage.Pickers.FolderPicker plus FileOpenPicker and FileSavePicker. This is the preferred way for apps that don’t need to enumerate contents of a library (see next bullet). This is also the only means through which an app can access safe (nonsystem) areas of the file system without additional declarations in the manifest. Alternately, a user can launch a file directly from Windows Explorer from any location, and the default app associated with that file type receives the StorageFile upon activation.

Libraries Windows.Storage.KnownFolders providesStorageFolder objects for the Pictures, Music, and Videos libraries, as well as Removable Storage. Given the appropriate capabilities in your manifest, you can work with the contents of these folders. (Attempting to obtain a folderwithout the correct capability will throw an Access Denied exception.)

Downloads The Windows.Storage.DownloadsFolder object provides a createFolderAsync method through which you can obtain a StorageFolder in that location. It also provides a createFileAsync method to create a StorageFile directly. You would use this API if your app manages downloaded files directly. Note that DownloadsFolder itself provides only these two methods; it is not a StorageFolder in its own right, to prevent apps from interfering with one another’s downloads.

Arbitrary paths The static method StorageFolder.getFolderFromPathAsync returns a StorageFolder for a given pathname if and only if your app already has permissions to access it; otherwise, you’ll get an Access Denied exception. A similar static method exists for files calledStorageFile.getFileFromPathAsync, with the same restrictions; the static method StorageFile.getFileFromApplicationUriAsync opens files with ms-appx:// (package) and ms-appdata:/// URIs. Other schema are not supported.

Access cache Once a folder or file object is obtained, it can be stored in the AccessCache that allows an app to retrieve it sometime in the future with the same programmatic permissions. This is primarily needed for folders or files selected through the pickers because permission to access the storage item is granted only for the lifetime of that in-memory object—we’ll see more in Chapter 11. The short of it is that you should always use this API, as demonstrated in scenario 7 of the File access sample, where you’d normally think to save a file path as a string. Again, StorageFolder.getFolderFromPathAsync and StorageFile.get-FileFromPathAsyncwill throw Access Denied exceptions if they refer to any locations where you don’t already have permissions. Also, pathnames will typically not work for files provided by another app through the file picker, because the StorageFile object might not, in fact, refer to any kind of file system entity.

Beyond just enumerating a folder’s contents, you often want only a partial list filtered by certain criteria (like file type), along with thumbnails and other indexed file metadata (like music album and track info, picture titles and tags, etc.) that you can use to group and organize the files. This is the purpose of file, folder, and item queries, as well as extended properties and thumbnails. We already saw a little with the FlipView app we built using the Pictures Library in Chapter 7, “Collection Controls,” and we’ll return to the subject in Chapter 11.

In the end, of course, we usually want to get to the contents of a particular file. This is the purpose of the StorageFile.open* methods, each variant providing a different kind of access. The result in each case is some kind of stream, an object that’s backed by a series of bytes and transparently accommodates the flow of those bytes between storage systems with different latencies, such as memory and disk, disk and network, memory and network, and so on. Each stream type has certain characteristics based its means of access:

openAsync and openReadAsync provide random-access byte streams for read/write and read-only, respectively. The streams are objects with the IRandomAccessStream and IRandomAccessStreamWithContentType interfaces, respectively, both in the Windows.Storage.Streams namespace. The first of these works with a pure binary stream; the second works with data+type information, as would be needed with an http response that prepends a content type to a data stream.

openSequentialReadAsync provides a read-only Windows.Storage.Streams.IInputStream object through which you can read file contents in blocks of bytes but cannot skip back to previous locations. You should always use this method when you need only to consume (read) the stream as it has better performance than a random access stream (the source can optimize for sequential reads).

openTransactedWriteAsync provides a Windows.Storage.StorageStreamTransaction that’s basically a helper object around an IRandomAccessStream with commitAsync and close methods to handle transactions. This is necessary when saving complex data to make sure that the whole write operation happens atomically and won’t result in corrupted files if interrupted. Scenario 5 of the File access sample shows this.

The StorageFile class also provides these static methods: createStreamedFileAsync,createStreamedFileFromUriAsync, replaceWithStreamedFileAsync, and replaceWithStreamed-FileFromUriAsync. These provide a StorageFile that you typically pass to other apps through contracts, as we’ll see in Chapter 15, “Contracts.” The utility of these methods is that the underlying file isn’t accessed at all until data is first requested from it, if such a request ever happens.

Pulling all this together, here’s a bit of code using the raw API we’ve seen thus far to create and open a “data.tmp” file in our temporary AppData folder and to write a given string to it. This bit of code is in the RawFileWrite example for this chapter. Let me be clear that what’s shown here utilizes low-level APIs in WinRT and is not what you typically use, as we’ll see in the next section. It’s instructive nonetheless, as you might occasionally need to exercise precise control over the process:

var fileContents = "Congratulations, you're written data to a temp file!";

writeTempFileRaw("data.tmp", fileContents);

function writeTempFileRaw(filename, contents) {

var ws = Windows.Storage;

var tempFolder = ws.ApplicationData.current.temporaryFolder;

var outputStream;

//Promise chains, anyone?

tempFolder.createFileAsync(filename, ws.CreationCollisionOption.replaceExisting)

.then(function (file) {

return file.openAsync(ws.FileAccessMode.readWrite);

}).then(function (stream) {

outputStream = stream.getOutputStreamAt(0);

var writer = newws.Streams.DataWriter(outputStream);

writer.writeString(contents);

return writer.storeAsync();

}).done(/* Completed handler if necessary */);

}

Good thing we learned about chained async operations a while back! Starting with a StorageFolder from the ApplicationData object, we call its createFileAsync to get a StorageFile. Then we open that file to obtain a random access stream. At this point the file is open and locked, so no other apps can access it.

Now a random access stream itself doesn’t have any methods to read or write data. For that we use the DataReader and DataWriter classes in Windows.Storage.Streams. The DataReader takes an IInputStream object, which you obtain fromIRandomAccessStream.getInputStreamAt(<offset>). The DataWriter, as shown here, takes an IOutputStream similarly obtained from IRandomAccess-Stream.getOutputStreamAt(<offset>).

The DataReader and DataWriter classes then offer a number of methods to read or write data, either as a string (as shown above with writeString), as binary (for instance, DataReader.loadAsync), or as specific data types. With the DataWriter, we have to end the process with a call to itsstoreAsync, which commits the data to the backing store. With both DataReader and DataWriter, discarding the objects will close the files and invalidate the streams.

Two tips First, if you use a transacted output stream, you also need to call its flushAsync method in the chain after DataWriter.storeAsync. Second, the DataReader and DataWriter constructors do not validate their backing streams. That happens only during loadAsync, storeAsync, and other read/write operations, which will throw confusing exceptions if the backing stream is invalid. When in doubt, double-check the stream passed to the constructor.

Now, you might be saying, “You’ve got to be kidding me! Four chained async operations just to write a simple string to a file! Who designed this API?” Indeed, when we started building the first Store apps within Microsoft, this is all we had and we asked these questions ourselves! After all, doing some basic file I/O is typically the first thing you add to a Hello World app, and this was anything but simple. To make matters worse, at that time we didn’t yet have promises for async operations in JavaScript, so we had to write the whole thing with raw nested async operations. Such were the days.

Fortunately, simpler APIs were already available and more came along shortly thereafter. These are the APIs you’ll typically use when working with files, as we’ll see in the next section. It is nevertheless important to understand the structure of the low-level code above because theDataReader and DataWriter classes are very important mechanisms for working with a variety of different I/O streams and are essential for data encoding processes. Having control over the fine details also supports scenarios such as having different components in your app that are all contributing to the file structure. So it’s good to take a look at the DataReader/DataWriter reference documentation along with the Reading and writing data sample to familiarize yourself with the capabilities.

Tip You don’t see any reference to a close method on the file or stream in the RawFileWrite example because that’s automatically taken care of in the DataWriter (the DataReader does it as well). A stream does, in fact, have a close method that will close its backing file, which is what the DataReader and DataWriter objects call when they’re disposed. If, however, you separate a stream from these objects through their detachStream methods, you must call the stream’s close yourself.

When you’re developing apps that write to files and you see errors indicating that the file is still open, check whether you’ve properly closed the streams involved. For more on this, see “Q&A on Files, Streams, Buffers, and Blobs” a little later in this chapter.

FileIO, PathIO, and WinJS Helpers (plus FileReader)

Simplicity is a good thing where file I/O is concerned, and the designers of WinRT made sure that the most common scenarios didn’t require a long chain of async operations like we saw in the previous section. The Windows.Storage.FileIO and PathIO classes provide such a streamlined interface—and without having to muck with streams! The only difference between the two is that the FileIO methods take a StorageFile parameter whereas the PathIO methods take a pathname string, the latter assuming that you already have programmatic access to that path. Beyond that, both classes offer the same methods called [read | write]BufferAsync (these work with byte arrays), [append | read | write]LinesAsync (these work with arrays of strings), and [append |read | write]TextAsync (these work with singular strings). With strings, WinJS.Applicationsimplifies matters even further for your appdata folders: its local, roaming, and temp properties, which implement an interface called IOHelper, provide readText and writeText methods that relieve you from even having to touch a StorageFile object.

Using the FileIO class, the code in the previous section can be reduced to the following, which can also be found in the RawFileWrite example (js/default.js):

function writeTempFileSimple(filename, contents) {

var ws = Windows.Storage;

var tempFolder = ws.ApplicationData.current.temporaryFolder;

tempFolder.createFileAsync(filename, ws.CreationCollisionOption.replaceExisting)

.then(function (file) {

ws.FileIO.writeTextAsync(file, contents);

});

}

And here’s the same thing written with WinJS.Application.temp.writeText, which is an async call and returns a promise if you need it:

WinJS.Application.temp.writeText(filename, fileContents);

Additional examples can be found in Scenario 3 of the File access sample.One other option you have—provided the file already exists—is using the PathIO.writeTextAsync method with an ms-appdata URI like so:

Windows.Storage.PathIO.writeTextAsync("ms-appdata:///temp/" + filename,contents);

Reading text from a file through the async readText method is equally simple, and WinJS provides two other methods through IOHelper: exists and remove.80 That said, these WinJS helpers are available for only your AppData folders and not for the file system more broadly. For areas outside app data, you must use the FileIO and PathIO classes.

You also have the HTML5 FileReader class available for use in Windows Store apps, which is part of the W3C File API specification. As its name implies, it’s suited only for reading files and cannot write them, but one of its strengths is that it can work both with files and blobs. Some examples of this are found in the Using a blob to save and load content sample.

Encryption and Compression

WinRT provides two capabilities that might be helpful to your state management: encryption and compression. Encryption is provided through the Cryptography and Cryptography.Core API, both part of the Windows.Security namespace. Cryptography contains methods for basic encoding and decoding (base64, hex, and text formats); Cryptography.Core handles encryption according to various algorithms. As demonstrated in the Secret saver encryption sample, you typically encode data in some manner with the Cryptography.CryptographicBuffer.convertStringToBinary method and then create or obtain an algorithm and pass that with the data buffer to Cryptography.Core.Crypto-graphicEngine.encrypt. Methods like decrypt and convertBinaryToString perform the reverse.

Compression is a little simpler in that it’s just a built-in API through which you can make your data smaller (say, to decrease the size of your roaming data). The Windows.Storage.CompressionAPI for this is composed of Compressor and Decompressor classes, both of which are demonstrated in the Compression sample. Although this API can employ different compression algorithms, including one called MSZIP, it does not provide a means to manage .ZIP files and the contents therein. For this purpose either employ a third-party JavaScript library or write a WinRT component in C++ that utilizes a higher-performance library (see Chapter 18, “WinRT Components”).

Both the encryption and compression APIs utilize a WinRT structure called a buffer, which is another curious beast like the random access stream in that it doesn’t have its own methods to manipulate it. Here again you use the DataReader and DataWriter classes, as described in the next section.

Q&A on Files, Streams, Buffers, and Blobs

As we’ve started to see in this chapter, the APIs for working with files and state begin to involve a plethora of object types and interfaces, all of which deal with managing and manipulating piles of data in some manner. Here’s the complete roster I’m referring to:

File system entities, represented by the StorageFolder and StorageFileclasses and the IStorageItem interface, all in the Windows.Storage namespace of WinRT.

Streams, represented by classes in Windows.Storage.Streams. Here we find a veritable pantheon of types:FileInputStream, FileOutputStream, FileRandomAccessStream, IInputStream, IOutputStream, IRandomAccessStream, InMemoryRandomAccessStream, InputStreamOverStream,OutputStreamOverStream, RandomAccessStream, and RandomAccessStreamOverStream.

Buffers, also in Windows.Storage.Streams, limited here to the Buffer class and the IBuffer interface.

Blob and MSStream objects, which come from the app host via the HTML API and serve to bridge gaps between the HTML5 world and WinRT.

In this section I want to bring all of these together, because you often encounter one or more of these types at an inconvenient point in your app development, like where you’re just trying to complete a seemingly simple task but end up getting lost in a jungle, so to speak. And I haven’t found a topic in the documentation that makes sense of them all. But instead of boring you with every last detail, I’ve reduced matters down to a few key questions, the answers to which have, for me, made much better sense of the landscape. So here goes.

Question #1: Why on earth doesn’t the StorageFile class have a close method?

Answer:As noted earlier, StorageFolder and StorageFileare abstractions for pathnames and thus only represent entities on the extended/virtual file system but not the contents of those entities (which is what streams are for). If you’re even asking this question, it means you need to update your mental model. After all, one of the first things we tinker with when learning to code is file I/O. We learn to open a file, read its contents, and close the file, thus establishing a basic mental model at a young age for anything with the name of “file.” Indeed, if you learned this through the C runtime library (OK, so I’m dating myself), you used a function like fopen with a pathname to open a file and get a handle. Then you called fread with that handle to read data, followed by fclose. All those methods nicely site next to each other in the API reference.

Coming to WinRT, you see StorageFile.openAsync quickly enough but then can’t find the read and close equivalents anywhere nearby, which breaks the old mental model. What gives? The main difference is that whereasfopen and its friends are synchronous, WinRT is an asynchronous API. In that asynchronous world, the request to open a StorageFile produces some result later on. So, technically, the file isn’t actually “open” until that result is delivered.

That result, to foreshadow the next question, is some kind of stream, which is the analog to a file handle. That stream is what manages access to the file’s contents, so when you want to read from “the file”à lafread you actually read from a stream that’s connected to the file contents. When you want to “close the file” in the way that you think of with fclose, you close and dispose of the stream through its own close method. This is why the StorageFile object does not have a close: it’s the stream that holds the backing entity open, so you must close the stream.

Again, all of this is important because many file-like entities actually have no pathname at all thanks to the unification of local-, cloud-, and app-based entities. This means that whatever thingy a StorageFile represents might not be local to the device or even exist as a real file anywhere in the known universe—backing data for a StorageFile can, in fact, be generated on the fly and fed into the stream. Lots of work might be involved, then, in getting a stream through which you can get to that entity’s contents, and that’s the complexity that the WinRT API is handling on your behalf. Be grateful!

And to repeat another point from earlier, the APIs in Windows.Storage.FileIO and PathIO classes shield you from streams altogether (see Question #4).

Question #2: My God, what are all those different stream types about?

Answer: Trust me, I feel your pain!Let's sort them out.

A stream is just an abstraction for a bit bucket. Streams don't make any distinction about the data in those buckets, only about how you can get to those bits. Streams are used to access file contents, pass information over sockets, talk to devices, and so forth.

Streams come in two basic flavors: original and jalapeño. Oops! I'm writing this while cooking dinner…sorry about that. They come in two sorts: sequential and random access. This differentiation allows for certain optimizations to be made in the implementation of the stream:

• A sequential stream can assume that data is accessed (read or written) once, after which it no longer needs to be cached in memory. Sequential streams do not support seeking or positioning. When possible, it's always more memory-efficient to use a sequential stream.

• A random access stream needs to keep that data around in case the consuming code wants to rewind or fast-forward (seek or position).

As mentioned with Question #1, all streams have a close method that does exactly what you think. If the stream is backed by a file, it means closing the file. If the stream is backed by memory, it means freeing that memory. If it’s backed by a socket, it means closing the socket. You get the idea.

In the sequential group there is a further distinction between "input" streams, which support reading (but not writing), and "output" streams, which support writing (but not reading). These reflect the reality that communication with many kinds of backing stores is inherently unidirectional—for example, downloading or uploading data through HTTP requests.

The primary classes in this group are FileInputStream and FileOutputStream.Also, the IInputStream and IOutputStreaminterfaces serve as the basic abstractions. (Don’t concern yourself with InputStreamOverStream and OutputStreamOverStream, which are wrappers for lower-level COMIStream objects.)

An input stream has a method readAsync that copies bytes from the source into a buffer (an abstraction for a byte array, see Question #3). An output stream has two methods, writeAsync, which copies bytes from a buffer to the stream, and flushAsync, which makes sure the data is written to the backing entity before it deems the flushing operation is complete. When working with such streams, you always want to call flushAsync, using its completed handler for any subsequent operations (like a copy) that are dependent on that completion.

Now for the random access group. Within Windows.Storage.Streams we find the basic types: FileRandomAccessStream, InMemoryRandomAccessStream, and RandomAccessStream, along with the abstract interfacesIRandomAccessStream. (RandomAccessStreamOverStream again builds on the low-level COM IStream and isn't something you use directly.)

The methods of IRandomAccessStreamare common among classes in this group. It provides:

• Properties of canRead, canWrite, position, and size.

• Methods of seek, cloneStream(the clone has an independent position and lifetime), getInputStreamAt(returns an IInputStream), and getOutputStreamAt(returns an IOutputStream).

The getInputStreamAt and getOutputStreamAt methods are how you obtain a sequential stream for some section of the random access stream, allowing more efficient read/write operations. You often use these methods to obtain a sequential stream for some other API that requires them.

If we now look at FileRandomAccessStream and InMemoryRandomAccessStream (whose backing data sources I trust are obvious), they have everything we've seen already (properties like position and canRead, and methods like closeandseek) along with two more methods, readAsync andwriteAsync, which behave just like their counterparts in sequential input and output streams (using those buffers again).

As for the RandomAccessStreamclass, it's a curious beast that contains onlystatic members—you never have an instance of this one. It exists to provide the generic helper methods copyAsync (with two variants) and copyAndCloseAsyncthat transfer data between input and output streams. This way other classes like FileRandomAccessStream don't need their own copy methods. To copy content from one file into another, you call FileRandomAccessStream.getInputStreamAt on the source (for reading) and FileRandomAccessStream.getOutputStreamAt on the destination (for writing), and then you pass those to RandomAccessStream.copyAsync (to leave those streams open) or copyAndCloseAsync (to automatically do a flushAsync on the destination and closeon both).

The other class to talk about here, RandomAccessStreamReference, also supplies other static members. When you read "reference," avoid thinking about reference types or pointers or anything like that—it's more about having read-only access to resources that might not be writable, like something at the other end of a URI. Its three static methods are createFromFile(which takes a StorageFile), createFromStream(which takes an IRandomAccessStream), and createFromUri (which takes a Windows.Foundation.Uri that you construct with a string). What you then get back from each of these static methods is an instanceof RandomAccessStreamReference (how's that for confusing?). That instance has just one method, openReadAsync, whose result is an IRandomAccessStream-WithContentType(the same thing as an IRandomAccessStream with an extra string propertycontentTypeto identify its data format).

To sum up, remember the difference between sequential streams (input or output) and random access streams, and remember that streams are just ways to talk to the bits (or bytes) that exist in some backing entity like a file, memory, or a socket. When a stream exists, its backing entity is "open" and typically locked (depending on access options) until the stream is closed or disposed.

Question #3: So now what do I do with these buffer objects, when neither the Buffer class nor the IBuffer interface have any methods?

Answer: I totally agree that this one stumped me for a while, which is one reason I’ve included this Q&A in this book!

As in question #2, straightforward stream object methods like readAsync and writeAsyncresult in this odd duck called a Buffer instead of just giving us a byte array. The problem is, when you look at the reference docs for Buffer and IBuffer, all you see are two properties: length andcapacity. "That's all well and good," you say (and I have said too!), "but how the heck do you get at the data itself?" After all, if you just opened a file and read its contents from an input stream into a buffer, that data exists somewhere, but this buffer thing is just a black box as far as you can see!

Indeed, looking at the Buffer itself, it's hard to understand how one even gets created in the first place, because the object itself also lack methods for storing data (the constructor takes only a capacity argument). This gets confusing when you need to provide a buffer to some API, likeProximityDevice.PublishBinaryMessage (for near-field communications): you need a buffer to call the function, but how do you create one?

To make things clear, first understand that a buffer is just an abstraction for an untyped byte array, and it exists because marshaling byte arrays between Windows and language-specific environments like JavaScript and C# is a tricky business. Having an abstract class makes such marshaling easier to work with in the API.

Next, when you are about to call FileRandomAccessStream.readAsync, you do, in fact, create an empty buffer with new Buffer(), which readAsync will populate. On the other hand, if you need to create a new buffer with real data, there are two ways to go about it:

• One way is our friendWindows.Storage.Streams.DataWriter. Create an instance with new DataWriter(), and use its write* methods to populate it with whatever you want (including writeBytes, which takes an array). Once you've written those contents, call its detachBuffer and you have your populated Buffer object.

• The other way is super-secret or maybe just unintentional, but nonetheless it’s useful here. You have to look way down in Windows.Security.Cryptography.—wait for it!—Cryptographic-Buffer.createFromByteArray. This API has nothing to do with cryptography per se and simplycreates a new buffer with a byte array you provide. This is simpler than using DataWriter if you have a byte array; DataWriter is better, though, if you have data in any other form, such as a string.

How, then, do you get data out of a buffer? With the Windows.Storage.Streams.DataReader class. Create an instance of DataReaderwith the static method DataReader.fromBuffer, after which you can call methods like readBytes, readString, and so forth.81

In short, the methods that you would normally expect to find on a class like Buffer are found instead within DataReader and DataWriter, because these reader/writer classes also work with streams. That is, instead of having completely separate abstractions for streams and byte arrays with their own read/write methods for different data types, those methods are centralized within the DataReader and DataWriter objects that are themselves initialized with either a stream or a buffer. DataReader and DataWriteralso take care of the details of closing streams for you when appropriate.

In the end, this reduces the overall API surface area once you understand how they relate.

Question #4: Now doesn't all this business with streams and buffers make simple file I/O rather complicated?

Answer:Yes and no, and for the most part we’ve already answered this question earlier in “FileIO, PathIO, and WinJS Helpers” with the RawFileWrite code examples. To reiterate, the low-level API gives you full control over the details; the higher-level APIs to simplify common scenarios.

Indeed, we can go even one step lower than our writeTempFileRaw function. In that code we used DataWriter.storeAsync, which is itself just a helper for the truly raw process that involves buffers. Here is the lowest-level implementation for writing a file (see js/default.js in the example):

var fileContents = "Congratulations, you're written data to a temp file!";

writeTempFileReallyRaw("data-raw.tmp", fileContents);

function writeTempFileReallyRaw(filename, contents) {

var ws = Windows.Storage;

var tempFolder = ws.ApplicationData.current.temporaryFolder;

var writer;

var outputStream;

//All the control you want!

tempFolder.createFileAsync(filename,ws.CreationCollisionOption.replaceExisting)

.then(function (file) {

//file is a StorageFile

return file.openAsync(ws.FileAccessMode.readWrite);

}).then(function (stream) {

//Stream is an RandomAccessStream. To write to it, we need an IOuputStream

outputStream = stream.getOutputStreamAt(0);

//Create a buffer with contents

writer = newws.Streams.DataWriter(outputStream);

writer.writeString(contents);

var buffer = writer.detachBuffer();

return outputStream.writeAsync(buffer);

}).then(function (bytesWritten) {

console.log("Wrote " + bytesWritten + " bytes.");

return outputStream.flushAsync();

}).done(function () {

writer.close(); //Closes the stream too

});

}

Again, within this structure, you have the ability to inject any other actions you might need to take at any level, such as encrypting the contents of the buffer or cloning a stream. But if you don’t need to see the buffers you can cut that part out by using DataWriter.storeAsync. If you don't need to play with the streams directly, you can usethe FileIO class to hide those details. And if you have programmatic access to the desired location on the file system, you can even forego using Storage-Fileby insteadusing the PathIO class or the WinJS.Application.local, roaming, andtemp helpers.

So, yes, file I/O can be complicated but only if you need it to be. The APIs are designed to give you the control you need when you need it but not burden you when you don't.

Question #5: What are MSStream and Blob objects in HTML5, and how do they relate to the WinRT classes?

Answer: To throw another stream into the river, so to speak, when working with the HTML5 APIs, specifically those in the File API section, we encounter MSStream and Blob types. (See the W3C File API and the Blob section therein for the standards.) As an overview, the HTML5 File APIs—as provided by the app host to Store apps written in JavaScript—contain a number of key objects that are built to interoperate with WinRT APIs:

• Blob A piece of immutable binary data that allows access to ranges of bytes as separate blobs. It has size, type, and msRandomAccessStream properties (the latter being a WinRTIRandomAccessStream). It has two methods:slice (returning a new blob from a subsection) and msClose(releases a file lock).

• MSStream Technically an extension of this HTML5 File API that provides interop with WinRT. It lives outside of WinRT, of course, and is thus available to both the local and web contexts in an app. The difference between a blob and a stream is that a stream doesn't have a known size and can thus represent partial data received from an in-process HTTP request. You can create an MSStreamReader object and pass an MSStream to its readAsBlob method to get the blob once the rest is downloaded.

• URL Creates and revokes URLs for blobs, MSStream, IRandomAccessStreamWithContentType, IStorageItem, and MediaCapture. It has only two methods: createObjectURL and revokeObjectURL. (Make note that even if you have a oneTimeOnly URL for an image, it's cached, so it can be reused.)

• File Derived from Blob, has name and lastModifiedDateproperties. A File in HTML5 is just a representation of a Blob that is known to be a file. Access to contents is through the HTML5 FileReader or FileReaderSync objects, with readAs*methods: readAsArrayBuffer, readAsDataURL,readAsText.

• MSBlobBuilder Used only if you need to append blobs together, with its appendmethod, and then the getBlobmethod to obtain the result.

The short of it is that when you get MSStream or Blob objects from some HTML5 API (like an XmlHttpRequest with responseTypeof "ms-stream," as when downloading a file or video, or from the canvas' msToBlob method), you can pass those results to various WinRT APIs that acceptIInputStream or IRandomAccessStream as input. To use the canvas example, if you call canvas.msToBlob, the msRandomAccessStreamproperty of that blob can be fed directly into the Windows.Graphics.-ImagingAPIs for transform or transcoding. A video stream can be similarly manipulated using the APIs in Windows.Media.Transcoding. You might also just want to write the contents of a stream to a StorageFile (especially when the backing store isn’t local) or copy them to a buffer for encryption.

The aforementioned MSStreamReader object, by the way, is where you find methods to read data from an MSStream or blob. Do be aware that these methods are synchronous and will block the UI thread if you're working with large data sets. But MSStreamReader will work just fine in a web worker.

On the flip side of the WinRT/HTML5 relationship, the MSApp object in JavaScript provides methods to convert from WinRT types to HTML5 types. One such method, createStreamFromInputStream, creates an MSStream from an IInputStream, allowing to take data from a WinRT source and call URL.createObjectURL, assigning the result to something like an img.src property. Similarly, MSApp.createBlobFromRandomAccessStream creates an MSStream from an IRandomAccessStream, and MSApp.createFileFromStorageFile converts a WinRT StorageFile object into an HTML5File object.

Let me reiterate thatURL.createObjectURL, which is essential to create a URI you can assign to properties of various HTML elements, can work with both HTML objects (blobs and MSStream) and WinRT objects (IRandomAccessStreamWithContentType, IStorageItem, and MediaCapture). In some cases you'll find that you don't need to convert a WinRT stream into an MSStream or Blob explicitly—URL.createObjectURL does that automatically. This is what makes it simple to take a video preview stream and display it in a <video> element, as we’ll see in Chapter 13. You just set up the MediaCapture object in WinRT, assign the result from URL.createObjectURL on that object to video.src, and call video.play() to stream the video preview directly into the element.

Using App Data APIs for State Management

Now that we’ve seen the nature of state-related APIs, let’s see how they’re used to manage for different kinds of state and any special considerations that come into play.

Transient Session State

As described before, transient session state is whatever an app saves when being suspended so that it can restore itself to that state if it’s terminated by the system and later restarted. Being terminated by the system is again the only time this happens, so what you include in session state should always be scoped to giving the user the illusion that the app was running the whole time. In some cases, as described in Chapter 3, you might not in fact restore this state, especially if it’s been a long time since the app was suspended and it’s unlikely the user would remember where they left off. That’s a decision you need to make for your own app regarding the experience you want to provide for your customers.

Session state should be saved within the AppData localFolder or the localSettings object. It should not be saved in a temp area because the user could run the Disk Cleanup tool while your app is suspended or terminated in which case session state might be deleted (see next section). And because this type of state is specific to a device, you would not use roaming areas for it.

Regardless of what information you save as session state, be sure that it is completely saved by the time you return from any suspending event handler, using deferrals of course if you need to do async work (also described in Chapter 3). Remember that you have only a few seconds to complete this task, so apps often save session state incrementally while the app is running rather than wait for the suspending event specifically.

As we saw in Chapter 3, it’s a good idea to just maintain your session variables directly in the WinJS sessionState object so that it’s always current with your state. WinJS then automatically saves the contents of sessionState in its own handler for WinJS.Application.oncheckpoint (a wrapper for suspending). It does this by calling JSON.stringify(sessionState) and writing the resulting text to a file called _sessionState.json within the localFolder.

If you need to store additional values within sessionState just before it’s written, do that in your own checkpoint handler. A good example of such data is the navigation stack for page controls, which is available in WinJS.Navigation.history; you could also copy this data tosessionState within the PageControlNavigator.navigated method (in navigator.js as provided by the project templates). In any case, WinJS will always call its own checkpoint handler after yours is done to make sure any changes to sessionState are saved.

When an app is restarted after being terminated, WinJS also automatically loads _sessionState.json into the sessionState object, so everything will be there when your own activation code is run.

If you don’t use the WinJS sessionState object and just manage your state directly (in settings containers and other files), you can save your session variables whenever you like (including within checkpoint). You then restore them directly within your activated event forpreviousExecutionState == terminated.

It’s also a good practice to build some resilience into your handling of session state: if what gets loaded doesn’t seem consistent or has some other problem, revert to default session values. Remember too that you can use the localSettings container with composite settings to guarantee that groups of values will be stored and retrieved as a unit. You might also find it helpful during development to give yourself a simple command to clear your app state in case things get fouled up, but just uninstalling your app will clear out all that as well. At the same time, it’s not necessary to provide your users with a command to clear session state: if your app fails to launch after being terminated, the previousExecutionState flag will be notRunning the next time the user tries, in which case you won’t attempt to restore the state.

Similarly, if the user installs an update after an app has been suspended and terminated and the app data version changes, the previousExecutionState value will be reset. If you don’t change the state version for an app update, your session state can carry forward. (This is a behavioral change between Windows 8 and Windows 8.1—the former resets previousExecutionState when an app update is installed, but the latter does not.)

Sidebar: Using HTML5 sessionStorage and localStorage

If you prefer, you can use the HTML5 localStorage object for both session and other local state; its contents get persisted to the AppData localFolder. The contents of localStorage are not loaded until first accessed and are limited to 10MB per app; the WinRT and WinJS APIs, on the other hand, are limited only by the capacity of the file system.

As for the HTML5 sessionStorage object, it’s not really needed when you’re using page controls and maintaining your script context between app pages—your in-memory variables already do the job. However, if you’re actually changing page contexts by using <a> links or settingdocument.location, then sessionStorage can still be useful. You can also encode information into URIs, as is commonly done with web apps.

Both sessionStorage and localStorage are also useful within iframe or webview elements running in the web context, as the WinRT APIs are not available. At the same time, you can load WinJS into a web context page (this is supported) and the WinJS.Application.local, roaming, and temp objects still work using in-memory buffers instead of the file system.

Local and Temporary State

Unlike session state that is restored only in particular circumstances, local app state is composed of those settings and other values that are always applied when an app is launched. Anything that the user can set directly falls into this category, unless it’s also part of the roaming experience, in which case it is still loaded on app startup. Any other cached data, saved searches, display units, preferred media formats, and device-specific configurations also fall into this bucket. In short, if it’s not pure session state and not part of your app’s roaming experience, it’s local or temporary state. (Remember that credentials should be stored in the Credential Locker instead of in your app data and that recent file lists and frequently used file lists should use Windows.Storage.AccessCache.)

All the same APIs we’ve seen also work for this form of state, including all the WinRT APIs, the WinJS.Application.local and temp objects, and HTML localStorage. You can also use the HTML5 IndexedDB APIs, SQLite, and the HTML AppCache—these are just other forms of local state even though they aren’t necessarily stored in your AppData folders.

It’s very important to version-stamp your local and temp state because it will always be preserved across an app update (unless temp state has been cleaned up in the meantime). With any app update, be prepared to load old versions of your state or migrate it with setVersionAsync, or simply decide that a version is too old and purge it (Windows.Storage.ApplicationData.current.clearAsync) before setting up new defaults. As mentioned before, it’s also possible to migrate state from a background task. (See Chapter 16.)

Generally speaking, local and temp app data are the same—they have mostly the same APIs and are stored in parallel folders. Temp, however, doesn’t support settings and settings containers. The other difference, again, is that the contents of your temp folder (along with the HTML5 app cache) are subject to the Windows Disk Cleanup tool when Temporary Files is selected. This means that your temp data could disappear at any time when the user wants to free up some disk space. You could also employ a background task with a maintenance trigger for doing cleanup on your own (again see Chapter 16, in “Tasks for Maintenance Triggers”).

For these reasons, temp should be used for storage that optimizes your app’s performance but not for anything that’s critical to its operation. For example, if you have a JSON file in your package that you parse or decompress on first startup such that the app runs more quickly afterwards, and if you don’t make any changes to that data from the app, you might elect to store that in temp. The same is true for graphical resources that you might have fine-tuned for the specific device you’re running on; you can always repeat that process from the original resources, so it’s another good candidate for temp data. Similarly, if you’ve acquired data from an online service as an optimization (that is, so that you can just update the local copy incrementally), you can always reacquire it. This is especially helpful for providing an offline experience for your app, though in some cases you might want to let the user choose to save it in local instead of temp (an option that would appear in Settings along with the ability to clear the cache).

Sidebar: HTML5 App Cache

Store apps can employ the HTML 5 app cache as part of an offline/caching strategy. It is most useful in iframe and webview elements (running in the web context) where it can be used for any kind of content. For example, an app that reads online books can show such content in a webview, and if those pages include app cache tags, they’ll be saved and available offline. In the local context, the app cache works for nonexecutable resources like images, audio, and video but not for HTML or JavaScript.

IndexedDB, SQLite, and Other Database Options

Many forms of local state are well suited to being managed in a database. In Windows Store apps, the IndexedDB API is available through the window.indexedDB and worker.indexedDB objects. For complete details on using this feature, I’ll refer you to the W3C specifications, the Indexed Database API reference for Store apps, and the IndexedDBsample. (Aaron Powell’s db.js wrapper for IndexedDB might also be of interest.)

It’s very important to understand that some app and systemwide limits are imposed on IndexedDB because there isn’t a means through which the app or the system can shrink a database file and reclaim unused space:

• IndexedDB has a 250MB limit per app and an overall system limit of 375MB on drives smaller than 32GB, or 4% (to a maximum 20GB) for drives over 32GB. So it could be true that your app might not have much room to work with anyway, in which case you need to make sure you have a fallback mechanism. (When the limit is exceeded the APIs will throw a Quota Exceeded exception. And if you want to use a third-party tool to explore the database contents, you can find the .edb files in %localappdata%/Microsoft/Internet Explorere/Indexed DB.)

• IndexedDB as provided by the app host does not support complex key paths—that is, it does not presently support multiple values for a key or index (multientry).

• By default, access to IndexedDB is granted only to HTML pages that are part of the app package and those declared as content URIs. (See “App Content URIs” in Chapter 4.) Random web pages you might host in a webview will not be given access, primarily to preserve space within the 250MB limit for those pages you really care about in your app. However, you can grant access to arbitrary pages by including the following tag in your home page and not setting the webview’s contents until the DOMContentLoaded or activated event has fired:

<meta name="ms-enable-external-database-usage" content="true"/>

Besides IndexedDB a few other database options for Store apps exist. For a local relational database, the most popular option is SQLite, which you can install as a Visual Studio extension from the SQLite for Windows Runtime page.82 Full documentation and other downloads can be found at http://sqlite.org, and Tim Heuer’s blog on SQLite provides many details for Windows Store apps. The key thing for apps written in JavaScript is that you can’t talk directly to a compiled SQLite DLL, so a little more work is necessary.

One solution that’s emerged in the community is a WinRT wrapper component for the DLL called SQLite3-WinRT, available on GitHub, which provides a familiar promise-oriented async API for your database work. There is also a version called SQL.js, which is SQLite compiled to JavaScript via Emscripten. This gives you more of the straight SQLite API, but be mindful that as JavaScript it’s always going to be running on the UI thread.

Another local, nonrelational database option are the Win32 “Jet” or Extensible Storage Engine (ESE) APIs (on which the IndexedDB implementation is built). For this you’ll need to write a WinRT Component wrapper in C++ (the general process for which is in Chapter 18), because JavaScript cannot get to those APIs directly.

There’s also LINQ for JavaScript, a project on CodePlex that allows you to use SQL-like queries against JavaScript objects. (LINQ stands for Language INtegerated Queries, a concept introduced with .NET languages that has proven very popular and convenient.) If your data is small enough to be loaded into memory from serialized JSON, this could make a suitable database for your app.

An alternate possibility for searchable file-backed data is to use the system index by creating a folder named “Indexed” in your local and/or roaming AppData folder. The contents of the files in this folder, including metadata (properties), will be indexed by the system indexer and can be queried using Advanced Query Syntax (AQS). (The general query APIs are explained in “Custom Queries” in Chapter 11; content indexing is covered in Chapter 15 in “Indexing and Searching Content.”) You can also do property-based searches for Windows properties, making this approach a simple alternative to database solutions.

You can probably find other third-party libraries that would fulfill your needs for local data storage, perhaps just using the file system with JSON or XML. I haven’t investigated any specific ones, however, so I can't make recommendations here. (If a library uses Win32 APIs under the covers, make sure that they use only those listed on Win32 and COM for Windows Store apps.)

If you’re willing to work with online databases, you have a couple of additional technologies to choose from. First, Windows Azure Mobile Services makes it very easy to create and manage online tables (stored in an online SQL Server database), which you can query through simple client-side libraries. For more, see Get started with data in Mobile Services. Note that if you’re planning to use push notifications in your app, a subject that we’ll return to in Chapter 16, you’ll likely want to use tables in Mobile Services for that purpose, so you might as well get started now.

Speaking of online SQL Server databases, you can work with them directly through the OData protocol, provided that you have configured the necessary data services on the server side (a REST interface). When that’s in place, you can use the client-side OData Library for JavaScript or the datajs library to do all your work, because they handle the details of making the necessary HTTP requests.

Finally, OneDrive is an option for working with cloud-based files. We’ll talk more about OneDrive in Chapter 11, but you can refer to the Live Connect Developer Center in the meantime.

Roaming State

The automatic roaming of app state between a user’s devices (up to 81 of them!) is one of the most interesting and enabling features introduced for Windows Store apps. Seldom does such a small piece of technology like this so greatly reduce the burden on app developers!

It works very simply. First, your AppData roamingFolder and your roamingSettings container behave exactly like their local counterparts. As long as their combined size is less than the roamingStorageQuota (in Windows.Storage.ApplicationData.current), Windows will copy that data to the cloud (where it maintains a copy for each discrete version of your state) and then from the cloud to other devices where the same user is logged in and has the same app installed. In fact, Windows attempts to copy roaming data for an app during its installation process so that it’s there when the app is first launched on that device.

If the app is running simultaneously on multiple devices, the last writer of any particular file or setting always wins. When roaming state gets synced from the latest copy on the cloud, apps will receive the Windows.Storage.ApplicationData.ondatachanged event. So an app should always read the appropriate roaming state on startup and refresh that state as needed within datachanged. You should always employ this strategy too in case Windows cannot bring down roaming state for a newly installed app right away (such as when the user installed the app and lost connectivity). As soon as the roaming state appears, you’ll receive the datachanged event. Scenario 5 of the Application data sample provides a basic demonstration of this.

Tip Roaming state is meant to keep multiple apps synchronized to state in the cloud. It is not intended as a message-passing system—that is, having one app write to a file and having the app on another device clearing out that file once it "receives" the data. If you need to pass messages, use another service of your own.

Deciding what your roaming experience looks like is really a design question more than a development question. It’s a matter of taking all app settings that are not specific to the device hardware (that is, those not related to screen size, video capabilities, the presence of particular peripherals or sensors, etc.), and thinking through whether it makes sense for each setting to be roamed. A user’s favorites, for example, are appropriate to roam if they refer to data that isn’t local to the device. That is, favorite URIs or locations on a cloud storage service like OneDrive or Flickr are appropriate to roam; favorites and recently used files in a user’s local libraries are not. The viewing position within a cloud-based video, like a streaming movie, would be appropriate to roam, as would be the last reading position in a magazine or book. But again, if that content is local, then maybe not. Account configurations like email settings are often good candidates so that the user doesn’t have to configure the app again on other devices.

At the same time, you might not be able to predict whether the user will want to roam certain settings. In this case, the right choice is to give the user a choice! That is, include options in your Settings UI to allow the user to customize the roaming experience to their liking, especially as a user might have devices for both home and work where they want the same app to behave differently. For instance, with an RSS Reader the user might not want notifications on their work machine whenever new articles arrive but would want real-time updates at home. The set of feeds itself, on the other hand, would probably always be roamed, but then again the user might want to keep separate lists.

To minimize the size of your roaming state and stay below the quota, you might employ the Windows.Storage.Compression API for file-based data, as described earlier. For this same reason, never use roaming state for user data. Instead, use an online service like OneDrive to store user data in the cloud, and then roam URIs to those files as part of the roaming experience. Put another way, think in terms of roaming references to content, not content itself. Also consider putting caps on otherwise open-ended data sets (like favorites) to avoid exceeding the quota.

By now you probably have a number of other questions forming in your mind about how roaming works: “How often is data synchronized?” “How do I manage different versions?” “What else should I know?” These are good questions, and fortunately there are good answers!

• Assuming there’s network connectivity, an app’s roaming state is roamed within 30 minutes on an active machine. It’s also roamed immediately when the user logs on or locks the machine. Locking the machine is always the best way to force a sync to the cloud. Note that if the cloud service is aware of only a single device for a user (that is, for any given a Microsoft account), synchronization with the cloud service happens only about once per day. When the service is aware that the user has multiple machines, it begins synchronizing within the 30-minute period. If the app is uninstalled on all but one machine, synchronization reverts to the longer period.

• When saving roaming state, you can write values whenever you like, such as when those settings are changed. Don’t worry about grouping your changes: Windows has a built-in debounce period to combine changes together and reduce overall network traffic.

• If you have a group of settings that must be roamed together, manage these as a composite setting in your roamingSettings container.

• Files you create within the roamingFolder are not be roamed so long as you have the file open for writing (that is, as long as you have an open stream). For this reason it’s a good idea to make sure that all streams are closed when the app is suspended.

• Windows allows each app to have a “high priority” setting that is roamed within one minute, thereby allowing apps on multiple devices to stay much more closely in sync. This one setting—which can be a composite setting—must exist in the root of your roamingSettings with the nameHighPriority—that is, roamingSettings.values["HighPriority"]. That setting must also be 8K or smaller to maintain the priority. If you exceed 8K, it roams with normal priority. (And note that the setting must be a single or composite setting; a settings container with the same name roams with normal priority.) See scenario 6 of the Application data sample for a demonstration.

• On a trusted PC, systemwide user settings like the Start page configuration are automatically roamed independent of apps. This includes encrypted credentials saved by apps in the credential locker (if enabled in PC Settings); apps should never attempt to roam passwords. Apps that create secondary tiles (as we’ll see in Chapter 16) can indicate whether such tiles should be copied to a new device when the app is installed.

• When multiple state versions are in use by different versions of an app, Windows manages each version of the state separately, meaning that newer state won’t be roamed to devices with apps that use older state versions. In light of this, it’s a good idea to not be too aggressive in versioning your state because it breaks the roaming connection between apps.

• The cloud service retains multiple versions of roaming state so long as multiple versions are in use by the same Microsoft account. Only when all instances of the app have been updated or uninstalled will older versions of the roaming state be eligible for deletion.

• When an updated app encounters an older version of roaming state, it should load it according to the old version but call setVersionAsync to migrate to the new version.

• Avoid using secondary versioning schemes within roaming state such that you introduce structural differences without changing the state version through setVersionAsync. Because the cloud service is managing the roaming state by this version number, and because the last writer always wins, a version of an app that expects to see some extra bit of data, and in fact saved it there, might find that it’s been removed because a slightly older version of the app didn’t write it.

• Even if all apps are uninstalled from a user’s devices, the cloud service retains roaming state for “a reasonable time” (maybe 30 days) so that if a user reinstalls the app within that time period they’ll find that their settings are still intact. To avoid this retention and explicitly clear roaming state from the cloud, use the clearAsync method.

• To debug roaming state, check out the Roaming Monitor Tool available in the Visual Studio Gallery. It provides status information on the current sync state, a Sync Now button to help with testing, and a browser for roaming state and a file editor. (At the time of writing, however, this tool is only available for Visual Studio 2012 for Windows 8 and has not been updated for Windows 8.1; it might appear directly in Visual Studio and not as an extension.)

For additional discussion, refer to Guidelines for roaming app data.

Settings Pane and UI

We’ve now seen all the different APIs that an app can use to manage its state where storage is concerned. The question that remains is how to present settings that a user can configure directly. That is, within the whole of an app’s state, there will be some subset that you allow a user to control directly, as opposed to indirectly through other actions. Many bits of state are tracked transparently or, like a navigation history, might reflect user activity but aren’t otherwise explicitly shown to or configurable by the user. Other pieces of state—like preferences, accounts, profile pictures, and so forth—can and should be exposed directly to the user. This is the purpose of the Settings charm.

When the user invokes the Settings charm (which can also be done directly with the Win+i key), Windows displays the Settings pane, a piece of UI that is populated with various settings commands as well as system functions along the bottom. Some examples are shown in Figure 10-4.

images

FIGURE 10-4 Examples of commands on the top-level settings pane. Notice that the lower section of the pane always has system settings and the app name and publisher always appear at the top. Permissions and Rate And Review are added automatically for apps acquired from the Store. Rate And Review is not included for side-loaded apps (nor the Store app itself).

What appears in the Settings charm for an app should be those settings that affect behavior of the app as a whole and are adjusted only occasionally, or commands like Feedback and Support that simply navigate to a website. Options that apply only to particular pages or workflowsshould not appear in Settings: place them directly on the page (the app canvas) or in the app bar. Of course, such options are still part of your app state—just not part of the Settings charm UI!

Details and options that typically appear in the Settings charm include the following:

• Display preferences like units, color themes, alignment grids, and defaults.

• Roaming preferences that allow the user to control exactly what settings get roamed, such as to keep configurations for personal and work machines separate.

• Account and profile configurations, along with commands to log in, log out, and otherwise manage those accounts and profiles. Passwords should never be stored directly or roamed, however; use the Credential Locker instead.

• Behavioral settings like online/offline mode, auto-refresh, refresh intervals, preferred video/audio streaming quality, whether to transfer data over metered network connections, the location from which the app should draw data, and so forth.

• A feedback link where you can gather specific information from the user, or you can use a feedback panel that records data to telemetry you collect and upload separately.

• Additional information about the app, such as Help, About, a copyright page, a privacy statement, license agreements, and terms of use. Oftentimes these commands will take the user to a separate website, which is perfectly fine.

I highly recommend that you run the apps that are built into Windows and explore their use of the Settings charm. You’re welcome to explore how Settings are used by other apps in the Store as well, but those might not always follows the design guidelines as consistently—and consistency is essential to settings!

Speaking of which, apps can add their own commands to the Setting pane but they are not obligated to do so. Windows guarantees that something always shows up for the app in this pane: it automatically displays the app name and publisher, a Rate And Review command that takes you to the Windows Store page for the app, an Update command if an update is available from the Store (and auto-update is turned off), and a Permissions command if the app has declared any capabilities in its manifest that are subject to user consent (such as Location, Camera, Microphone, etc.). Note that Rate And Review and Update won’t appear for apps you run from Visual Studio or for side-loaded apps, because they weren’t acquired from the Store.

One of the beauties of the Settings charm is that it appears as a flyout on top of the app, meaning you never need to incorporate settings pages into your app’s navigation hierarchy. Furthermore, the Settings charm is always available no matter where you are in the app, so you don’t need to think about having such a command on your app bar, nor do you ever need a general settings command on your app canvas. That said, you can invoke the Settings charm programmatically, such as when you detect that a certain capability is turned off and you prompt the user about that condition. You might ask something like “Do you want to turn on geolocation for this app?” and if the user says Yes, you can invoke the Settings charm. This is done through the settings pane object returned from Windows.UI.ApplicationSettings.SettingPane.getForCurrentView, whoseshow method displays the UI (or throws a kindly exception if the app doesn’t have the focus). The edge property of the settings pane object also tells you if it’s on the left or right side of the screen, depending on the left-to-right or right-to-left orientation of the system as a whole (a regional variance).

And with that we’ve covered all the methods and properties of this object! Yet the most interesting part is how we add our own commands to the settings pane. But let’s first look at a few design considerations as described on Guidelines for app settings.

Design Guidelines for Settings

Beyond the commands that Windows automatically adds to the settings pane, an app can provide up to eight others, typically around four; anything more than eight will throw an exception. Because settings are global to an app, the commands you add are always the same: they are not sensitive to context. To say it another way, the only commands that should appear on the settings pane are those that are global to the app (refer back to Figure 10-4 for examples); commands that apply only to certain pages or contexts within a page should appear on the app bar or app canvas.

Each app-supplied command can do one of two things. First, a command can simply be a hyperlink to a web page. Some apps use links for their Help, Privacy Statement, Terms of Use, License Agreements, and so on, which will open the linked pages in a browser. The other option is to have the command invoke a secondary flyout panel with more specific settings controls or simply a webview to display web-based content. You can provide Help, Terms of Use, and other textual content in both these ways rather than switch to the browser.

Note As stated in the App certification requirements, section 4.1.1, apps that collect personal information in any way, or even just use a network, must have a privacy policy or statement. This must be included on the app’s product description page in the Store and must also be accessible through a command in your Settings pane.

Secondary flyouts are created with the WinJS.UI.SettingsFlyoutcontrol; some examples are shown in Figure 10-5. Notice that the secondary settings panes can be sized however needed, but they should fall between 346px and 646px. You should style the flyout header (using the win-header class) to use your app’s primary color for the background and style the entire flyout with a border color that’s 20% darker. Also note that the Permissions flyout, shown on the left of Figure 10-5, is provided by Windows automatically, is configured according to capabilities declared in your manifest, and uses the system colors to specifically differentiate system settings. Some capabilities like geolocation are controlled in this pane; other capabilities like Internet and library access are simply listed because the user is not allowed to turn them on or off.

images

FIGURE 10-5 Examples of secondary settings panes in the Travel, Weather, News, and Music apps. The first three are 346px wide; the fourth is 646px. Notice that each app-provided pane is appropriately branded and provides a back button to return to the main Settings pane. The Permissions pane is provided by the system and thus reflects the system theme (that is, it cannot be customized).

A common group of settings are those that allow the user to configure their roaming experience—a group of settings that determine what state is roamed (you see this on PC Settings > OneDrive > Sync Settings). It is also recommended that you include account/profile management commands within Settings, as well as login/logout functionality. As noted in Chapter 9, “Commanding UI,” logins and license agreements that are necessary to run the app at all should be shown upon launch. For ongoing login-related functions, and to review license agreements and such, create the necessary commands and panes within Settings. Refer to Guidelines for login for more information on this subject. Guidelines for a Help command can also be found on Quickstart: add app help.

Behaviorally, settings panes are light-dismiss (returning to the app) and have a back button to return to the primary settings pane with all the commands. Because of the light-dismiss behavior, changing a setting on a pane applies the setting immediately: there is no OK or Apply button or other such UI. If the user wants to revert a change, she should just restore the original setting.

For this reason it’s a good idea to use simple controls that are easy to switch back, rather than complex sets of controls that would be difficult to undo. The recommendation is to use toggle switches for on/off values (rather than check boxes), a button to apply an action (but without closing the settings UI), hyperlinks (to open the browser), text input boxes (which should be set to the appropriate type such as email address, password, etc.), radio buttons for groups of up to five mutually exclusive items, and a listbox (select) control for four to six text-only items.

In all your settings, think in terms of “less is more.” Avoid having all kinds of different settings, because if the user is never going to find them, you probably don’t need to surface them in the first place! Also, while a settings pane can scroll vertically, try to limit the overall size such that the user has to pan down only once or twice, if at all (that is, three pages on a 768px vertical display).

Some other things to avoid with Settings:

• Don’t use Settings for workflow-related commands. Those belong on the app bar or on the app canvas, as discussed in Chapter 9.

• Don’t use a top-level command in the Settings pane to perform an action other than linking to another app (like the browser). Top-level commands should never execute an action within the app.

• Don’t use settings commands to navigate within the app.

• Don’t use WinJS.UI.SettingsFlyout as a general-purpose control.

And on that note, let’s now look at the steps to use Settings and the SettingsFlyout properly!

Populating Commands

The first part of working with Settings is to provide your specific commands when the Settings charm is invoked. Unlike app bar commands, these should always be the same no matter the state of the app; if you have context-sensitive settings, place commands for those in the app bar.

The two ways to implement this process in an app written in HTML and JavaScript are using WinRT directly or using the helpers in WinJS. Let’s look at these for a simple Help command.

To know when the charm is invoked through WinRT, obtain the settings pane object through Windows.UI.ApplicationSettings.SettingsPane.getForCurrentView and add a listener for its commandsrequested event (this is a WinRT event, so be sure to remove the listener if necessary):

// The n variable here is a convenient shorthand

var n = Windows.UI.ApplicationSettings;

var settingsPane = n.SettingsPane.getForCurrentView();

settingsPane.addEventListener("commandsrequested", onCommandsRequested);

Within your event handler, create SettingsCommand objects for each command, where each command has an id, a label, and an invoked function that’s called when the command is tapped or clicked. These can all be specified in the constructor:

function onCommandsRequested(e) {

// n is still the shortcut variable to Windows.UI.ApplicationSettings

var commandHelp = new n.SettingsCommand("help", "Help", helpCommandInvoked);

e.request.applicationCommands.append(commandHelp);

}

A command is added to the Settings pane by adding it to the e.request.applicationCommands vector; above we use the vector’s append method, but you could also use insertAt. You’d make such a call for each command, or you can pass an array of such commands to the vector’sreplaceAll method. What then happens within the invoked handler for each command is the interesting part, and we’ll come back to that soon.

You can also prepopulate the applicationCommands vector outside of the commandsrequested event; this is perfectly fine because your settings commands should be constant for the app anyway. Here’s an example, which also shows the vector’s replaceAll method:

var n = Windows.UI.ApplicationSettings;

var settingsPane = n.SettingsPane.getForCurrentView();

var vector = settingsPane.applicationCommands;

//Ensure no settings commands are currently specified in the settings charm

vector.clear();

var commands = [new settingsSample.SettingsCommand("Custom.Help", "Help", OnHelp),

new n.SettingsCommand("Custom.Parameters", "Parameters", OnParameters)];

vector.replaceAll(commands);

This way, you don’t actually need to register for or handle commandsrequested directly.

Now because most apps will likely use settings in some capacity and will typically employ flyouts for each command, WinJS provides some shortcuts to this whole process. First, instead of listening for the WinRT event, simply assign a handler to WinJS.Application.onsettings (which is a wrapper for commandsrequested):

WinJS.Application.onsettings = function (e) {

// ...

};

In your handler, create a JSON object describing your commands and store that object in e.detail.applicationcommands. Mind you, this is different from the WinRT object—just setting this property accomplishes nothing. What comes next is passing the now-modified event object to the static WinJS.UI.SettingsFlyout.populateSettings method as follows (taken from scenario 2 of the App settings sample, js/2-AddFlyoutToCharm.js):

WinJS.Application.onsettings = function (e) {

e.detail.applicationcommands =

{ "help": { title: "Help", href: "/html/2-SettingsFlyout-Help.html" } };

WinJS.UI.SettingsFlyout.populateSettings(e);

};

The populateSettings method traverses the e.details.applicationcommands object and calls the WinRT applicationCommands.append method for each item. This gives you a more compact means to accomplish what you’d do with WinRT, and it also simplifies the implementation of settings commands, as we’ll see in a moment.

Tip The populateSettings method is just a helper function and isn’t anything you’re required to use. You can easily see its implementation in the WinJS ui.js file and even make a copy of the code to customize it however you like.

As you can see above, the JSON in e.detail.applicationcommands has this format:

{ <command id>: { title: <command label>, href: <path to in-package flyout markup> }}

The href property here must refer to an in-package HTML file that describes the content of the SettingsFlyout that WinJS will invoke for that command. That is, you cannot use href to specify an arbitrary URI to launch a browser, as commonly employed for Terms of Service, Privacy Statement, and other such commands.

To intermix external URI commands with flyouts, you need to use a combination of both the WinJS and WinRT APIs (or you can just bring the external content into a flyout with a webview). Fortunately, within WinJS.Application.onsettings, the event args for the original WinRTcommandsrequested event is available in the e.detail.e property. That is, within the WinJS event, e.detail.e.request.-applicationCommands is the WinRT vector. Thus, you can call WinJS.UI.SettingsFlyout.-populateSettings for those commands that use flyouts and then create and add other commands through e.detail.e.request.applicationCommands.append or insertAt. You’d use insertAt, clearly, if you want to place a command at a specific point in the list rather than add it to the end.

Caveat You can call populateSettings only once, because WinJS internally stores the list of commands in another internal object. Any subsequent call with a different list of commands will cause any previous commands to be visible but unresponsive. In short, don’t do it.

Implementing Commands: Links and Settings Flyouts

Technically speaking, you can do anything you want within the invoked function for a settings command. Truly! Of course, as described in the design guidelines earlier, recommendations exist for how to use settings and how not to use them. For example, settings commands shouldn’t act like app bar commands that affect content, nor should they navigate within the app itself. Ideally, a settings command does one of two things: launch a hyperlink (to open a browser) or display a secondary settings pane.

In the first case, launching a hyperlink uses the Windows.System.Launcher.launchUriAsync API as follows:

function onCommandsRequested(e) {

// n is still the shortcut variable to Windows.UI.ApplicationSettings

var commandHelp = new n.SettingsCommand("help", "Help", helpCommandInvoked);

e.request.applicationCommands.append(commandHelp);

}

function helpCommandInvoked(e) {

var uri = new Windows.Foundation.Uri("http://example.domain.com/help.html");

Windows.System.Launcher.launchUriAsync(uri).done();

}

In the second case, settings panes are implemented with the WinJS.UI.SettingsFlyout control. Again, technically speaking, you’re not required to use this control: you can display any UI you want within the invoked handler. The SettingsFlyout control, however, supplies enter and exit animations and fires events like [before | after][show | hide]83. And because the flyout automatically handles vertical scrolling with any HTML you place within it, including other controls (almost), there’s no reason not to use it.

Note The one limitation is that nesting flyouts is not presently supported in WinJS, so you cannot have a secondary flyout appear in the context of a settings flyout.

Because it’s a WinJS control, you can declare a SettingsFlyout for each one of your commands in markup (making sure WinJS.UI.process/processAll is called, which handles any other controls in the flyout). For example, scenario 2 of the App settings sample defines the following flyout for its Help command (html/2-SettingsFlyout-Help.html, omitting the text content and reformatting a bit); the result of this is shown in Figure 10-6:

<div data-win-control="WinJS.UI.SettingsFlyout" id="helpSettingsFlyout"

aria-label="Help settings flyout" data-win-options="{settingsCommandId:'help'}">

<!-- Use either 'win-ui-light' or 'win-ui-dark' depending on the contrast between the

header title and background color; background color reflects app's personality -->

<div class="win-ui-dark win-header" style=" background-color:#00b2f0">

<button type="button" id="backButton" class="win-backbutton"></button>

<div class="win-label">Help</div>

<img src="../images/smallTile-sdk.png" style=" position: absolute; right: 40px;"/>

</div>

<div class="win-content ">

<div class="win-settings-section">

<h3>Settings charm usage guidelines summary</h3>

<!-- Other content omitted -->

<li>For more in-depth usage guidance, refer to the

<a href="http://msdn.microsoft.com/en-us/library/windows/apps/hh770544">

App settings UX guide</a>.</li>

</div>

</div>

</div>

As always, the SettingsFlyout control has options (just one, settingsCommandId for obvious purpose) as well as a few applicable win-* style classes: win-settingsflyout, which styles the whole control, most especially for width and your border color, and win-ui-light and win-ui-dark, which apply a light or dark theme to the contents of the flyout. In this example, we use the dark theme for the header while the rest of the flyout uses the default light theme, which is inherited from the app’s global stylesheet (that is, default.html pulls in ui-light.css).

images

FIGURE 10-6 The Help settings flyout (truncated vertically) from scenario 2 of the App settings sample. Notice the hyperlink on the bottom.

In any case, you can see that everything within the control is just markup for the flyout contents, nothing more, and you can wire up events to controls in the markup or in code. You’re free to use hyperlinks here, such as to launch the browser to open a fuller Help page. You can also use a webview to host web content within a settings flyout; for an example, see the Here My Am! example in this chapter’s companion content, specifically html/privacy.html.

Tip If you load any stylesheets in settings flyouts, they will be added into the global stylesheet and can possibly affect the rest of the app. Make sure, then, to avoid conflicts by scoping your selectors specifically to elements inside the flyout.

So, how do we get a flyout to show when a command is invoked on the top-level settings pane? The easy way is to let WinJS take care of the details using the information you provide to WinJS.UI.-SettingsFlyout.populateSettings. When you specify a reference to the flyout’s markup, like we saw earlier (from js/2-AddFlyoutToCharm.js):

WinJS.Application.onsettings = function (e) {

e.detail.applicationcommands =

{ "help": { title: "Help", href: "/html/2-SettingsFlyout-Help.html" } };

WinJS.UI.SettingsFlyout.populateSettings(e);

};

then WinJS will automatically invoke a flyout with that markup when the command is invoked, calling WinJS.UI.processAll along the way. This is why in most of the scenarios of the sample you don’t see any explicit calls to showSettings, just a call to populateSettings. But you can useshowSettings to programmatically invoke a flyout, as we’ll now see.

Programmatically Invoking Settings Flyouts

In addition to being a control that you use to define a specific flyout, WinJS.UI.SettingsFlyout has a couple of other static methods beyond populateSettings: show and showSettings. The show method specifically brings out the top-level Windows settings pane—that is, Windows.UI.-ApplicationSettings.SettingsPane. A call to show is what you typically wire up to a flyout’s back button so that you return to the main Settings pane.

Note Although it’s possible to programmatically invoke your own settings panes, you cannot do so with the system-provided Permissions command. If you have a condition for which you need the user to change a permission, such as enabling geolocation, the recommendation is to display an error message that instructs the user to use the Permissions command (as Here My Am! in this chapter does) and that perhaps opens the main Setting pane. You cannot invoke the Rate And Review settings command either, but you can launch the ms-windows-store:REVIEW?PFN=<package_family_name> URI to achieve that end. See “Connecting Your Website and Web-Mapped Search Results” in Chapter 20, “Apps for Everyone, Part 2.”

The showSettings method, for its part, shows a specific settings flyout that you define in your app. The signature of the method is showSettings(<id> [, <page>])where <id> identifies the flyout you’re looking for and the optional <page> parameter identifies an HTML document to look in if a flyout with <id> isn’t found in the current document. That is, showSettings always starts by looking in the current document for a SettingsFlyout element that has a matching settingsCommandId property or a matching HTML id attribute. If such a flyout is found, that UI is shown.

If the markup in the previous section (with Figure 10-7) was contained in the same HTML page that’s currently loaded in the app, the following line of code will show that flyout:

WinJS.UI.SettingsFlyout.showSettings("help");

In this case you could also omit the href part of the JSON object passed to populateCommands, but only again if the flyout is contained within the current HTML document already.

It usually makes more sense to separate your settings flyouts from the rest of your markup and then use the page parameter to showSettings, passing a URI for a page in your app package. The App settings sample uses this to place the flyout for each scenario into a separate HTML file. You can also place all your flyouts in one HTML file, so long as they have unique ids. Either way, showSettings loads the flyout’s HTML into the current page using WinJS.UI.Pages.load (which calls WinJS.UI.-processAll), scans that DOM tree for a matching flyout with the given <id>, and shows it. Failure to locate the flyout will throw an exception.

Scenario 4 of the App settings sample shows this form of programmatic invocation. This is also a good example (see Figure 10-7) of a vertically scrolling flyout (js/4-ProgrammaticInvocation.js):

WinJS.UI.SettingsFlyout.showSettings("defaults", "/html/4 -SettingsFlyout-Settings.html");

images

images

FIGURE 10-7 The settings flyout from scenario 4 of the App settings sample, showing how a flyout supports vertical scrolling; note the scrollbar positions for the top portion (left) and the bottom portion (right).

A call to showSettings is thus exactly what you use within any particular command’s invoked handler; it’s what WinJS sets up within populateCommands. But it also means you can call showSettings from anywhere else in your code when you want to display a particular settings pane. For example, if you encounter an error condition in the app that could be rectified by changing an app setting, you can provide a button in a message dialog or notification flyout that calls showSettings to open that particular pane. And for what it’s worth, the hide method of that flyout will dismiss it; it doesn’t affect the top-level settings pane for which you must use Windows.UI.Application-Settings.SettingsPane.getForCurrentView().hide.

You might use showSettings and hide together, in fact, if you need to navigate to a third-level settings pane. One of your own settings flyouts could contain a command that calls hide on the current flyout and then calls showSettings to invoke another. The back button of that subsidiary flyout (and it should always have a back button) would similarly call hide on the current flyout and showSettings to make its second-level parent reappear. That said, we don’t recommend making your settings so complex that third-level flyouts are necessary, but the capability is there if you have a particular scenario that demands it.

Knowing how showSettings tries to find a flyout is also helpful if you want to create a SettingsFlyout programmatically. So long as such a control is in the DOM when you call showSettings with its id, WinJS will be able to find it and display it like any other. It would also work, though I haven’t tried this and it’s not in the sample, to use a kind of hybrid approach. Because showSettings loads the HTML page you specify as a page control with WinJS.UI.Pages.load, that page can also include its own script wherein you define a page control object with methods likeprocessed and ready. Within those methods you could then make specific customizations to the settings flyout defined in the markup.

Sidebar: Changes to Permissions

A common question is whether an app can receive events when the user changes settings within the Permissions pane. The answer is no, which means that you discover whether access is disallowed only by handling Access Denied exceptions when you try to use the capability. To be fair, though, you always have to handle denial of a capability gracefully because the user can always deny access the first time you use the API. When that happens, you again display a message about the disabled permission (as shown with the Here My Am! app) and provide some UI to reattempt the operation. But the user still needs to invoke the Permissions settings manually. Refer to the Guidelines for devices that access personal data for more details, specifically in the “What if access to a device is turned off?” section.

Here My Am! Update

To bring together some of the topics we’ve covered in this chapter, the companion content includes another revision of the Here My Am! app with the following changes and additions (mostly to pages/home/home.js unless noted):

• It now incorporates the Bing Maps SDK so that the control is part of the package rather than loaded from a remote source. This eliminates the webview we’ve been using to host the map, so all the core code from html/map.html can move into js/default.js and we can eliminate the code needed to communicate between the app and the webview. Note that to run this sample in Visual Studio you need to download and install the SDK yourself (be sure to choose the version for Windows 8.1).

• Instead of copying pictures taken with the camera to app data, those are now copied to a HereMyAm folder in the Pictures library. The Pictures Library capability has been declared.

• Instead of saving a pathname to the last captured image file, which is used when the app is terminated and restarted, the StorageFile is saved in Windows.Storage.AccessCache to guarantee future programmatic access.

• An added appbar command allows you to use the File Picker to select an image to load instead of relying solely on the camera. This also allows you to use a camera app, if desired. Note that we use a particular settingsIdentifier with the picker in this case to distinguish from the picker for recent images. We’ll again learn about the file pickers in Chapter 11.

• Another appbar command allows you to choose from recent pictures from the camera. This defaults initially to the Pictures library but uses a different settingsIdentifier so that subsequent invocations will default to the last viewed location.

• Additional commands for About, Help, and a Privacy Statement are included on the Settings pane using the WinJS.Application.onsettings event (see js/default.js). The first two display content from within the app whereas the third pulls down web content in a webview; all the settings pages are found in the html folder of the project, with styles in css/default.css.

What We’ve Just Learned

• Statefulness is important to Windows Store apps, to maintain a sense of continuity between sessions even if the app is suspended and terminated.

• App data is session, local, temporary, and roaming state that is tied to the existence of an app; it is accessible only by that app.

• User data is stored in locations other than app data (such as the user’s music, pictures, and videos libraries, along with removable storage) and persists independent of any given app. Multiple apps might be able to open and manipulate user files.

• The StorageFolder and StorageFile classes in WinRT are the core objects for working with folders and files. All programmatic access to the file system begins, in fact, with a StorageFolder. The Windows.Storage.FileIO and PathIO classes simplify file access, as do helpers inWinJS.Application.

• WinRT offers encryption services through Windows.Security.Cryptography, as well as a built-in compression mechanism in Windows.Storage.Compression.

• Streams are the objects through which you general access file contents. Blobs and buffers interact with streams to handle different interchange needs between WinRT and the app host.

• App data is accessed through the Windows.Storage.ApplicationData API and accommodates both structured settings containers as well as file-based data. Additional APIs like IndexedDB and HTML5 localStorage are also available. Third-party libraries, such as SQLite and the OData Library for JavaScript, provide other options.

• It is important to version app state, especially where roaming is concerned, because versioning is how the roaming service manages what app state gets roamed to which devices based on what version apps are looking for.

• The size of roaming state is limited to a quota (provided by an API), otherwise Windows will not roam the data. Services like OneDrive can be used to roam larger files, including user data.

• The typical roaming period is 30 minutes or less. A single setting or composite named “HighPriority,” so long as it’s under 8K, will be roamed within a minute.

• To use the Settings pane, an app populates the top-level pane provided by Windows with specific commands. Those commands map to handlers that either open a hyperlink (in a browser) or display a settings flyout using the WinJS.UI.SettingsFlyout control. Those flyouts can contain any HTML desired, including webview elements that load remote content.

• Settings panes can be invoked programmatically when needed.

79 You can maintain your own versioning system within particular files or settings, but I recommend against doing this with roaming data because it’s hard to predict how Windows will manage synchronizing slightly different structures. Even with local state, trying to play complex versioning games is, well, rather complex and probably best avoided altogether.

80 If you’re curious why async methods like readText and writeTextdon’t have Async in their names, this was a conscious choice on the part of the WinJS designers to follow existing JavaScript conventions where such a suffix isn’t typically used. The WinRT API, on the other hand, is language-independent and thus has its own convention with the Async suffix.

81If you use newwith DataReader, you provide an IInputStream argument instead—with buffers you have to use the static method. The reason for this is that JavaScript cannot differentiate overloaded methods by arity only (number of arguments), thus the designers of WinRT have had to make a few oddball choices like this where the more common usage employs the constructor and a static method is used for the less common option.

82 It’s a very robust solution—it’s apparently used to operate commercial airliners like the massive Airbus A380.

83 How’s that for a terse combination of four event names? It’s also worth noting that the document.body.DOMNodeInserted event will also fire when a flyout appears.