Creating a fast and fluid app launch experience

AWS

Owner
FPCH Owner
Joined
Nov 19, 2003
Messages
10,976
Location
Florida U.S.A.
App launch is a principal part of the fast and fluid Windows 8 experience, so it’s important that you prioritize your app’s launch UX. A highly-polished launch flow is sure to improve the initial reception of any app. In this post, I’ll discuss how to craft a well-designed, responsive app launch experience and explain why app launch is a critical time to make a positive impression on users. I’ll introduce four app launch design patterns that can be applied to your apps and point out some key things to keep in mind as you continue building Metro style apps.
App launch overview

If you’ve already read the Managing app lifecycle so your apps feel “always alive” post, you should be familiar with app lifecycle states. This post targets app launch, or the transition between the “not running” and “running” state.
.​

Lifecycle states: app launch is the transition from the “not running” to “running” state.
Because app launch is highly visible, it’s important to plan this transition well. Not only is the transition front-and-center, but users will experience it repeatedly. The more contracts (see past post: Activating Windows 8 contracts in your app) you implement, the more reasons users will have to launch your app. Whether you make a positive or negative first impression will depend on how you’ve designed this transition it may also, ultimately, determine whether users routinely return to your app.
Before I recommend a few ways to handle app launch, it might be useful to review the sequence of operations during this transition.
.​
The sequence of operations during app launch.
When users launch an app, they are immediately greeted by the splash screen. Every Metro style app has a splash screen, which consists of a 620x300 image and solid background color. (See Quickstart: Adding a splash screen to learn how to customize your splash screen.) Windows presents the splash screen on your behalf in order to welcome users while your app is activated. The activated event is received by all apps on launch, and gives your app the ability to perform any initialization work needed to present its initial UI. This might include reading basic settings, determining what page to navigate to, and/or identifying whether the app was activated for one of the various contracts. After initialization is complete and your app is ready to dismiss the splash screen, it must present its first window. No work is required for JavaScript apps, as this is done automatically when the activation callback returns. C# apps, however, must do this explicitly through a call to Window.Current.Activate. Be careful not to delay this operation, as your app will be terminated if a window is not displayed within a reasonable amount of time (~15 seconds). In addition, you’ll want to present a window as fast as possible because unnecessarily keeping the splash screen up can quickly deteriorate the user experience. We recommend that you present a window within 2-3 seconds to ensure that your app will always launch as expected, even on low-end hardware.
After the app’s window is presented and the splash screen is dismissed, apps take control of the experience it is up to you to determine how users will be introduced to your app’s content. Depending on the app, different transitions might be appropriate. In the next few sections, I’ll recommend four app launch design patterns that can be applied based on your app’s needs:
  • Default app launch
For apps that don’t require additional loading and are immediately ready to use.
Ex: A dictionary app that enables users to look up or translate various terms. The landing page consists only of a textbox for user input.
  • Skeleton app launch
Great for apps that fill the landing page incrementally on launch.
Ex: A reading app that tracks the user’s books, magazines, and newspapers. When launched, the app incrementally populates the user’s library.
  • Extended app launch
For apps that perform lengthy loading operations before presenting UI. This might include network calls or substantial file I/O.
Ex: A sports app that shows the latest scores and highlights. The app uses REST APIs to retrieve this information over the network and displays live data on the landing page.
  • Deferred app launch
Useful for apps that need to complete basic asynchronous tasks on launch, like querying app settings to check for first-run.
Ex: A game that needs to determine whether the user has already created an account. This information is needed to determine which page to present to the user.
Default app launch

For many apps, the default app launch flow will be optimal. In this flow, Windows handles the display and removal of each app’s splash screen. The splash screen is displayed until activation completes and a window is presented, triggering an animated crossfade to the app’s landing page. Use default launch if your landing page is static and doesn’t depend on additional loading operations. If your content is ready immediately, there’s no need to artificially delay the user. In fact, you shouldn’t!
Below is an example of the default launch flow using Internet Explorer 10 from Consumer Preview. When the app is launched, the splash screen is immediately displayed to the user. The splash screen stays visible on the screen until the app is activated, at which point the splash screen fades out into the landing page. In this case, the landing page shows the last page visited by the user, which is readily available because it was saved during the previous session.
Here’s the process displayed in the following image:
  1. App tile clicked.
  2. Splash screen displayed.
  3. App landing page displayed.
