Porting App Life Cycle, Settings, and Search Features from iOS* to Windows* 8

Objective

Most mobile apps, regardless of the platform, have features like search, app settings, complex app life cycle, and state maintenance in them. In this article we will cover how to port these features from existing iOS apps, to the Windows* 8 platform. We will compare and contrast how search is implemented in iOS apps and Windows 8 apps. We will look at different states an app goes through and how to persist or recover state during these app life cycle states. Finally, we will discuss how app settings are implemented and integrated into the platform. Search and Settings contracts in Windows 8 will be discussed.

Table of Contents

  1. Introduction
  2. App Life Cycle in iOS and Windows 8 Store Apps
  3. Porting App Settings from iOS to Windows 8 settings contract
  4. Integrating App Search functionality -  iOS vs. Windows 8 Store Apps
  5. Summary

Introduction

Applications need to support robust user experiences that include state and session maintenance, search, and settings.

Mobile apps have complex run time and life cycle states. Modern OS platforms, like iOS, enforce their own app life cycle that each app has to conform to. iOS developers are familiar with different states an app goes through in its life:

http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html

Windows 8 Store apps also have well-defined life cycle states. In this article we will discuss how to handle these state transitions to enable seamless user experience.

Apps that depend and manipulate large amounts of data, typically also have a way for users to search quickly for information or relevant data in the app. For example, the iOS mobile platform has support for UI controls like UISearchBar to provide a common look and feel for search. Most apps tend to use this common search experience on iOS platforms.

Developers looking to port their iOS app to Windows 8 will be glad to know Windows 8 has a well-integrated search feature at the platform level. In Windows 8, users have access to a common system-wide search interface that apps can plug-into, thereby providing a seamless experience.

Windows 8 provides this interface as part of its system-wide UI control called a charms bar. Please see the screen shot below.

***Figure 1: Charms bar (captured from a Windows* Store app)

The charms bar can be invoked by users anytime using a special gesture (touch or keyboard CTRL-C). Along with Search, it has other interfaces like settings, devices, start button, and share. In this article we will discuss how to implement search and settings in your mobile app and compare to iOS equivalents.

App Life Cycle in iOS and Windows 8 Store Apps

Apps on the  iOS platform are in one of these states: not running, inactive, active, background, or suspended. Which state and how long the app will be in a particular state are unpredictable. iOS apps depend on UIKit to handle states and to provide a seamless user experience for the app.

iOS developers are familiar with how UIKit can  help in state preservation and restoration of apps:

http://developer.apple.com/library/ios/#DOCUMENTATION/iPhone/Conceptual/iPhoneOSProgrammingGuide/StatePreservation/StatePreservation.html#//apple_ref/doc/uid/TP40007072-CH11-SW13

When porting an existing iOS app to Windows 8, developers can conceptually map the UI and its view states to corresponding UI screens in Windows Store apps. Similar kinds of state can be saved and restored for the apps.

Like the iOS platform, the Windows 8 platform also has its own app life cycle states. Similar high level concepts of saving state before the app goes into the suspend (and eventual termination or “not running”) state apply. This enables us to provide a good user experience for the app.

This article provides the Windows Store apps life cycle overview:

http://msdn.microsoft.com/en-us/library/windows/apps/hh464925.aspx

In addition to a typical app launch (e.g., from the home screen), a Windows Store app can be started via contracts (e.g., charms bar) or extensions.

The UI for Windows Store apps using C# is built using XAML. Please refer to articles on porting iOS UI to Windows Store apps for details on XAML and UI methodologies. The default C#/XAML project templates in Visual Studio* 2012 make it very easy to implement the app life cycle transitions. They include two base classes: LayoutAwarePage (from which all “pages,” or views in iOS, inherit from), and SuspensionManager (similar to iOS UIKit type functionality to persist state). The LayoutAwarePage base class depends on, and uses, the SuspensionManager class, so all our views automatically inherit this functionality.

Whenever your app is switched or sent to the background (when another app is invoked), the LayoutAwarePage will automatically handle the state transitions. It will call appropriate methods in the page to give an opportunity to persist state.

