• Hulu
  • TV
  • Movies
  • More TV. On more devices.
Search
Hulu Tech Blog

Tips and Highlights from Developing Mobile apps for Windows

July 15th, 2013 by Zachary Pinter

For the past year or so, our mobile team has been hard at work on creating native apps for the Microsoft ecosystem.

Now that we’ve launched both Hulu Plus for Windows Phone 8 and Hulu Plus for Windows 8, I’d like to take some time reflect on their development by showing off some of the platform highlights.

Async/Await

The first and most obvious highlight is the async and await keywords added to C# 5. This pattern has been enormously helpful in cutting down code complexity and keeping our UI fast and responsive.

Typically, when writing code that makes use of asynchronous libraries (such as node.js), you end up structuring your project’s code around the handling of callbacks. With async/await, you get to write code that takes full advantage of asynchronous, non-blocking, off-the-ui-thread functions, while organizing your code much the same way as you might have done using synchronous calls.

There’s a lot of depth and detail about how this is done under the hood (see here), but I think it’s best to start with few quick examples:

public async Task<string> Nonce()
{
   var res = await ExecuteSiteRequest(new NonceRequest());
   return res.Value;
}

public async Task<SiteUser> Login(string username, string password)
{
    var req = new AuthenticateRequest(username, password, await Nonce());
    var res = await ExecuteSiteRequest(req);
    var user = new SiteUser(res);

    return user;
}

In the above example, the actual delay involved in waiting for the HTTP calls triggered by ExecuteSiteRequest happen outside the UI thread. However, the rest of the logic runs on the UI thread. The intelligence of the async/await keywords comes from the compiler knowing how to rewrite the methods and choosing when to run the various fragments of code that surround the calls to ExecuteSiteRequest.

Notice how the await keyword is placed inside the constructor of the AuthenticateRequest object. This means that before the first AuthenticateRequest object ever gets constructed, the framework will first wait for the successful execution of the async Nonce() method. After the Nonce() method’s call to ExecuteSiteRequest returns successfully, the code in the Login method resumes where it left off (now on the UI thread) and is able to construct the AuthenticateRequest.

If any of the calls to ExecuteSiteRequest were to fail (e.g. a server-side error), an exeception would bubble up on the UI thread just as if this were a blocking method (even if the original source of the exception came from a worker thread executing the HTTP request). We added a few keywords, but the organization of the code remains simple and straightforward.

Keep in mind that the value provided by these keywords is not limited to IO calls. The metaphor extends to anywhere you might want to break away from the current thread. For example, a common issue that frequently comes up in UI development is stuttering/freezing and how to prevent it. The obvious first step is to move all your service calls off the UI thread, though you can still end up with a stuttering app if you take up too much CPU time on the UI thread. For example, you might execute your HTTP call off the UI thread, but then process the result on the UI thread. In C# 5.0, you can pass a lambda to Task.Run (which then executes that lambda on a worker thread pool) and await the result.

Here’s an example of how we use Task.Run to process JSON:

// Example:
//  var user = await JsonToObject<User>(userJson);
public async Task<T> JsonToObject<T>(string json)
{
    var res = await Task.Run<T>(() => {
        return JsonConvert.DeserializeObject<T>(json,
            new JsonSerializerSettings
            {
                MissingMemberHandling = 
                 Newtonsoft.Json.MissingMemberHandling.Ignore
            });
    });
    return res;
}

The same sort of pattern can be applied to any snippet of code or function call that you might expect to be CPU intensive. Assuming the snippet doesn’t try to manipulate shared state, just add in the async/await keywords and wrap the snippet in a call to Task.Run.

Static Extension Methods

Extension methods have been in C# for quite some time (since 3.0), but I’ve found several handy use cases for them during the development of our Windows 8 and Windows Phone 8 applications.

One of them I’d like to highlight is type conversion helper methods. Like many mobile apps, we work with and parse data from a variety of different backend services and libraries. A lot of times, this means dealing converting to and from typed and primitive values (e.g. string, double, bool, int, object, Double). So, we created extension methods to make the process easier:

public static int AsInt(this object me, int defaultValue = default(int))
{
    if (me == null) return defaultValue;
    if (me is string)
    {
        int result;
        if (int.TryParse(me as string, out result))
        {
            return result;
        }
    }

    if (IsNum(me))
    {
        return Convert.ToInt32(me);
    }

    return defaultValue;
}

public static bool AsBool(this object me, bool defaultValue = default(bool))
{
    if (me == null) return defaultValue;

    if (me is string)
    {
        var meStr = (me as string);

        if (("true".Equals(meStr, StringComparison.OrdinalIgnoreCase)) ||
            ("1".Equals(meStr, StringComparison.OrdinalIgnoreCase))
        )
        {
            return true;
        }
        if (
            ("false".Equals(meStr, StringComparison.OrdinalIgnoreCase)) ||
            ("0".Equals(meStr, StringComparison.OrdinalIgnoreCase))
            )
        {
            return false;
        }
    }

    if (me is bool)
    {
        return (bool)me;
    }
    return defaultValue;
}

Granted, in many cases you can just type cast and be done with it (e.g. var x = (int)foo). However, doing so naively can run into issues later on if the server-side data changes format (e.g. values that are largely whole numbers, but can have decimal values under some scenarios).

Some areas where these extension methods are helpful:

  • Saved settings from IsolatedStorageSettings

  • Parsing page paremeters in NavigationContext.QueryString

  • Json objects represented as IDictionary

Another benefit of using static extensions is that they can be chained and still behave correctly when null values are returned earlier in the chain.

For example:

protected virtual void LoadState(System.Windows.Navigation.NavigationEventArgs e, 
                                 IDictionary<String, Object> pageState)
{
    // omitted code...

    bool resetHistory = 
       this.NavigationContext.QueryString.ValueOrDefault("reset_history").AsBool();

    // omitted code...

    if (resetHistory)
    {
        while (NavigationService.RemoveBackEntry() != null)
        {
            ; // do nothing
        }
    }
}

In the above example, ValueOrDefault can return null (the default value for a string) if the “reset_history” string isn’t present in the QueryString dictionary. However, that’s no problem for our AsBool() helper, since it returns the default boolean value (false) if called on a null object.

The ability to consolidate all this type conversion logic into easy-to-call, composable extension methods makes our code both cleaner and safer.

For the full set of type conversion helpers we use, see ObjectHelper.cs.

XAML Data Binding and MVVM

A fairly common idea in UI development is that your views should be decoupled from your models in a way that allows for, at least in theory, alternative views to share the same model.

One of the best ways to achieve this on Microsoft platforms is through XAML and the MVVM pattern. XAML (eXtensible Application Markup Language) is an XML format for specifying UI layouts and templates. MVVM (Model-View-ViewModel) is a design pattern in the same genre as MVC with a focus on data binding.

Let’s see how this works in practice:

<Grid x:Name="detailsContainer" Grid.Row="1" Background="White" 
  VerticalAlignment="Top" Grid.ColumnSpan="2">
    <Grid Margin="15">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <b:BehaviorCollection.Behaviors>
            <b:EventBehavior Event="Tap" Action="{Binding TileTextClickedAction}"/>
        </b:BehaviorCollection.Behaviors>

        <TextBlock
            Margin="0,15,0,8"
            Style="{StaticResource MastheadShowTitleTextStyle}"
            Text="{Binding Title}" 
            TextWrapping="NoWrap"
            TextTrimming="WordEllipsis"
            Visibility="{Binding Title, 
                               Converter={StaticResource HuluConverter}, 
                               ConverterParameter='NotBlankToVisible'}"
            />

        <TextBlock 
            Grid.Row="1"
            Margin="0,0,0,4"
            Style="{StaticResource MastheadVideoTitleTextStyle}"
            Text="{Binding Subtitle}"
            TextWrapping="NoWrap"
            TextTrimming="WordEllipsis"
            Visibility="{Binding Subtitle, 
                               Converter={StaticResource HuluConverter}, 
                               ConverterParameter='NotBlankToVisible'}"
            />

        <TextBlock 
            Grid.Row="2"
            Style="{StaticResource MastheadDescriptionTextStyle}"
            TextWrapping="Wrap"
            TextTrimming="WordEllipsis"
            Text="{Binding Description}" MaxHeight="80"
            Visibility="{Binding Description, 
                               Converter={StaticResource HuluConverter}, 
                               ConverterParameter='NotBlankToVisible'}"
            />
    </Grid>