.​

The default app launch flow.
To implement the default launch flow, no additional work is required. Simply specify your 620x300 splash screen image in your app’s manifest and then design the landing page using static content. After activation completes, the splash screen will fade out into your landing page and your app will be ready to use.
Skeleton app launch

For a majority of apps, default launch will work great. After the splash screen is dismissed, the app will be running and ready for interaction. Some apps, however, might not be entirely ready at this point these apps will need to load content dynamically after they launch. For this class of app, the skeleton app launch pattern is a great way to provide meaningful loading information to users, while bringing them into the app as quickly as possible. In this pattern, the splash screen dismisses to a skeleton landing page (the landing page, as it would look, without any content), while content is retrieved. By adding a progress bar to the page, you can indicate that the app is still loading. This next image shows an example of the skeleton launch flow using the Music app from Consumer Preview. Here, the skeleton landing page is displayed while the latest music content is retrieved.
Here’s the process displayed in the following image:
  1. App tile clicked.
  2. Splash screen displayed.
  3. Skeleton landing page displayed. Note the progress bar displayed at the top of the view.
  4. App landing page displayed.
.​
The skeleton app launch flow.
To implement the skeleton launch flow, create a static, skeleton view of your app’s landing page and display it as your start page. Then, during activation, begin executing the loading tasks needed to fill the page with content. Because most operations are asynchronous, the splash screen might be torn down while these tasks are still executing. As a result, your users will see the landing page fill incrementally.
If you are interested in learning when the splash screen has been dismissed, you can use the splash screen API. The API includes the SplashScreen.Dismissed event, which indicates when the transition from splash screen to app start page has occurred. This may be useful if you want to know when your skeleton landing page is in view.
JavaScript

As you will see in the following example, your app can begin executing operations to fill the landing page inside of the activated callback. To optionally learn when the splash screen is dismissed, use the activated event args to obtain the splash screen object. Using this object, register for the dismissed event to be notified of splash screen dismissal.
app.onactivated = function (eventArgs) {
if (eventArgs.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
// Begin executing setup operations.
performSetupTasks()

// Retrieve splash screen object.
var splash = eventArgs.detail.splashScreen

// Register an event handler to be executed when the splash screen has been dismissed.
splash.addEventListener("dismissed", onSplashScreenDismissed, false)
...
}
}

function performSetupTasks() {
// Begin additional loading tasks here…
...
}

function onSplashScreenDismissed() {
// The splash screen has dismissed and the skeleton landing page is now in view.
...
}​
C#

Implementation is no different in C#. Begin operations that are necessary to fill the landing page during activation. Then, optionally obtain the splash screen object from the activated event args and register to be notified of splash screen dismissal.
async protected override void OnLaunched(LaunchActivatedEventArgs args)
{
// Begin executing setup operations.
PerformSetupTasks()

// Retrieve splash screen object.
SplashScreen splashScreen = args.SplashScreen

// Register an event handler to be executed when the splash screen has been dismissed.
splashScreen.Dismissed += new TypedEventHandler<SplashScreen, object>(eSplash.onSplashScreenDismissed)
...
}

internal void PerformSetupTasks()
{
// Begin additional loading tasks here…
...
}

internal void onSplashScreenDismissed(Windows.ApplicationModel.Activation.SplashScreen sender, object e)
{
// The splash screen has dismissed and the skeleton landing page is now in view.
...
}​
Extended app launch

For apps that perform additional loading tasks post-launch, skeleton app launch is a great option. That said, it does have one downside: because users are brought to the “skeleton” page immediately, the collective loading flow might feel disjointed. The transition from splash screen to skeleton landing page may give users the impression that there are two, independent loading operations. When this is undesirable, the extended app launch pattern is a great alternative.
This flow uses the concept of an extended splash screen to produce one, seamless loading experience. When the regular splash screen is dismissed, the app displays an extended splash screen instead of the landing page. The extended splash screen is owned entirely by the app and is formatted using the splash screen API. The API provides positioning information that ensures the look and feel of the extended splash screen is visually identical to the splash screen (with the exception of a progress ring or loading details), which unifies the seemingly independent loading operations. While the extended splash screen is up, the app can continue executing tasks needed to paint the landing page. Then, after loading is complete, you can transition from the extended splash screen to the landing page.
The extended launch flow works especially well if initialization could be lengthy (network connectivity, for example, is unpredictable). If you need to do any sort of “heavy lifting” during launch, the extended splash screen is the ideal choice. It’s also a great choice if you want to ensure that your landing page has been fully updated before transitioning to it (if you choose not to show cached data).
This next example shows the extended launch flow using the Weather app from Windows 8 Consumer Preview. A Weather app is a great example of an app that might implement the extended launch pattern, because the extended splash screen can be shown while the latest weather data is requested from the network (there’s not much value in showing the user cached weather data).
Here’s the process displayed in the following image:
  1. App tile clicked.
  2. Splash screen displayed.
  3. Extended splash screen displayed (with progress ring).
  4. App landing page displayed.
.​

The extended app launch flow.
To implement the extended launch flow, the splash screen API must be used. Using SplashScreen.ImageLocation, apps can obtain the image coordinates for the splash screen image. This is important, as the image must be positioned in the exact same location in the extended splash screen for the user to perceive a smooth transition. As mentioned in the previous section, the API also exposes a SplashScreen.Dismissed event, which will notify you of the transition from regular splash screen to your extended splash screen.
Like before, you can trigger additional tasks during activation. Below, we’ll walk through the implementation of an extended splash screen (with an optional progress ring) in both JavaScript and C#.
Note: When implementing an extended splash screen, it’s also important to handle snapping, unsnapping, rotation, and so on. These details are omitted in this post for the sake of brevity. To learn more, see the splash screen sample.
JavaScript

First, add HTML markup for the extended splash screen (with progress ring) to your start page.
<div id="extendedSplashScreen" class="extendedSplashScreen hidden">
<img id="extendedSplashImage" src="/images/splash.png" style="position: absolute" />
<progress id="extendedSplashProgress" style="color: white" class="win-medium win-ring"></progress>
</div>
Next, add CSS styles for the extended splash screen.
.extendedSplashScreen {
position: absolute
text-align: center
background-color: #000000
height: 100%
width: 100%
top: 0px
left: 0px
}
.extendedSplashScreen.hidden {
display: none
}​
Again, start additional tasks during activation and use the activated event args to obtain the splash screen object. This time, the splash screen object will be used to both register for the dismissed event and to set up the extended splash screen. Query the splash screen object for the position of the splash screen image and arrange the layout accordingly.
app.onactivated = function (eventArgs) {
if (eventArgs.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
// Begin executing setup operations.
performSetupTasks()

// Retrieve splash screen object.
var splash = eventArgs.detail.splashScreen

// Register an event handler to be executed when the regular splash screen has been dismissed.
splash.addEventListener("dismissed", onSplashScreenDismissed, false)

// Display the extended splash screen.
displayExtendedSplash(splash)

WinJS.UI.processAll()
}
}