Let’s see some code. Typically, when you want to save/restore states in an XAML page (UI view), you modify or implement the “SaveState” function (which is a method of LayoutAwarePage), and its corresponding “LoadState” where you restore the state. By default, the LayoutAwarePage automatically saves the navigation stack. We only need to take care of the page-specific state. The sample code listing** below shows a Login UI page, where we save and restore the login username in case the app gets suspended (and may get terminated).

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
 
using PRApp.ViewModels;
 
// The Basic Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234237
 
namespace PRApp
{
    /// <summary>
    /// A basic page that provides characteristics common to most applications.
    /// </summary>
    public sealed partial class Login : PRApp.Common.LayoutAwarePage
    {
        public Login()
        {
            this.InitializeComponent();
        }
 
        /// <summary>
        /// Populates the page with content passed during navigation.  Any saved state is also
        /// provided when recreating a page from a prior session.
        /// </summary>
        /// <param name="navigationParameter">The parameter value passed to
        /// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested.
        /// </param>
        /// <param name="pageState">A dictionary of state preserved by this page during an earlier
        /// session.  This will be null the first time a page is visited.</param>
        protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
        {
            var uvm = UsersViewModel.GetDefaultUserViewModel();
            if (pageState != null && pageState.ContainsKey("username"))
                uvm.Login = (String)pageState["username"];
           
            this.DataContext = uvm;
        }
 
        /// <summary>
        /// Preserves state associated with this page in case the application is suspended or the
        /// page is discarded from the navigation cache.  Values must conform to the serialization
        /// requirements of <see cref="SuspensionManager.SessionState"/>.
        /// </summary>
        /// <param name="pageState">An empty dictionary to be populated with serializable state.</param>
        protected override void SaveState(Dictionary<String, Object> pageState)
        {
            pageState["username"] = usernameInput.Text;
        }
 
        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            var uvm = (UsersViewModel)this.DataContext;
            uvm.LogOut();
            if (uvm.LogIn()) this.Frame.Navigate(typeof(Patients));
        }
    }
}
 

In the SaveState method, we simply save the “Text” value of our “usernameInput” XAML text box UI control. We use the “pageState” dictionary that was passed into the method. Anything you put in this dictionary will automatically persist on suspension. It’s pretty simple and straightforward.

Please note, your app may have two kinds of data to persist:  app-specific data (often associated with data model or app settings) and UI session or state (a temporary, session, or UI-related state that can be discarded or purged with no impact on the app’s functionality).  It’s recommended we use pageState dictionary to persist the latter kind.

To persist the former kind, you will want to use a different mechanism. Windows Store apps can store their app-specific data on different kinds of data stores: local, roaming, and temporary stores.  If you would like your app data to sync between multiple devices where the app was installed, you would use roaming. If not, use local store. The temporary store is just that, temporary and can be removed any time. More detailed information can be found here:

http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh464917.aspx

Below, we will look at an example of how to use local settings data store. A similar technique can be applied to other data stores like the roaming settings.  LocalSettings is available in Windows.Storage namespace. It exposes a key-value store that we can use to save/restore objects. In our sample app, we want to persist user settings for displaying patients. We make this available in the settings flyout (discussed later in this article). To save this user setting across reboots, we can use LocalSettings data store. Please see the code listing** below for an example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PRApp.Common;
using PRApp.DataModel;
using PRApp.ViewModels;
using System.Collections.ObjectModel;
using Windows.Storage;
 
namespace PRApp.ViewModels
{
    class SessionSettingsViewModel : BindableBase
    {
        ApplicationDataContainer locsettings = null;
        public SessionSettingsViewModel()
        {
            locsettings = ApplicationData.Current.LocalSettings;
            if (!locsettings.Values.ContainsKey("showdeceased"))
            {
                locsettings.Values["showdeceased"] = false;
            }
 
            showdeceased = (bool)locsettings.Values["showdeceased"];
 
        }
        private UsersViewModel loginuser = null;
        public UsersViewModel Loginuser { get { return loginuser; } set { this.SetProperty(ref loginuser, value); } }
 
        private static SessionSettingsViewModel sessionsettings = null;
        public static SessionSettingsViewModel SessionSettings
        {
            get
            {
                if (sessionsettings == null) { sessionsettings = new SessionSettingsViewModel(); sessionsettings.Loginuser = UsersViewModel.GetDefaultUserViewModel(); }
                return sessionsettings;
            }
        }
 
        private bool showdeceased = false;
        public bool ShowDeceased { get { return showdeceased; } set { locsettings.Values["showdeceased"] = value; this.SetProperty(ref showdeceased, value); } }
    }   
}

In the above code snippet, we are persisting “showdeceased” user setting to the LocalSettings data store. Since we expose this variable as a bindable property (inherited from BindableBase), we can directly bind this variable to our UI controls. Please refer to the next section on how to bind this value to the app settings UI flyout. We can also persist new containers or composite objects. For more detailed information please refer to the link below:

http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh700361.aspx

In this section we looked at how iOS app life cycle states, state preservation, and restoration translate to Windows Store apps. We looked at a couple of examples on how to save persist View state and app-specific data.

Porting App Settings from iOS to Windows 8 settings contract

In iOS, we use the user defaults system or the new iCloud key-value store to implement our app settings. The iOS platform provides a way to expose app settings under system settings/preferences, thereby providing a seamless and consistent settings experience across all apps. iOS developers are also free to implement their own in-app settings screen for frequently used options (e.g., volume level).

Developers porting their iOS apps to Windows Store apps, will find the settings framework in Windows 8 is comprehensive. It even includes consistent UI guidelines, with size, color and general look-and-feel specifications. As we discussed in the introduction, the system-wide settings interface is exposed via a charms bar. This interface is available in any app and at system level too.

The screen shot below shows the charms bar in our sample app.

***Figure 2: Sample Patient Records App with Charms Bar (captured from simulator)

When the settings button is pressed, it brings up the standard settings flyout with default options (typically just “Permissions” by default). The app can choose to implement additional settings options in the settings flyout. Each option, in turn can open another flyout or may even direct to custom settings screen. The screen shot below shows our sample app with our own “PRApp Options” command added to the settings flyout.

***Figure 3: Default Settings flyout (captured from simulator)

Below are comprehensive guidelines for app settings in Windows Store apps :

http://msdn.microsoft.com/en-us/library/windows/apps/hh770544.aspx

To implement all of these guidelines from the ground up takes lot of effort. Unfortunately, XAML/C# has no standard or default Settings Flyout control (though, there is one available in HTML5/JavaScript*/WinJS framework).

C# has some 3rd party libraries that we can use to get most of the functionality and comply with guidelines. If you would like to implement your own custom app settings flyout from scratch, please refer to the article below:

http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh872190.aspx

We can either programmatically add our settings control, or declare an XAML page that inherits it from UserControl and has all our custom UI pieces in there. Below** is an example of our sample settings flyout:

<UserControl
    x:Class="PRApp.PRSettingsControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:PRApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400"
    xmlns:c="using:PRApp.ViewModels">
    <UserControl.Resources>
        <c:SessionSettingsViewModel x:Key="myDataSource"/>
    </UserControl.Resources>
    <Grid>
        <StackPanel>
            <StackPanel Orientation="Horizontal"  Margin="5" >
                <TextBlock Text="{Binding Source={StaticResource myDataSource}, Path=SessionSettings.Loginuser.Login}" Margin="0,0,5,0" />
                <TextBlock Text="{Binding Source={StaticResource myDataSource}, Path=SessionSettings.Loginuser.Loginmsg}" />
            </StackPanel>
            <Button Content="Logout" Margin="5" Click="Button_Click_1" />
            <ToggleSwitch Header="Show Deceased Patients" Margin="5" IsOn="{Binding Mode=TwoWay, Source={StaticResource myDataSource}, Path=SessionSettings.ShowDeceased}"/>
        </StackPanel>
    </Grid>
</UserControl>

The code behind is pretty straightforward, we just implement the button click method for “Logout.” The “Show Deceased Patients” ToggleSwitch binds to the SessionSettingsViewModel directly.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using PRApp.ViewModels;
 
// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236
 
namespace PRApp
{
    public sealed partial class PRSettingsControl : UserControl
    {
        public PRSettingsControl()
        {
            this.InitializeComponent();
        }
 
        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            SessionSettingsViewModel.SessionSettings.Loginuser.LogOut();
 