</Grid>

In the above example we have three text blocks where any given text block can be hidden/collapsed if the text it’s bound to is blank (null or an empty string). Any time the title, subtitle, or description changes, the UI will automatically be updated (and each text block will be made visible or collapsed as needed). Behind the scenes, there’s a view model object (a fairly simple/typical C# object) driving this view. However, the view model has no specific knowledge about what this view looks like or how it is arranged (nor does it need to). Instead, the view model’s chief concern is in providing a useful set of bindable properties (getters/setters) that any particular layout might want.

XAML and MVVM ends up being a really nice workflow/pattern when it comes to iterating designs and reusing code across different templates (e.g. templates for Windows 8 snapped mode versus full screen mode). With a bit of legwork, this reusability extends even across applications. For example, the bulk of our Windows Phone 8 templates are bound to the same view models we created for the Windows 8 app.

If you’re interested in learning more about the MVVM pattern and how it helped drive the development of our Windows 8 and Windows Phone 8 apps, check out this video from Build 2013.

XAML Control Template Overrides

Another perk of working with XAML-based frameworks is that you can extract and override the standard XAML templates used by the built-in controls.

For example, if you want a Windows Phone 8 LongListSelector to have some extra padding at the bottom of the list (but inside the scrollable region), just override the template and add the padding to the inner ViewportControl:

<Style x:Key="LongListSelectorWithBottomPadding" 
  TargetType="fcontrols:HLongListSelector">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="fcontrols:HLongListSelector">
                <Grid Background="{TemplateBinding Background}">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="ScrollStates">
                            <VisualStateGroup.Transitions>
                                <VisualTransition GeneratedDuration="00:00:00.5"/>
                            </VisualStateGroup.Transitions>
                            <VisualState x:Name="Scrolling">
                                <Storyboard>
                                    <DoubleAnimation Duration="0" To="1" 
                                      Storyboard.TargetProperty="Opacity" 
                                      Storyboard.TargetName="VerticalScrollBar"/>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="NotScrolling"/>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <Grid Margin="{TemplateBinding Padding}">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="auto"/>
                        </Grid.ColumnDefinitions>
                      <!-- Note the bottom padding being set to 80 -->
                        <ViewportControl x:Name="ViewportControl" 
                          Padding="0,0,0,80"
                          HorizontalContentAlignment="Stretch" 
                          VerticalAlignment="Top"/>
                        <ScrollBar x:Name="VerticalScrollBar" 
                          Grid.Column="1" Margin="4,0,4,0" 
                          Opacity="0" Orientation="Vertical"/>
                    </Grid>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

The template itself is large and might look like a lot of gibberish due to all the built in styling and transitions. However, we didn’t have to write all of that. You can find the built-in control’s default XAML using Expression Blend (just right-click on the component and choose Edit Template -> Edit a Copy). From there, we simply copy the default XAML and make our tweaks/changes as needed.

This separation of UI from behavior, found in all the system controls, ends up providing a lot of power when you start approaching the limits of the built-in controls and want to extend your UI to do things outside of the original design. For example, if you want to use the FlipView component on Windows 8, but you don’t like the appearance of the previous/next buttons, override the FlipView template and provide your buttons (see how this works for our masthead). The logic of the control (animations, multitouch gestures, item recycling, etc) all stays the same.

Zachary is a software developer on the mobile team at Hulu, and has worked on our Android, Windows 8, and Windows Phone apps.

Last comment: Aug 29th 2014 1 Comment
  • Brian says:

    Another reason to like the MVVM design pattern is that you can write unit tests against your ViewModels. Since they encapsulate the business logic of the form, the unit tests can act as a way of documenting the behavior of the form for future developers.

*
*