function displayExtendedSplash(splash) {
// Position the extended splash screen image in the same location as the system splash screen image.
var extendedSplashImage = document.getElementById("extendedSplashImage")
extendedSplashImage.style.top = splash.imageLocation.y + "px"
extendedSplashImage.style.left = splash.imageLocation.x + "px"
extendedSplashImage.style.height = splash.imageLocation.height + "px"
extendedSplashImage.style.width = splash.imageLocation.width + "px"

// Position the extended splash screen's progress ring.
var extendedSplashProgress = document.getElementById("extendedSplashProgress")
extendedSplashProgress.style.marginTop = splash.imageLocation.y + splash.imageLocation.height + 32 + "px"

// After the extended splash screen is set up,
// apply the CSS style that will make the extended splash screen visible.
var extendedSplashScreen = document.getElementById("extendedSplashScreen")
WinJS.Utilities.removeClass(extendedSplashScreen, "hidden")
}​
Specify the loading tasks to be completed while the extended splash screen is up. When loading is complete, tear down the extended splash screen. Make sure to handle error cases gracefully if loading tasks timeout or fail, transition the user to a page that explains what’s happened.
function performSetupTasks() {
// Begin additional loading tasks here…
...

// Tear down the extended splash screen after all operations are complete.
removeExtendedSplash()
}

function onSplashScreenDismissed() {
// The splash screen has been dismissed and the extended splash screen is now in view.
...
}

function removeExtendedSplash() {
var extendedSplashScreen = document.getElementById("extendedSplashScreen")
WinJS.Utilities.addClass(extendedSplashScreen, "hidden")
}​
C#

Like JavaScript, we need to add markup for the extended splash screen with progress ring. This time, we’ll use XAML.
<!-- This snippet represents the ExtendedSplash class -->
<Canvas Grid.Row="0">
<Image x:Name="extendedSplashImage" Source="images/splash.png" />
<ProgressRing x:Name="ProgressRing" Foreground="White"
HorizontalAlignment="Center" IsActive="True"
MaxHeight="30" MinHeight="30" MaxWidth="30"
MinWidth="30"></ProgressRing>
</Canvas>
Again, we’ll start by kicking off loading tasks in activation. We’ll then obtain the splash screen object from the activated event args and display the extended splash screen.
async protected override void OnLaunched(LaunchActivatedEventArgs args)
{
// Begin executing setup operations.
PerformSetupTasks()

// Retrieve splash screen object.
SplashScreen splashScreen = args.SplashScreen

ExtendedSplash eSplash = new ExtendedSplash(splashScreen)

// Register an event handler to be executed when the splash screen has been dismissed.
splashScreen.Dismissed += new TypedEventHandler<SplashScreen, object>(eSplash.onSplashScreenDismissed)
...

Window.Current.Content = eSplash
Window.Current.Activate()
}

public ExtendedSplash(SplashScreen splash)
{
InitializeComponent()

// Position the extended splash screen image in the same location as the splash screen image.
this.extendedSplashImage.SetValue(Canvas.LeftProperty, splash.ImageLocation.X)
this.extendedSplashImage.SetValue(Canvas.TopProperty, splash.ImageLocation.Y)
this.extendedSplashImage.Height = splash.ImageLocation.Height
this.extendedSplashImage.Width = splash.ImageLocation.Width

// Position the extended splash screen's progress ring.
this.ProgressRing.SetValue(Canvas.TopProperty, splash.ImageLocation.Y + splash.ImageLocation.Height + 32)
this.ProgressRing.SetValue(Canvas.LeftProperty,
splash.ImageLocation.X +
(splash.ImageLocation.Width / 2) - 15)
}​
Perform necessary loading tasks while the extended splash screen is on screen, and then navigate to the landing page.
internal void PerformSetupTasks()
{
// Begin additional loading tasks here…
...

// Tear down the extended splash screen after all operations are complete.
RemoveExtendedSplash()
}

internal void onSplashScreenDismissed(Windows.ApplicationModel.Activation.SplashScreen sender, object e)
{
// The splash screen has been dismissed and the extended splash screen is now in view.
...
}

void RemoveExtendedSplash()
{
Window.Current.Content = new LandingPage()
}​
Deferred app launch

