iPhone/iPad Development With MonoTouch – A Tutorial

Mark Fricke Development Technologies, Mobile, Xamarin 5 Comments

Attention: The following article was published over 11 years ago, and the information provided may be aged or outdated. Please keep that in mind as you read the post.

What if your boss came to you today and said: “We need to build a mobile iPhone/iPad application for our company’s legacy desktop application.”

After the excitement of finally working on a mobile application subsides, you might begin to wonder if you’ve actually just gotten yourself down a rabbit hole. 

As a .NET development shop, you realize that you don’t have Objective-C programming skills. Plus the requirements of the mobile application must have the same business rules as the desktop version. Since the user would like the application as soon as possible, you don’t have the time to learn and rewrite the current desktop application into Objective-C.

Don’t panic, MonoTouch by Xamarin is here to help you.

Overview

MonoTouch allows you to reuse your C# skills to build iPhone and iPad applications. Unlike HTML5 options, MonoTouch is natively compiled to the iOS platform. This native compile allows you full access of Apple’s API and UI. Some of MonoTouch’s key features include:

  • MonoDevelop – Just like Visual Studio, MonoDevelop is an IDE that provides code assist, project management, and debugging tools.
  • C# – The features of the language Type-Safety, Dynamic Constructs, Functional Constructs such as Lambdas, LINQ, Automatic Garbage Collection are all available.
  • Inter-operability – Third party libraries that are written in Objective-C or C/C++ can be called directly from MonoTouch.
  • Re-usability – The common background business logic can be reused in Windows 7 & 8 mobile, and Mono for Android mobile applications.

Prerequisites

In order for you to use MonoTouch, an Apple computer running Snow Leopard (or later) is required. Since the application is compiled natively, Apple’s Xcode SDK is required. The Xcode development enviroment also provides the iOS emulator for testing the application. If your manager doesn’t want to spend the entire IT budget on your new Apple Macbook, a cheap alternative is the Mac Mini.

MonoTouch is free to download, but it is limited to only running the application within the iOS emulator. If you want to install applications on a iPhone or iPad, then you will need to buy a license.

Download and install the following: 

  • Apple’s Xcode from either the Mac App Store or Apple’s iOS developer website
  • MonoTouch from Xamarin

First Application

Instead of doing a “Hello World” application for the millionth time, we’ll work on an example application that will retrieve the current weather conditions based on location.

A working example can be downloaded from the Keyhole’s GitHub repository: https://github.com/in-the-keyhole/khs-blog-examples.git

After clicking the MonoDevelop IDE icon, lets begin with starting a new MonoTouch project:

After clicking the OK button, a single view template project has been created for you. As a Visual Studio developer, the MonoDevelop environment should look like an old friend. Solutions, projects, files and references are laid out the same way as Visual Studio.

Let’s take a close look at the template that MonoTouch created for you:

  • Main.cs – This file is the starting point of the application.
  • AppDelegate.cs – This file contains the main application class that is responsible for listening to events.
  • CurrentTemperatureViewController.cs – This is where you will write the code to interact with the screen.
  • CurrentTemperatureViewController.designer.cs – This class is automatically updated when you add controls to the screen from XCode IDE.
  • CurrentTemperatureViewController.xib – This file is the screen design as describe by XML.
  • Info.plist – This file contains properties for the application such as application name and target platform

Building the Screen Layout

Double click the CurrentTemperatureViewController.xib file within the project. This will open Xcode and visually display the screen layout. Using the Xcode editor, you can lay out the controls by dragging them from the object list to the screen.

In our example, we are going to add four labels and a button. To learn more about how to use Xcode, visit Apple’s Developer site.

From the object pane of Xcode, drag and drop the UILabels and UIButton to the screen area.

Wire Controls

Apple provides Outlets and Actions that implements the interaction needed for our two controls.

  • Outlets – Outlets work exactly the same as properties in .NET applications.
  • Actions – Actions work in the same manner as the command pattern does for .NET. Actions allow the developer to wire many controls to the same method.

To add an Outlet or Action: change the Xcode view mode to Assistant Editor, which will split the editor screen. While holding the CTRL key, drag the controls from the view to any space in the code after the @interface definition. A popup will be displayed asking for the type and name. Select Outlet and give it a name. Also for the button, we will create an Action for the Touch Up Inside event.

