Consuming a WebApi Asynchronously in Xamarin iOS

Introduction

There are a seemingly endless variety of web services available, like: Amazon, Facebook, and Twitter just to name a few. For developers, being able to access those services allows them opportunities to either extend the capabilities of their own applications by integrating with these services or building new applications using the APIs provided to them over the web.

One great example is the StackExchange family of question and answer sites. StackExchange exposes a rich set of APIs that allow application developers to access almost any data they could imagine from Stack Exchange.

In this article I am going to be using the StackExchange API to access featured questions from the StackOverflow site and display them in an iOS TableView.

**Note: The source code for this article is hosted on GitHub in the TableDemo project, Part2 folder and builds on my previous article ‘A better UITableViewSource in Xamarin iOS’.

Getting Started

First, we need to create a model class to represent the question data that returns from StackExchange. For this example we’re only interested in the question’s title even though there are many more properties in the question object. Here is Question.cs:

public class Question
{
  // title property exposed via the StackExchange webapi
  public string title { get; set; }

  // Override the ToString method to specify what displaying in the
  // TableView's TextLabel Text
  public override string ToString()
  {
    return title;
  }
}

Importing the RestSharp Component

Before we can access the data from the WebApi we need to include a 3rd part component to handle making the Rest Requests to the api. I like to use the RestSharp library which is available through the component library in Xamarin Studio. RestSharp is a simple WebApi client for .NET that makes consuming web services easy. While, you most certainly could write the code to handle the communication with the web services on your own, I find that RestSharp is reliable, easy to use, and provides all of the features I need like object serialization/deserialization and authentication. The RestSharp website has information about how to get questions answered, samples, and links to the project’s source code.

To add this component to our project all we need to do is to right-click on the Components folder in our project and select the ‘Get more components…’ menu option.

Get more components

In the component library when we search for RestSharp we are able to include the library from the search results.

Screen Shot 2013-09-09 at 11.09.07 PM

Loading WebApi Data Asynchronously

Now that we have RestSharp included, we need to add a method to load the data for the TableView to HomeScreen.cs. Here we use the RestSharp library to load the data asynchronously from the StackExchange API. Loading the data asynchronously is an extremely important consideration in application design. It keeps the user interface responsive while the data load is happening and allows for the background handling of any errors that may occur, like there being no network connection or some other data processing exception.

void LoadData()
{
  var request = new RestRequest {RootElement = "items", Resource = "/questions/featured"};
  request.AddParameter("site", "stackoverflow");

  var client = new RestClient("http://api.stackexchange.com/2.1");
  client.ExecuteAsync<List<Question>> (request, response => {
    // do work on UI thread
    InvokeOnMainThread(delegate {
      // pass the data to the TableSource class
      ((TableSource<Question>)table.Source).Data = response.Data;

      // make the TableView reload the data
      table.ReloadData();
    });
  });
}

To complete our update, all we need to do is update the ViewDidLoad method to initialize our modified TableSource and to call the LoadData method.

public override void ViewDidLoad ()
{
  base.ViewDidLoad ();
  table = new UITableView(View.Bounds);
  table.AutoresizingMask = UIViewAutoresizing.All;

  // new TableSource to handle Question model
  var tableSource = new TableSource<Question>();

  // display the question's title when a row is selected
  tableSource.OnRowSelected += (object sender, TableSource<Question>.RowSelectedEventArgs e) => {
    new UIAlertView("Row Selected", 
      tableSource.Data[e.indexPath.Row].ToString(), null, "OK", null).Show();
    e.tableView.DeselectRow (e.indexPath, true);
  };
  table.Source = tableSource;
  Add (table);

  // Load data from the WebApi using RestSharp
  LoadData();
}

Finally

Now when we run our application it looks like this:

iOS Simulator Screen shot Sep 9, 2013 11.22.46 PM

We’ve built a basic iOS TableView application that asynchronously accesses WebApi data from the StackExchange api. We loaded the data asynchronously keeping the application responsive, even during slow data loads.

A better UITableViewSource in Xamarin iOS

The Xamarin Developer Center has a lot of great content to help developers build apps using Xamarin iOS/Droid using many of the components users expect in modern mobile apps. One very common component is a Table View. In this article I am going to show how to extend the UITableViewSource from the BasicTable sample (update: This is Part0 in the GitHub project I’ve created) to be generic and a bit more flexible. Doing so allows the developer to focus on expanding application functionality and not dealing with boilerplate code over and over. The first step is to make the TableSource class generic, change the internal data structure from a string[] to a List<T> and update the class constructors. Here is what the code looks like after this change:

public class TableSource<T> : UITableViewSource {
  public List<T> Data { get; set; }
  protected string cellIdentifier = "TableCell";

  public TableSource ()
  {
    Data = new List<T> ();
  }

  public TableSource(List<T> data)
  {
    Data = data;
  }

  /// <summary>
  /// Called by the TableView to determine how many cells to create for that particular section.
  /// </summary>
  public override int RowsInSection (UITableView tableview, int section)
  {
    return Data.Count;
  }
  ...

We also need to adjust how the GetCell method populates the cell’s title. This is done by changing this the line that sets the cell title from cell.TextLabel.Text = tableItems[indexPath.Row]; to cell.TextLabel.Text = Data[indexPath.Row].ToString();. This convention allows you to easily change what displays in the cell title by overriding the model object’s ToString method.

Now we need to fix the ViewDidLoad method in the HomeScreen class to accommodate these update, which now looks like:

  public override void ViewDidLoad ()
  {
    base.ViewDidLoad ();
    table = new UITableView(View.Bounds); // defaults to Plain style
    table.AutoresizingMask = UIViewAutoresizing.All;
    var tableSource = new TableSource<string>(new List<string>
                                                  {"Vegetables","Fruits","Flower Buds","Legumes","Bulbs","Tubers"});
    table.Source = tableSource;
    Add (table);
  }

Finally, I changed the overridden RowSelected method to use an event. This allows for the customization of row clicks without modifying our base class. For our example app this required two changes. The first to the TableSource class where I replaced the existing RowSelected method:

  /// <summary>
  /// Called when a row is touched
  /// </summary>
  public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
  {
        if (OnRowSelected != null) {
          OnRowSelected (this, new RowSelectedEventArgs (tableView, indexPath));
        }
  }

  public class RowSelectedEventArgs : EventArgs
  {
    public UITableView tableView { get; set; }
    public NSIndexPath indexPath { get; set; }

    public RowSelectedEventArgs(UITableView tableView, NSIndexPath indexPath) : base()
    { 
      this.tableView = tableView;
      this.indexPath = indexPath;
    }
  }

  public event EventHandler<RowSelectedEventArgs> OnRowSelected;

And second where I updated the ViewDidLoad method in the HomeScreen class to handle the row click.

  public override void ViewDidLoad ()
  {
    base.ViewDidLoad ();
    table = new UITableView(View.Bounds); // defaults to Plain style
    table.AutoresizingMask = UIViewAutoresizing.All;
    var tableSource = new TableSource<string>(new List<string>{"Vegetables","Fruits","Flower Buds","Legumes","Bulbs","Tubers"});
    tableSource.OnRowSelected += (object sender, TableSource<string>.RowSelectedEventArgs e) => {
      new UIAlertView("Row Selected", 
        tableSource.Data[e.indexPath.Row].ToString(), null, "OK", null).Show();
      e.tableView.DeselectRow (e.indexPath, true);
    };
    table.Source = tableSource;
    Add (table);
  }

This should give you a good starting point to use UITableView’s in you Xamarin iOS app without having to worry about repeating the UITableViewSource code again and again. The final source is Part1 in my TableDemo GitHub Project.

Refactoring Nerd Dinner – Adding Knockout JS

Ever wonder what Knockout JS is? Since it’s going to be a part of the default ASP.NET template in VS2012 I figured that I should take a look. AND what better project to try something new in than Nerd Dinner. The Popular Dinners list in the Home/Index view is already using jQuery to get JSON data and dynamically building the list, this seems like the perfect candidate to refactor to use knockout. dinners The first step in adding Knockout to NuGet (assuming that you already have the latest Nerd Dinner source) is to get the package from NuGet knockout First, I need to add a script reference to knockout in the _Layout.cshtml

<script src="@Url.Content("~/Scripts/knockout-2.1.0.js")" type="text/javascript"></script>

Next, the code in the Index.cshtml view needs to be changed from the empty container that gets dynamically populated to the use a knockout template. So I changed this line:

<div class="entry-content" id="dinnerList"></div>

to:

<ul class="entry-content" id="dinnerList" data-bind="foreach: dinners">
  <li class="dinnerItem">
    <a data-bind="attr: { href: Url }, text: Title"></a>
        <br />
        <strong data-bind="dateString: EventDate, datePattern: 'mmm d'"></strong><span data-bind="rsvpMessage: RSVPCount"></span>        
  </li>  
</ul>

Similarly, in the NerdDinner.js file _renderDinners needs to be changed to use knockout model binding. After some code cleanup _renderDinners now looks like this:

NerdDinner._renderDinners = function (dinners) {
    var viewModel = {
        dinners: ko.observableArray(dinners)
    };
    ko.applyBindings(viewModel);

    NerdDinner.ClearMap();

    $.each(dinners, function (i, dinner) {
        var LL = new VELatLong(dinner.Latitude, dinner.Longitude, 0, null);

        // Add Pin to Map
        NerdDinner.LoadPin(LL, _getDinnerLinkHTML(dinner), _getDinnerDescriptionHTML(dinner), false);

        // Display the event's pin-bubble on hover.
        var dinnerPin = _getDinnerPin(dinner.DinnerID);
        if (dinnerPin != null) {
            $(dinner).hover(
                function () { NerdDinner._map.ShowInfoBox(dinnerPin); },
                function () { NerdDinner._map.HideInfoBox(dinnerPin); }
            );
        }
    });

    // Adjust zoom to display all the pins we just added.
    if (NerdDinner._points.length &gt; 1) {
        NerdDinner._map.SetMapView(NerdDinner._points);
    }
};

Next, there were some custom binders that I needed to add. One for displaying the EventDate properly and another for showing the RSVP Message. They look like this at the end of the NerdDinner.js file:

ko.bindingHandlers.dateString = {
    update: function (element, valueAccessor, allBindingsAccessor) {
        var value = valueAccessor();
        var allBindings = allBindingsAccessor();
        var valueUnwrapped = ko.utils.unwrapObservable(value);
        var pattern = allBindings.datePattern || 'MM/dd/yyyy';
        var v1 = eval('new' + valueUnwrapped.replace(///g, ' '));
        var v2 = v1.format(pattern);
        $(element).text(v2);
    }
};
ko.bindingHandlers.rsvpMessage = {
    update: function (element, valueAccessor) {
        var value = valueAccessor();
        var rsvpMessage = " with " + value + " RSVP";
        if (value &gt; 1)
            rsvpMessage += "s";
        $(element).text(rsvpMessage);
    }
};

Finally, This sample has just barely scratched the surface of the interesting things that knockout allows you to do. If you’re interested in learning more go

the Knockout JS site. Also, the Nerd Dinner source is on Codeplex. I’d encourage you to get it, fork it, and make it better!

Resolving database issues with Nerd Dinner

Since releasing the latest version of Nerd Dinner into the wild, we’ve had some reports of people having a variety of issues either getting or working with the databases in the source branch. Hopefully, this post will help clear up some of those issues.

First, both of the MDF files should get pulled down with the source. If they aren’t in the App_Data directory, please try to download them again from the source repository. Also, It’s not necessary to explicitly attach these MDF files to the SQL Express instance. The connection string specifies ‘User Instance’ and ‘AttachDBFilename’ properties which spin up a run-time initiated instance of the MDF file.

If you choose to build out your own SQL instance the connection strings will need to be modified to remove the ‘User Instance’ and ‘AttachDBFilename’ properties and include the ‘Initial Catalog’ property pointing to the database you created on your server. Something like:
Data Source=myServerAddress;Initial Catalog=myDataBase;User Id=myUsername;Password=myPassword;MultipleActiveResultSets=True;

Next, If you’re getting this error:

“The database ‘NERDDINNER.MDF’ cannot be opened because it is version 661. This server supports version 662 and earlier. A downgrade path is not supported.Could not open new database ‘NERDDINNER.MDF’. CREATE DATABASE is aborted. (Microsoft SQL Server, Error: 948)”

You have SQL Express 2008 SP2 installed and not SQL Express 2008 R2. Really the way this error should read is:

“The database cannot be opened because it is version 661. This server supports versions 662, 655 and earlier than 655. A downgrade path is not supported.”

My understanding is that SQL Server 2008 SP2 and SQL Server 2008 R2 databases are not interchangeable because a feature introduced in SP2. Part of the confusion, for me, is with the version numbers. R2 is the ‘latest and greatest’, but has a lower data format version #. Here’s a table of the various versions of SQL Server 2008 and their data format version #s.

• SQL Server 2008 R2 (v 10.50.1600) data format version # 661
• SQL Server 2008 SP2 (v 10.00.400) data format version #662
• SQL Server 2008 (v 10.00.1600) data format version #655

The MDF files included in the Nerd Dinner source are from SQL Express 2008 R2. If you find that you don’t have R2 installed there are two ways of remedying this scenario, first would to be to upgrade the SP2 instance to R2. Or you could manually build out the DB on SP2, in which case you would need to build some mechanism of importing the schema.

If you choose to build-out your on SQL instance here are the schema scripts for creating the DBs.

NerdDinners-Deployment-SchemaOnly.sql

ApplicationServices-Deployment-SchemaOnly.sql

ASP.NET MVC Mobile ViewEngines Available on Nuget

Lately, I’ve been spending my spare time helping with Nerd Dinner open source project. The other day Scott Hanselman annouced the latest release of Nerd Dinner, one of the fixes he mention was as fixing a release-mode caching bug that was introduced a few years back.

Also, Phil Haack published the ASP.NET MVC 4 Roadmap, one of the announced improvements was better mobile application support.

So I set out to 1) fix the caching issue and 2) provide the type of mobile application support to ASP.NET MVC 3 applications that was announced for ASP.NET MVC 4. The result was the Mobile View Engines and Sample NuGet packages.

Mobile1

The package provides the source files necessary to render .Mobile ASPX and Razor views for both C# and VB.NET projects. NOTE: These are replacements for the existing Razor and WebForm ViewEngines.

Here’s what the solution looks like after the Mobile View Engines Sample package has been added via NuGet:

Mobile3

Granted, not all of these files are necessary, depending on which language or ViewEngines you use in your project, but I’ve left it up to you dear reader to remove those files that your find unnecessary based on your situation.

The Nerd Dinner project uses the same techniques provided by these MobileCapableViewEngines along with browser detection by 51Degrees.mobi and jQuery Mobile to provide rich context to mobile devices.

I encourage you to give Mobile View Engines a try and let me know how you’d make it better!

Adding HTML5 Geolocation to Nerd Dinner with yepnope.js, and Modernizr

One of the new features of the recently updated Nerd Dinner project is the support for HTML5 Geolocation. There are two JavaScript libraries that I used to accomplish this. The first is yepnope.js (http://yepnopejs.com/). Yepnope.js is an asynchronous resource loader. This means that you can load scripts based on the outcome of some condition. The second is Modernizr v1.7 (http://www.modernizr.com/). Modernizr detects a web browser’s ability to support various HTML5 and CSS3 features. Note: Modernizr v2 has yepnope.js built into the production version so you may want to consider using it in your project. It’s important to remember that yepnope.js is an asynchronous resource loader. This means that the script you’re loading WON’T be available on page load or $(document).ready. What I did was create a common JavaScript function called getCurrentLocation in both javascript files and call that when the script load is completed. The code in Nerd Dinner that kicks the Geolocation off is this:

<script type="text/javascript">
        $(document).ready(function () {
            yepnope({
                test: Modernizr.geolocation,
                yep: '@Url.Content("~/Scripts/geo.js")',
                nope: '@Url.Content("~/Scripts/geo-polyfill.js")',
                callback: function (url, result, key) {
                    getCurrentLocation();
                }
            });
        });
    </script>

So the whole thing works like this: First, the resource loader (yepnope.js) tests if the browser supports native geolocation by using Moderizr. If the browser does (yep) support geolocation natively, the geo.js gets loaded and the callback calls the geo.js version of getCurrentLocation. geo.js

function getCurrentLocation() {
        try {
            // do native HTML5 geolocation
            navigator.geolocation.getCurrentPosition(showCurrentLocation, errorHandler)
        }
        catch (e) {
            // something happened, do it the old way
            NerdDinner.getCurrentLocationByIpAddress();
        }
    }

    // callback from geolocation
    function showCurrentLocation(position) {
        // go update the location box
        NerdDinner.getCurrentLocationByLatLong(position.coords.latitude, position.coords.longitude);
    }

Otherwise (nope), Nerd Dinner uses geo-polyfill.js and the callback calls his version of getCurrentLocation, which uses the browser’s IP address to query a geolocation service for the location data.

function getCurrentLocation() {
    // this is how we used to do it by calling a geolocation service using the client IP address
    NerdDinner.getCurrentLocationByIpAddress();
}

Finally both methods use the location data they got to populate the Location box. This behavior is in both the regular (Views/Home/Index.cshtml) and mobile (Views/Home/Index.Mobile.cshtml) versions. If you want to learn more, I’d encourage you to go checkout the source for Nerd Dinner on CodePlex at http://nerddinner.codeplex.com/.

Hey Microsoft, please extend ASP.NET MVC 4 View Redirection!

There is a lot of discussion going on in the web development world around how to best support mobile devices.

Scott Hanselman’s ( @shanselman ) post ‘A Better ASP.NET MVC Mobile Device Capabilities ViewEngine’ is an interesting look on how to do view redirection using a combination of a custom view engine with extension methods in ASP.NET MVC.

Kevin Montrose’s ( @kevinmontrose ) post ‘Mobile Views in ASP.NET MVC3’ describes how the StackExchange platform uses a view redirection technique to render mobile-friendly views.

And finally, Phil Haack ( @haacked ) posted a sneak peak into the future of ASP.NET MVC with ‘ASP.NET MVC 4 Roadmap’ which describes the tooling around a view redirection technique very similar to what Kevin Montrose describes.

There are all great, but my thought is why not continue down this path of view redirection and make the functionality more generic. This would allow me (the developer) to use the same technique to do things like deliver specific localized views for my Spanish speaking friends.

Conceptually, here’s what I mean, if I want to redirect the mobile views to SomeView.Mobile.cshtml then I can add some code that looks like this:

ViewEngines.Engines.Add(new RazorViewEngine(c => c.IsMobileDevice(), "Mobile"));

Where IsMobileDevice is some extension method that I wrote that does mobile detection. Alternatively, by doing something like this it allows me to similarly do this:

ViewEngines.Engines.Add(new RazorViewEngine(c => c.IsSpanish(), "es"));

Again, IsSpanish is an extension method that I wrote that allows me to do language detection and the views would redirect to SomeView.es.cshtml.

The whole idea here is that while the framework might provide a basic implementation of mobile views, it is open enough to allow the developer to customize it to whatever scenario they can dream up.

Using HTML5 attributes with ASP.NET MVC 3 Helpers

ASP.NET MVC 3 introduces a new way of declaring HTML5 attributes with Html Helpers. The trick is to use underscores in place of the dashes. For Example:

@Html.ActionLink("About", "About", "Home", new { data_some_attribute="testing" })

That is all. k. thx. bye.

Extending the ASP.NET MVC ViewEngine to support localization

I’ve been using with Scott Hanselman’s CustomMobileViewEngine from his post A Better ASP.NET MVC Mobile Device Capabilities ViewEngine along with jQuery Mobile (for mobile templates) and 51degrees.mobi (for accurate mobile browser detection) to build ASP.NET MVC sites that output nice mobile-friendly templates. The techniques that Scott talks about in his post have been working really well.

So recently when I had to solve a similar problem when trying to render out localized templates I took a deeper look into Scott’s approach to see if I could ‘tweak’ it to do what I wanted, which, in Visual Studio looks like:

This allows my site to serve the same URLs for multiple languages. For example the url /Home/Index uses the same controllers, models, etc, but will call different views for English and Spanish users based on the current uiCulture.

Here’s how I did it. I took Scott’s classes and refactored them just a bit so that as I extended this functionality there weren’t bits of code getting duplicated (See DRY – Don’t repeat yourself ).

First, I took the existing CustomMobileViewEngine class and renamed it CustomViewEngine as this engine will no longer be Mobile only. Other than that no changes were necessary.

public class CustomViewEngine : IViewEngine
{
    public IViewEngine BaseViewEngine { get; private set; }
    public Func IsTheRightDevice { get; private set; }
    public string PathToSearch { get; private set; }

    public CustomViewEngine(Func isTheRightDevice, string pathToSearch, IViewEngine baseViewEngine)
    {
        BaseViewEngine = baseViewEngine;
        IsTheRightDevice = isTheRightDevice;
        PathToSearch = pathToSearch;
    }

    public ViewEngineResult FindPartialView(ControllerContext context, string viewName, bool useCache)
    {
        if (IsTheRightDevice(context))
        {
            return BaseViewEngine.FindPartialView(context, PathToSearch + "/" + viewName, useCache);
        }
        return new ViewEngineResult(new string[] { }); //we found nothing and we pretend we looked nowhere
    }

    public ViewEngineResult FindView(ControllerContext context, string viewName, string masterName, bool useCache)
    {
        if (IsTheRightDevice(context))
        {
            return BaseViewEngine.FindView(context, PathToSearch + "/" + viewName, masterName, useCache);
        }
        return new ViewEngineResult(new string[] { }); //we found nothing and we pretend we looked nowhere
    }

    public void ReleaseView(ControllerContext controllerContext, IView view)
    {
        throw new NotImplementedException();
    }
}

Next, I took the most generic AddMobile extension method, renamed it AddCustomView and put it in it’s own ViewHelper class.

public static class ViewHelper
{
    public static void AddCustomView(this ViewEngineCollection ves, Func isTheRightDevice, string pathToSearch)
    where T : IViewEngine, new()
    {
        ves.Add(new CustomViewEngine(isTheRightDevice, pathToSearch, new T()));
    }
}

Additionally, I refactored the existing MobileHelpers class to call the newly refactored AddCustomView to prevent further duplication of code.

public static class MobileHelpers
{
    public static bool UserAgentContains(this ControllerContext c, string agentToFind)
    {
        return (c.HttpContext.Request.UserAgent.IndexOf(agentToFind, StringComparison.OrdinalIgnoreCase) >= 0);
    }

    public static bool IsMobileDevice(this ControllerContext c)
    {
        return c.HttpContext.Request.Browser.IsMobileDevice;
    }

    public static void AddMobile(this ViewEngineCollection ves, string userAgentSubstring, string pathToSearch)
        where T : IViewEngine, new()
    {
        ves.AddCustomView(c => c.UserAgentContains(userAgentSubstring), pathToSearch);
    }

    public static void AddIPhone(this ViewEngineCollection ves) //specific example helper
        where T : IViewEngine, new()
    {
        ves.AddCustomView(c => c.UserAgentContains("iPhone"), "Mobile/iPhone");
    }

    public static void AddGenericMobile(this ViewEngineCollection ves)
        where T : IViewEngine, new()
    {
        ves.AddCustomView(c => c.IsMobileDevice(), "Mobile");
    }
}

Finally, I created some AddLanguage extension methods in their own LocalizationHelpers class along with the UICulture detection routing.

public static class LocalizationHelpers
{
    public static bool UICultureEquals(this ControllerContext c, string stringToFind)
    {
        var culture = CultureInfo.CurrentUICulture;
        var cultureName = culture != null ? culture.Name : string.Empty;

        return (cultureName.IndexOf(stringToFind, StringComparison.OrdinalIgnoreCase) >= 0);
    }

    public static void AddLanguage(this ViewEngineCollection ves, string cultureName, string pathToSearch)
    where T : IViewEngine, new()
    {
        ves.AddCustomView(c => c.UICultureEquals(cultureName), pathToSearch);
    }

    public static void AddLanguage(this ViewEngineCollection ves, string cultureName)
    where T : IViewEngine, new()
    {
        ves.AddCustomView(c => c.UICultureEquals(cultureName), cultureName);
    }
}

Using the Localized views in your project is as simple as registering the view in the Application_Start() method.

ViewEngines.Engines.Clear();
ViewEngines.Engines.AddLanguage("es-ES");
ViewEngines.Engines.AddGenericMobile();
ViewEngines.Engines.AddCustomView(c => c.IsMobileDevice() && c.UICultureEquals("es-ES"), "Mobile/es-ES");
ViewEngines.Engines.Add(new WebFormViewEngine());

Lastly, my sample project has these classes in they’re own class library because it’s my hope to be able to provide this functionality as a NuGet package soon.