Chapter 7: iPhone to WP7 Application Preference Migration

Date: Tuesday, April 26, 2011, 12:00:00 AM

Tags: iPhone, WP7, Porting, Preferences

Table of Contents

Application Preferences

Preferences are application-specific settings that define the look and feel and behavior of an application. The user can update the preferences at any time to change the behavior of the application.

Both iPhone and Windows Phone provide easy means to update application settings. They both follow a similar philosophy of ease of use and simplicity. However, they follow different ways for presenting and implementing application preferences.

iPhone Application Preferences

On the iPhone, applications have two options for presenting preferences.

  • Display preferences inside the application.
  • Manage preferences from the system wide Settings application.

While the developer can use either mechanism to implement the preferences, the second way to manage application preferences via the Settings application is the preferred one. If the developer expects that the settings will be updated frequently, it may be better to include the preferences within the application. Developers may use both mechanisms.

The Settings application implements a hierarchical set of pages for managing application preferences. The main page shows the system preferences and links to the pages for third party applications. Selecting the application preferences link takes the user to the preferences page for that application. We will look at how to migrate the iPhone application preferences presented using the preferred way, i.e. via the Settings application.

Windows Phone 7 Application Preferences

Windows Phone 7 provides a Settings application for system and system application settings. In contrast to iPhone, third party application settings cannot be placed within this system Settings application. Application settings must be part of the application. This provides consistency and ease of use; users are not confused about where to look for the settings and avoids having to look for settings in both central settings application and the application pages.

Windows Phone 7 UI Design and Interaction Guide recommends that developers keep the application settings brief and clear to improve the usability, and also that they avoid complex multi-level, multi-page application settings. As such, the application should be designed for the most common use. If the application has several user-selectable settings, it should create a separate settings page within the application. This allows the user to update the settings without leaving the application.

Changes to application settings should be applied immediately without confirmation. This means that confirmation dialog such as "OK" is not needed. However, you may inform the user that the settings have been updated. Any settings that that cannot be undone or overwrite or delete data should allow the user to opt out by canceling the change.

Comparison between the two platforms

The following table provides the comparison between the two platforms.

Purpose

iPhone

Windows Phone 7

Application Preferences

System wide settings application In-Application preference pages

In Application preference page

Preferred/Required

System wide settings application

In-app preferences required

Preference UI

Declarative syntax for System wide Settings

Developer implements a page for in-app preferences

Developer implements in-app page for in-app preferences

Storage mechanism

Application specific file store

IsolatedStorage

Preferences

Saved as key-value pairs

Saved as key- value pairs

Migrating Application Preferences

Overview of iPhone Application Preferences

iPhone provides a Settings bundle to manage preferences from the Settings application. The Settings bundle consists of Root.plist and other .plist files as well as files that store localized string resources. The system uses the .plist files to present the UI for the application preference page. The Settings page .plist file consists of specifications for preference parameter types. For the preferences included within the application, the developer needs to implement the view to access and manipulate the preferences.

Regardless of how the application preferences are presented to the user, the application developer uses NSUserDefaults class to access the preferences from the code. The NSUserDefaults class provides a dictionary to store key-value pairs which are persisted in the .plist file in the application-specific file store.

Windows Phone 7 Application Preferences

Application Preference UI

In Windows Phone 7 the developer needs to implement the pages for accessing and manipulating application preferences. The settings pages are no different from any other application page and one can use all available Windows Phone UI widgets to manipulate the preferences.

Let us look at the types of iPhone application settings and how they can be migrated to Windows Phone 7. The following table shows iPhone preference types and the types of controls. The right hand column shows the corresponding Windows Phone 7 controls that can be used to migrate them.

iPhone Preference control

iPhone preference type

Purpose

Corresponding Window Phone Control type

Text Field

PSTextFieldSpecifier

Editable text field for string parameter

TextBox

Title

PSTitleValueSpecifier

Read-only string value

TextBlock

Toggle switch

PSToggleSwitchSpecifier

Preference that can have only two values

Checkbox

Slider

PSSliderSpecifier

Preference that represents range of values

Slider

Multi-value

PSMultiValueSpecifier

Selection of one value from a list

RadioButtons or ListBox

Group

PSGroupSpecifier

Organize collection of preferences together

StackPanel, Grid, or Table

ChildPane

PSChildPaneSpecifier

Navigate to a different page of preferences

Button, link to navigate

Persisting User Preferences

Windows Phone 7 uses isolatedStorageSettings class. IsolatedStorageSettings is implemented using IsolatedStorage which provides complete isolation between applications. IsolatedStorage provides safety as one application cannot access settings of other applications or affect other applications. IsolatedStorageSettings class provides a dictionary to store preferences as key value pairs.

Application preferences presented via the preference page can be written to IsolatedStorage. The following code snippet shows how to write the settings to IsolatedStorage. All application preferences are stored as key-value pairs where the key represents the name of the preference.

// isolated storage settings 
IsolatedStorageSettings isolatedStoreSettings; 

try { 
    // Get the settings for this application. 
    isolatedStoreSettings = IsolatedStorageSettings.ApplicationSettings; 
} catch (Exception e) { 
    Debug.WriteLine("Exception getting IsolatedStorageSettings.ApplicationSettings: " + e.ToString()); 
} 

// Save the application settings 
try { 
    // check if the key exists. If not create it 
    if (!isolatedStoreSettings.Contains(Key)) { 
        isolatedStoreSettings.Add(Key, value); 
    } else { 
        // if new value is different, set the new value. 
        if (isolatedStoreSettings[Key] != value) { 
            isolatedStoreSettings[Key] = value; 
        } 
    } 
} catch (Exception e) { 
    Debug.WriteLine("Exception in IsolatedStoreSettings: " + e.ToString()); 
    throw e; 
} 


On the iPhone, the Settings application handles the persisting of application settings to the application defaults database. The defaults database is provided via the on-device .plist file. However, if your application presents preferences via application pages, you will see that the Windows Phone 7 isolated storage is similar to NSUserDefaults.

// get the handle to application NSUserDefaults 
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; 

// saving a string preference value to a key named "nickname"
[prefs setObject:@"DarthVader" forKey:@"nickname"]; 

// synchronize to persist them to file store
[prefs synchronize]; 

Reading Application Preferences

iPhone uses NSUserDefaults to access application preferences. Here is a typical code snippet to access a preference.

// get the handle to application NSUserDefaults
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];

// getting an NSString preference value
NSString *UserNickname = (NSString *)[prefs objectForKey:@"nickname"];

The corresponding code snippet for retrieving application settings is shown below. As you can see, the iPhone application retrieval code can be migrated easily to Windows Phone 7.

string UserNickname; 

try { 
    UserNickName = (string)isolatedStoreSettings["nickname"]; 
} catch (KeyNotFoundException) { 
    Debug.WriteLine("Nickname not found"); 
} catch (Exception e) { 
    Debug.WriteLine("Exception in reading from IsolatedStorageSettings: " + e.ToString()); 
} 

Purpose

iPhone mechanism

Windows Phone 7 mechanism

Preference store

NSUserDefaults backed by application file store

Persistent application specific store: Isolated Storage

Access persistent store

[NSUserDefaults standardUserDefaults]

IsolatedStorageSettings.ApplicationSettings;

Write Preference

[[NSUserDefaults standardUserDefaults] setObject:value forKey:@"Key"];

isolatedStoreSettings.Add("Key", value);

isolatedStoreSettings["Key"] = value;

Read Preference

// getting a preference object
[prefs objectForKey:@"Key"];

isolatedStoreSettings["Key"];

Migration Sample

Let us look at migrating simple application preferences from iPhone to Windows Phone 7. In our ReadPoetry application, there are only two preferences, namely, font size and night mode. In the normal mode, the application uses black fonts on a white background whereas in the night mode it uses white fonts on a black background.

clip_image002

Create Initial Application

Open Visual Studio 2010 for Windows Phone and create a new application using Windows Phone Application template. Name it ReadingPoetry and click OK.

Open MainPage.xaml using Solution Explorer. Right click on the application title and select view XAML to edit the title to "READING POETRY". Similarly, edit the Page Title to "jabberwocky". Your TitlePanel XAML should look like following:

<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28"> 
    <TextBlock x:Name="ApplicationTitle" Text="READING POETRY" Style="{StaticResource PhoneTextNormalStyle}"/> 
    <TextBlock x:Name="PageTitle" Text="jabberwocky" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/> 
</StackPanel> 

In the ContentPanel Grid, add the following TextBlock with the poem in it (with all those LineBreaks). Your XAML should now look like this:

<!--ContentPanel - place additional content here--> 

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> 

<TextBlock Name="PoetryBlock"> 

`Twas brillig, and the slithy toves<LineBreak/> 

Did gyre and gimble in the wabe:<LineBreak/> 

All mimsy were the borogoves,<LineBreak/> 

And the mome raths outgrabe.<LineBreak/> 

<LineBreak/> 

"Beware the Jabberwock, my son!<LineBreak/> 

The jaws that bite, the claws that catch!<LineBreak/> 

Beware the Jubjub bird, and shun<LineBreak/> 

The frumious Bandersnatch!"<LineBreak/> 

<LineBreak/> 

</TextBlock> 

</Grid> 

You can now run your application with 'F5' to see the application display first two stanzas of Jabberwocky.

clip_image004

Add Application Bar

We will first add an icon for application settings to our application. In Solution Explorer, right click ReadingPoetry project and select Add followed by New Folder and rename it to Images. Using Windows Explorer, find your Windows Phone SDK Icons directory (typically at C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v7.0\Icons) and copy appbar.feature.settings.rest.png to newly created Images folder from the "dark" subdirectory.

clip_image006

Right click on appbar.feature.settings.rest.png in Solution Explorer and select Properties. Change the Build Action to "Content" and Copy to Output Directory to "Copy Always."

clip_image008

Now we will add an Application Bar to our application. Uncomment XAML underneath "<!--Sample code showing usage of ApplicationBar-->" to add the application bar. Delete the Second ApplicationBarIconButton as well as the entire ApplicationBar.MenuItems node. Update the IconUri for the first ApplicationBarIconButton to "/Images/appbar.feature.settings.rest.png".

Your XAML should look like following

<!--Sample code showing usage of ApplicationBar--> 

<phone:PhoneApplicationPage.ApplicationBar> 

<shell:ApplicationBar IsVisible="True" IsMenuEnabled="False"> 

<shell:ApplicationBarIconButton IconUri="/Images/appbar.feature.settings.rest.png" Text="Settings"/> 

</shell:ApplicationBar> 

</phone:PhoneApplicationPage.ApplicationBar> 

Hit F5 to run the application again and now it should show the poem along with the application bar at the bottom as shown below. Of course, if you click on the settings icon, nothing will happens which is expected.In the next section we will create an event handler for it.

clip_image010

Create Settings Page

Stop the application and right click on the ReadingPoetry project in Solution Explorer and click Add followed by New Item and select Windows Phone Portrait Page and change its name to "Settings.xaml". Settings.xaml should open up in Visual Studio.

Follow the above procedure to update the ApplicationTitle TextBlock to "SETTINGS" and PageTitle TextBlock to "reading poetry."

<!--TitlePanel contains the name of the application and page title--> 

<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28"> 

<TextBlock x:Name="ApplicationTitle" Text="SETTINGS" Style="{StaticResource PhoneTextNormalStyle}"/> 

<TextBlock x:Name="PageTitle" Text="reading poetry" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/> 

</StackPanel> 

We will now migrate iPhone application preferences to our Windows Phone 7 application. We will use a check box instead of the toggle switch used for Night Mode. On the other hand, we will use radio buttons instead of multi-value control for font size.

Use TextBlock, CheckBox and RadioButtons to create the user interface for the settings page. The XAML should look like the following:

<!--ContentPanel - place additional content here-->

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

<StackPanel>

<TextBlock Text="Mode" FontSize="28"></TextBlock>

<CheckBox FontSize="24" Margin="30,0,0,0" Name="NightModeCheckbox" Content="Night Mode" />

<TextBlock Text="Font Size" FontSize="28"></TextBlock>

<RadioButton Name="FontSizeSmallBtn" Margin="30,0,0,0" GroupName="FontSizeGroup" FontSize="24" Content="Small"/>

<RadioButton Name="FontSizeMedBtn" Margin="30,0,0,0" GroupName="FontSizeGroup" FontSize="24" Content="Medium"/>

<RadioButton Name="FontSizeLargeBtn" Margin="30,0,0,0" GroupName="FontSizeGroup" FontSize="24" Content="Large"/>

</StackPanel>

</Grid>

Now we will hook up the new page with the settings icon.

Go back to MainPage.xaml and in the ApplicationBarIconButton tag and type space at the end and select Click and type return at which time Visual Studio will offer to create a New Event Handler.

clip_image012clip_image014

Select New Event Handler and it will create ApplicationBarIconButton_click event handler in the code behind. Your XAML for ApplicationBarIconButton should look like this <shell:ApplicationBarIconButton Click="ApplicationBarIconButton_Click" IconUri="/Images/appbar.feature.settings.rest.png" Text="Settings"/>

Right click on XAMLand select View Code which will open MainPage.xaml.cs file which should include a method ApplicationBarIconButton_Click. Add the following code so that your method look like this:

private void ApplicationBarIconButton_Click(object sender, EventArgs e) { 
    this.NavigationService.Navigate(new Uri("/Settings.xaml", UriKind.Relative)); 
} 

Hit F5 again to run the application. If you click on the settings icon, it will open the settings page as shown below. You can click on the settings but nothing will happen which is expected as we do not have code to save or retrieve settings.

clip_image016

Saving and Retrieving Application Settings

Right click on ReadingPoetry project in Solution Explorer and select Add followed by Class. In the add new item dialog, type AppSettings.cs as the name of the class.

Declare IsolatedStorageSettings as shown below and initialize it in the class constructor as shown below.

// isolated storage settings 
IsolatedStorageSettings isolatedStoreSettings; 

// Constructor for the application settings. 
public AppSettings() { 
    try { 
        // Get the settings for this application. 
        isolatedStoreSettings = IsolatedStorageSettings.ApplicationSettings; 
    } catch (Exception e) { 
        Debug.WriteLine("Exception getting IsolatedStorageSettings.ApplicationSettings: " + e.ToString()); 
    } 
} 

Now write three routines to write, read and save the application settings.

// Update the settings. If the setting does not exist, then add the setting. 
public bool AddUpdateSetting(string Key, Object value) { 
    bool updated = false; 

    try { 
        if (!isolatedStoreSettings.Contains(Key)) { 
            isolatedStoreSettings.Add(Key, value); 
            updated = true; 
        } else { 
            // if new value is different, set the new value. 
            if (isolatedStoreSettings[Key] != value) { 
                isolatedStoreSettings[Key] = value; 
                updated = true; 
            } 
        } 
    } catch (Exception e) { 
        Debug.WriteLine("Exception in IsolatedStoreSettings: " + e.ToString()); 
        throw e; 
    } 
    return updated; 
} 

// In the read routine, we will get the value from the isolateStoreSettings dictionary using our key 

// Get the current value of the setting, or if not found, set to the setting. 
public valueType GetSettingValue<valueType>(string Key, valueType val) { 
    try { 
        if (!isolatedStoreSettings.Contains(Key)) { 
            isolatedStoreSettings.Add(Key, val); 
            return val; 
        } else { 
            return (valueType) isolatedStoreSettings[Key]; 
        } 
    } catch (Exception e) { 
        Debug.WriteLine("Exception in IsolatedStoreSettings: " + e.ToString()); 
        throw e; 
    } 
} 


Finally, the save routine will save the settings to the persistent store.

// Save the settings. 
public void Save() { 
    isolatedStoreSettings.Save(); 
} 

With these routines in place, we will write two properties to save and retrieve our NightModeSettings and FontSizeSettings as shown below.

// isolated storage key names of our settings 
const string NightModeSettingKeyName = "NightModeSetting"; 
const string FontSizeSettingKeyName = "FontSizeSetting"; 

// default values for our settings 
const bool NightModeSettingDefault = false; 
const string FontSizeSettingDefault = "medium"; 

// Property to get and set a NightMode Setting. 
public bool NightModeSetting { 
    get { 
        return GetSettingValue<bool>(NightModeSettingKeyName, NightModeSettingDefault); 
    } 

    set { 
        AddUpdateSetting(NightModeSettingKeyName, value); 
        Save(); 
    } 
} 

// Property to get and set a RadioButton Setting Key. 
public string FontSizeSetting { 
    get { 
        return (GetSettingValue<string>(FontSizeSettingKeyName, FontSizeSettingDefault)); 
    } 

    set { 
        AddUpdateSetting(FontSizeSettingKeyName, value); 
        Save(); 
    } 
} 


With this, we have the plumbing for retrieving and persisting preferences.

Saving the Settings

Open Settings.xaml.cs file using Solution Explorer and edit the Settings class constructor to create appSettings instance.

AppSettings appSettings; 

public Settings() { 
    InitializeComponent(); 

    // create AppSettings instance 
    appSettings = new AppSettings(); 
} 

When the user navigates away from this page, by clicking back button, OnNavigatedFrom event is fired. In this event handler, we will save the user selections to our application settings.

protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e) { 
    //save settings based on selections in the UI 
    appSettings.NightModeSetting = ((bool)NightModeCheckbox.IsChecked); 

    if ((bool) FontSizeSmallBtn.IsChecked) appSettings.FontSizeSetting = "small"; 

    if ((bool)FontSizeMedBtn.IsChecked) appSettings.FontSizeSetting = "medium"; 

    if ((bool)FontSizeLargeBtn.IsChecked) appSettings.FontSizeSetting = "large"; 

    base.OnNavigatedFrom(e); 
} 


Similarly when the user navigates to this page, by clicking settings icon, OnNavigatedTo event is fired. In this event handler, we will initialize the UI controls based on the application settings.

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) { 
    //initialize controls in the UI based on oersisted settings 
    NightModeCheckbox.IsChecked = appSettings.NightModeSetting; 

    if (appSettings.FontSizeSetting == "small") 
        FontSizeSmallBtn.IsChecked = true; 

    if (appSettings.FontSizeSetting == "medium") 
        FontSizeMedBtn.IsChecked = true; 

    if (appSettings.FontSizeSetting == "large") 
        FontSizeLargeBtn.IsChecked = true; 
} 

Using Application Preferences

Whenever the application main page is loaded, we will use the settings to update the font size and colors of the application.

Open MainPage.xaml and associate a new event handler for page load to the main PhoneApplicationPage. Type space in the Phone:PhoneApplicationPage node and select Loaded. Select <New Event Handler> which will added the following XAML at the end of Phone:PhoneApplicationPage node and also create a placeholder method in the MainPage class.

Loaded="PhoneApplicationPage_Loaded"

Just like before, declare AppSettings instance in the MainPage class and instantiate it in the class constructor as shown below.

AppSettings appSettings; 

// Constructor 
public MainPage() { 
    InitializeComponent(); 
    appSettings = new AppSettings(); 
}

Write the following utility methods and properties in the class. The first method returns a SolidColorBrush from hex values. The other two properties create and return dark and light brushes that will be used to paint the background and foreground.

public static SolidColorBrush GetColorFromHexa(string hexaColor) { 
    return new SolidColorBrush( 
        Color.FromArgb( 
            Convert.ToByte(hexaColor.Substring(1, 2), 16), 
            Convert.ToByte(hexaColor.Substring(3, 2), 16), 
            Convert.ToByte(hexaColor.Substring(5, 2), 16), 
            Convert.ToByte(hexaColor.Substring(7, 2), 16) 
        ) 
    ); 
} 

SolidColorBrush DarkBrush { 
    get { 
        return GetColorFromHexa("#CCDD1167"); 
    } 
} 

SolidColorBrush LightBrush { 
    get { 
        return GetColorFromHexa("#FF00FFFF"); 
    } 
} 


The following routine returns the size of font based on the FontSize application setting.

double AppFontSize { 
    get { 
        switch (appSettings.FontSizeSetting) { 
        case "small": 
            return 20; 
        case "medium": 
            return 24; 
        case "large": 
            return 28; 
        default: 
            return 24; 
        } 
    } 
} 

Finally, we will update the font size and the application colors based on the settings. This event handler gets called when the page is loaded which happens when the application is loaded the first time or when we navigate from the settings page.

private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e) { 
    //Update the fontsize of the poetry textblock. 
    PoetryBlock.FontSize = AppFontSize; 
    SolidColorBrush bgBrush; 
    SolidColorBrush fgBrush; 

    if (appSettings.NightModeSetting) { 
        // use dark brush for the background and light brush for foreground for night mode 
        bgBrush = DarkBrush; 
        fgBrush = LightBrush; 
    } else { 
        // use light brush for the background and dark brush for foreground for day mode 
        bgBrush = LightBrush; 
        fgBrush = DarkBrush; 
    } 

    //now paint the entire app layout with background 
    LayoutRoot.Background = bgBrush; 

    //paint app title, page title and the poetry texblock text with foreground color 
    this.Foreground = fgBrush; 
    ApplicationTitle.Foreground = fgBrush; 
    PageTitle.Foreground = fgBrush; 
} 


Now hit F5 to see the application react to application settings. Here is the behavior with small fonts and day mode.

clip_image018clip_image020

On the other hand, if you select the night mode and large fonts, it should look like this.

clip_image022clip_image024

Conclusions

In contrast to iPhone, Windows Phone 7 requires that the application settings be managed within the application itself. This provides a consistent experience across all third party applications. It also provides an ease of use as the user can change preferences without leaving the application.

While the two platforms differ in their presentation, both platforms use similar mechanism for managing the preferences. They use dictionaries to save preferences as key-value pairs. While iPhone uses application specific file store for persistence, Windows Phone 7 uses IsolatedStorage class to persist preferences in protected storage.

Windows Phone 7 platform provides necessary widgets that closely correspond to widgets used for iPhone application preferences. It is possible to migrate the application preferences from iPhone to Windows Phone 7.

 
blog comments powered by Disqus