For more detail instructions on adding Outlets and Actions, I recommend visiting Xamarin’s Hello World example.

Now that controls have been wired up to the screen, we now need to start coding the business logic for the windows.

Reusing Business Logic

One of the advantages of using Monotouch is that the business logic that you already wrote for those legacy applications can be reused.  This also includes most 3rd party libraries that are written for .NET.  The domains, validations, and services can be used in the Monotouch application.

In this example, I will create two simple domains Location and Weather.  Even though I’m creating the code within the Monotouch project, for better reusability the code should be in its own library.  This way it can be shared by both the legacy application and Monotouch.

public class Location
{
	public string City { get; set; }
	public string State { get; set; }
	public string Zipcode { get; set; }

}

and

public class Weather
{
   public string Temperature { get; set; }
   public string Condition { get; set; }
   public string Humidity { get; set; }
   public string Wind { get; set; }
}

Now that the domains have been created, I will create a simple service that will call the Yahoo Weather API.  Our service will pass the zip code to the Yahoo Weather API and then parse the current conditions from the xml response.

public class YahooWeatherService : IWeatherService
{
   private const string URL_YAHOO_WEATHER = "http://weather.yahooapis.com/forecastrss?p={0}";
   private readonly string[] DIRECTIONS = {"N","NNE","NE","ENE","E","ESE","SE","SSE","S","SSW","SW","WSW","W","WNW", "NW","NNW"} ;
   public Weather CurrentWeather (string zipcode)
   {
      string url = string.Format(URL_YAHOO_WEATHER, zipcode);
      return ProcessService(url);
   }
   private Weather ProcessService (string url)
   {
      Weather weather = null;
      XmlDocument doc = new XmlDocument ();
      // Load data
      doc.Load (url);
      // Set up namespace manager for XPath
      XmlNamespaceManager ns = new XmlNamespaceManager (doc.NameTable);
      ns.AddNamespace ("yweather", "http://xml.weather.yahoo.com/ns/rss/1.0");
      // Get current forecast with XPath
      XmlNode condNode = doc.SelectSingleNode ("/rss/channel/item/yweather:condition", ns);
      XmlNode astrNode = doc.SelectSingleNode ("/rss/channel/yweather:atmosphere", ns);
      weather = new Weather {
         Condition = condNode.Attributes["text"].Value,
         Temperature = condNode.Attributes ["temp"].Value,
         Wind = getWindDescription(doc, ns),
         Humidity = astrNode.Attributes["humidity"].Value
      };
      return weather;
   }
   private string getWindDescription (XmlDocument doc, XmlNamespaceManager ns)
   {
      XmlNode windNode = doc.SelectSingleNode ("/rss/channel/yweather:wind", ns);
      var speed = windNode.Attributes["speed"].Value;
      var degreeString = windNode.Attributes["direction"].Value;
      float degree = 0;
      float.TryParse(degreeString, out degree);
      int index = (int) ((degree / 22.5) + 0.5);
      index = index % 16;
      string name = DIRECTIONS[index];
      return string.Format("Wind: {0} at {1}mph", name, speed);
   }
}

Location and Geocode

Now that the business and service logic are done, we need to retrieve the GPS coordinates and the location.  The nice thing about using Monotouch is that the classes are named the same as Apple’s own classes.

First, let’s create a delegate class that will be used by the GPS. This class will be extended from the CLLocationManagerDelegate class. The one method that will be overridden is the UpdateLocation. When the GPS updates the coordinates, the UpdateLocation method is called with the new values. The CLGeocoder class method ReverseGeocodeLocation will take the coordinates from the GPS and return the address information to the handler:

public class LocationDelegate : CLLocationManagerDelegate
{
    private CLGeocoder geocoder = null;
    private IWeatherService WeatherService {
        get { return new YahooWeatherService (); }
    }

    private CurrentTemperatureViewController screen;
    public LocationDelegate (CurrentTemperatureViewController screen) : base()
    {
        this.screen = screen;
    }

    public override void UpdatedLocation (CLLocationManager manager, CLLocation newLocation, CLLocation oldLocation)
    {
        if (screen.CurrentWeather == null)
        {
            geocoder = new CLGeocoder ();
            geocoder.ReverseGeocodeLocation (newLocation, ReverseGeocodeLocationHandle);
            manager.StopUpdatingHeading();
            manager.StopUpdatingLocation();
        }
    }