            var frame = Window.Current.Content as Frame;
            frame.Navigate(typeof(Login));
        }
    }
}

Settings UI guidelines require us to provide a consistent user experience. In XAML/C# we can embed the above** UI view (usercontrol) into a popup XAML control that can emulate the flyout and light-dismiss behavior.

Integrating App Search functionality - iOS vs. Windows Store Apps

Search has become an integral part of modern mobile apps, specifically for apps that have lots of data. Apps on the iOS platform implement search using the UISearchBar class, which provides a standard search experience for iOS users.

***Figure 4: UISearchBar in iOS* (captured from iOS iPad* emulator)

As we discussed previously, the search interface in Windows Store apps is part of the charms bar. Apps can register themselves to react to search queries at the system level, not just when the app is in the foreground or active. So apps with search contract implemented could be invoked (“activated”) directly from the search interface.

A detailed step-by-step search example is available in the Windows Store apps developer documentation:

http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh868180.aspx

The screen shot of our sample app below shows the Windows Store app search interface, which was invoked via the charms bar.

***Figure 5: Different components of a Search (captured from simulator)

To show the search results, a separate XAML page can be used and customized as needed. Developers can choose to implement user convenient features like search filters or suggestions, automatic searches invoked when users start typing (mainly useful on devices with a keyboard interface), and if needed, enable incremental search too.

Before we delve into implementing the search interface in our sample app, we first need to declare the app supports “Search” in the App manifest.

***Figure 6: App Manifest showing search declaration

If you use the Visual Studio 2012 search contract template, it will automatically add this to app manifest. Detailed information on the search contract template can be found here:

http://msdn.microsoft.com/EN-US/library/windows/apps/jj655408(v=vs.10).aspx

***Figure 7: Visual Studio* 2012 search contract template

In addition, the search contract template automatically modifies the main application object (App.xaml.cs) to enable search. It will add code stubs for “OnSearchActivated” method, which is a kind of trigger point for your app to respond to search activation of the app. The App.xaml.cs code** snippets below show the search-related code in the main application object :

/// Invoked when the application is activated to display search results.
        /// </summary>
        /// <param name="args">Details about the activation request.</param>
        protected async override void OnSearchActivated(Windows.ApplicationModel.Activation.SearchActivatedEventArgs args)
        {
            await InitMainPage(args);
            ShowSearch(args.QueryText);
        }       
 
        private void ShowSearch(String query)
        {
            var frame = Window.Current.Content as Frame;
 
            var loggedinuser = SessionSettingsViewModel.SessionSettings.Loginuser;
            if (loggedinuser == null || !loggedinuser.LoginState)
            {
                frame.Navigate(typeof(Login));
            }
            else if (query != "")
                frame.Navigate(typeof(SearchResults), query);
            else
                frame.Navigate(typeof(Patients));
        }
 
        private void OnQuerySubmitted(object sender, SearchPaneQuerySubmittedEventArgs args)
        {
            ShowSearch(args.QueryText);
        }
 
        private void OnQueryChanged(object sender, SearchPaneQueryChangedEventArgs args)
        {
            ShowSearch(args.QueryText);
        }
 
        protected override void OnWindowCreated(WindowCreatedEventArgs args)
        {
            // Register QuerySubmitted handler for the window at window creation time and only registered once
            // so that the app can receive user queries at any time.
            SearchPane.GetForCurrentView().QuerySubmitted += new TypedEventHandler<SearchPane, SearchPaneQuerySubmittedEventArgs>(OnQuerySubmitted);
 
            SearchPane.GetForCurrentView().QueryChanged += new TypedEventHandler<SearchPane, SearchPaneQueryChangedEventArgs>(OnQueryChanged);
           
            AppSettings.Current.ContentBackgroundBrush = new SolidColorBrush(Windows.UI.Colors.Green);
            AppSettings.Current.AddCommand<PRSettingsControl>("PRApp Options", SettingsFlyout.SettingsFlyoutWidth.Narrow);
 
        }

As seen above, it is recommended we handle all possible search scenarios including a blank search query. If you have login or session state(s), you might want to put in additional checks (e.g., require them to login) before you let users search an app’s data.

Windows Store apps recommended checklist and guidelines for search can be found here:

http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh465233.aspx

In our sample app, we used the Visual Studio 2012 to search contract templates. It automatically adds an XAML page for displaying the results in a grid view (or list view), which is very similar to other page templates. It also adds code behind with support for search filters. We just need to plug in our app data into these template hooks. Please see the code snippet below**, which shows the modifications done in our sample app:

protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
        {
            var queryText = navigationParameter as String;
 
            Results = new Dictionary<string, IEnumerable<PatientsViewModel>>();
            var filterList = new List<Filter>();
 
            var ps = PatientsViewModel.GetPatientsCached();
            if (ps != null)
            {
                Results["Lastname"] = ps.Where(p => p.Lastname.Contains(queryText.ToUpper()));
                filterList.Add(new Filter("Lastname", Results["Lastname"].Count()));
                Results["Firstname"] = ps.Where(p => p.Firstname.Contains(queryText.ToUpper()));
                filterList.Add(new Filter("Firstname", Results["Firstname"].Count()));
                Results["All"] = Results["Firstname"].Union(Results["Lastname"]);
                filterList.Insert(0, new Filter("All", Results["All"].Count(), true));
            }
            // Communicate results through the view model
            this.DefaultViewModel["QueryText"] = 'u201c' + queryText + 'u201d';
            this.DefaultViewModel["Filters"] = filterList;
            this.DefaultViewModel["ShowFilters"] = filterList.Count > 1;
        }
 
        /// <summary>
        /// Invoked when a filter is selected using the ComboBox in snapped view state.
        /// </summary>
        /// <param name="sender">The ComboBox instance.</param>
        /// <param name="e">Event data describing how the selected filter was changed.</param>
        void Filter_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // Determine what filter was selected
            var selectedFilter = e.AddedItems.FirstOrDefault() as Filter;
            if (selectedFilter != null)
            {
                // Mirror the results into the corresponding Filter object to allow the
                // RadioButton representation used when not snapped to reflect the change
                selectedFilter.Active = true;
 
                if (Results.ContainsKey(selectedFilter.Name))
                    this.DefaultViewModel["Results"] =
                         new List<PatientsViewModel>(Results[selectedFilter.Name]);
 
                // Ensure results are found
……
 
  

We can also enable automatic search invocation when a user starts typing, using the “ShowOnKeyboardInput” property of the search pane. In the below example, we enable this feature only on the main patients screen (which could potentially have thousands of entries). So when the user is on this page and starts typing a patient’s name, it will automatically bring up the search interface.

protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
        {
            // check login status
            var loggedinuser = SessionSettingsViewModel.SessionSettings.Loginuser;
            if (loggedinuser == null || !loggedinuser.LoginState)
            {
                var frame = Window.Current.Content as Frame;
                frame.Navigate(typeof(Login));
                return;
            }
 
            this.loginUser.Text = loggedinuser.Login + " (" + loggedinuser.Fullname + ")";
            this.DefaultViewModel["Patients"] = PatientsViewModel.GetPatients();
            // enable type to search.
            SearchPane.GetForCurrentView().ShowOnKeyboardInput = true;
        }
 
        protected override void SaveState(Dictionary<String, Object> pageState)
        {
            // disable type to search.
            SearchPane.GetForCurrentView().ShowOnKeyboardInput = false;
        }

This code** snippet is in the code behind file for the patients XAML page. Similarly, we can enable or disable this feature on a per-page basis depending on user requirements.

Summary

Lines-of-business apps tend to have search, settings, and complex app state maintenance in them. In this article we reviewed how iOS apps implement these features and compared and contrasted them to Windows Store apps. Using a sample app and source code, we discussed how to implement these features on Windows 8.

Notices

*Other names and brands may be claimed as the property of others

**This sample source code is released under the Intel OBL Sample Source Code License (MS-LPL Compatible) , Microsoft Limited Public License and Visual Studio 2012 License. Copyright© 2012 Intel Corporation. All rights reserved.

***This sample figure is released under the Intel OBL Sample Source Code License (MS-LPL Compatible) and Xcode License. Copyright© 2012 Intel Corporation. All rights reserved.

Nähere Informationen zur Compiler-Optimierung finden Sie in unserem Optimierungshinweis.