The final launch pattern I’ll discuss is deferred app launch. Deferred launch is similar to extended launch in that it enables the preparation of the landing page before transitioning away from the splash screen experience. Instead of displaying an “extended” splash screen, however, the app defers dismissal of the regular splash screen until asynchronous tasks have been executed. Because apps have limited time to complete this deferral, the deferral pattern should be used sparingly, and mainly to complete simple asynchronous operations like reading app settings before the app is displayed. Furthermore, if you fail to appropriately handle errors/exceptions during this time, it’s possible that launch will be terminated. Accordingly, if your app needs to make network calls or process large amounts of data, the skeleton or extended app launch pattern should be used instead.
Below is an example of deferred launch, using the Photos app from Consumer Preview. When a .jpg file is opened from the desktop, the Photos app is launched as the default handler for this file type. The Photos app uses deferral to load the image thumbnail before the splash screen is dismissed. This is an acceptable use of deferral as the thumbnail retrieval operation can be completed relatively fast. Then, after the user is in the app, the higher-res image can be acquired asynchronously to replace the thumbnail. This flow ensures that when the user transitions to the app, the thumbnail is already on screen.
Here’s the process displayed in the following image:
  1. The .jpg image is clicked (Photos is the default .jpg handler).
  2. Splash screen displayed.
  3. Activation deferred, while thumbnail is retrieved.
  4. App displayed with thumbnail in view.
.​

The deferred app launch flow.
To implement the deferred launch flow in JavaScript, activation deferral must be used. Deferral gives apps the ability to defer the completion of activation until asynchronous operations are completed. Otherwise, activation is completed when the callback returns. Because most Windows Runtime APIs are asynchronous, this is useful for apps that want to make sure things are done prior to the display of the app’s initial UI. In C#, apps can implement deferral by delaying the Window.Current.Activate call. Below, we’ll demonstrate both approaches.
JavaScript

There are two ways to implement activation deferral in JavaScript, depending on how your app handles activation.
If your app uses WinJS to handle activation (default for VS project templates), use the following code. Calling setPromise on the activation event args delays the completion of activation until both asynchronous calls (WinJS.UI.processAll and asyncOperation) have completed.
app.onactivated = function (eventArgs) {
if (eventArgs.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
eventArgs.setPromise(WinJS.UI.processAll().then(function() {
return asyncOperation().done(function () { })
}))
}
}​
If you manually register to handle the activated event, the ActivatedOperation.getDeferral method can be used. Below, a deferral object is obtained during activation and complete is called after all async work is complete.
Note: Complete must be called regardless of whether the async call succeeds. If complete isn’t called, the app will never return from activation and app launch will timeout. This is a common pitfall when using this pattern it’s crucial that your app handles all error cases.
Windows.UI.WebUI.WebUIApplication.addEventListener("activated", onActivated, false)
function onActivated(eventArgs) {
if (eventArgs.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
var deferral = eventArgs.activatedOperation.getDeferral()
asyncOperation().done(function () {
deferral.complete()
}, function (error) {
deferral.complete()
})
}
}​
C#

To implement deferral in C#, simply postpone the Window.Current.Activate call until your loading operations have completed. Remember - just like JavaScript - you must only delay this call briefly to complete simple operations. If your app doesn’t successfully activate its window within a reasonable amount of time, launch will timeout. Additionally, lengthy delays will negatively impact the perceived performance of your app, which will not endear you to users.
protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
// Create a frame to act navigation context and navigate to the first page.
var rootFrame = new Frame()
rootFrame.Navigate(typeof(BlankPage))

// Place the frame in the current Window and ensure that it is active.
Window.Current.Content = rootFrame

// Use “await” to execute an async operation prior to activating a window.
// Remember, avoid lengthy operations here.
await asyncOperation()

Window.Current.Activate()
}​
Conclusion

In this post, we discussed a few techniques for producing an elegant launch experience. To differentiate your app’s look and feel, we reviewed four specific design patterns that can be applied to any Metro style app. As you continue to build your apps, remember to prioritize app launch and use the patterns outlined in this post. After all, it’s your first chance to make a memorable impression on users.

View the full article
 
Last edited:
Back
Top