    public void ReverseGeocodeLocationHandle (CLPlacemark[] placemarks, NSError error)
    {
        Location loc = new Location();
        for (int i = 0; i < placemarks.Length; i++) {
            loc.Zipcode = placemarks [i].PostalCode;
            loc.City = placemarks[i].Locality;
            loc.State = placemarks[i].AdministrativeArea;
        }

        if (screen.CurrentWeather == null)
        {
            screen.CurrentWeather = WeatherService.CurrentWeather(loc.Zipcode);
            screen.CurrentLocation = loc;
            screen.DisplayScreen();
        }

    }
}

Controller and AppDelegate

We are now on the home stretch for running the application. The last thing we need to do is set up the controller class.

The CurrentTemperatureViewController class was created by the MonoTouch template wizard. The ViewDidLoad method is called when the view is about to load, and before the constructor is called. In this method, we are going to initialize the CLLocationManager and start the GPS location updates. The DisplayScreen is called from the LocationDelegate that we created earlier. The method will copy the values to the controls that we created on the screen layout. The Refresh method is called during the click event of the refresh button.

public partial class CurrentTemperatureViewController : UIViewController
{
   // Initialize a location manager
   private CLLocationManager iPhoneLocationManager = null;
   private Weather CurrentWeather { get; set; }
   private Location CurrentLocation { get; set; }
   public CurrentTemperatureViewController () : base ("CurrentTemperatureViewController", null)
   {
}
   public override void ViewDidLoad ()
   {
      base.ViewDidLoad ();
      // Perform any additional setup after loading the view, typically from a nib.
      iPhoneLocationManager = new CLLocationManager();
      iPhoneLocationManager.Delegate = new LocationDelegate(this);
      iPhoneLocationManager.StartUpdatingLocation();
      iPhoneLocationManager.StartUpdatingHeading();
      }
   public void DisplayScreen ()
   {
      lblTemp.Text = CurrentWeather.Temperature + "ツー F";
      lblCondition.Text = CurrentWeather.Condition;
      lblWind.Text = CurrentWeather.Wind;
      lblCityState.Text = string.Format("{0}, {1}", CurrentLocation.City, CurrentLocation.State);
   }
   public override void ViewDidUnload ()
   {
      base.ViewDidUnload ();
      // Clear any references to subviews of the main view in order to
      // allow the Garbage Collector to collect them sooner.
      //
      // e.g. myOutlet.Dispose (); myOutlet = null;
      ReleaseDesignerOutlets ();
   }
   public override bool ShouldAutorotateToInterfaceOrientation (UIInterfaceOrientation toInterfaceOrientation)
   {
      // Return true for supported orientations
      return (toInterfaceOrientation != UIInterfaceOrientation.PortraitUpsideDown);
   }
   partial void Refresh(NSObject sender)
   {
      CurrentWeather = null;
      lblTemp.Text = "";
      lblCondition.Text = "";
      lblWind.Text = "";
      lblCityState.Text = "";
      iPhoneLocationManager.StartUpdatingLocation();
      iPhoneLocationManager.StartUpdatingHeading();
   }
}

The last thing to do is set up the singleton AppDelegate class. This class will initialize the application during startup and setup any notifications. The main method to be implemented is the FinishedLaunching method, which will define the root controller. One caveat of the FinishedLaunching method is that code has to complete within 15 seconds or the application will shutdown. If you need a long running service to retrieve data, create a new thread for the task.

[Register ("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
   // class-level declarations
   UIWindow window;
   public override bool FinishedLaunching (UIApplication app, NSDictionary options)
   {
       window = new UIWindow (UIScreen.MainScreen.Bounds);
       window.RootViewController = new CurrentTemperatureViewController ();;
       window.MakeKeyAndVisible ();
       return true;
   }
}

Now that this is complete, we can run the application and see something like the following:

Final Thoughts

As you can see this is a simple application, but it gets you an idea of what Monotouch can bring to the table.  So when the boss asks about exposing that legacy application to mobile platforms, you can be confident about implementing the application.

In a later blog post, I will discuss in more detail navigation and the use of third party libraries. In the meantime, go to the Xamarin website and review the examples. It has a plethora of information from code snippets to guides. Good luck!

— Mark Fricke, [email protected]

0 0 votes
Article Rating
Subscribe
Notify of
guest

5 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments