If you're new to using MonoTouch, you might find it helpful to check out my article "Guide to Building iOS Applications with MonoTouch for the .NET/C# Developer." Additionally, a companion article to this one, "MonoTouch Tutorial: Display Tabular Data in iPhone and iPad Apps," covers how to display and retrieve data in iPhone and iPad apps.

Mobile phones are by definition, mobile. A couple of interesting questions that come up when users are mobile are, "What's around me for dinner?" and "Where can I get gasoline?" If you are a retailer or a company, you want to tell potential customers that there is a retail location near them. If you are a user, you might also be interested in learning about how to go from where you are to a specific address. In this article we'll look at these and associated features, so that we can provide them to users via mapping and location services to users in iOS apps.

A Few Preliminaries

In this article, we will use Apple's Map Kit framework to provide the app's mapping support and the Core Location framework to provide location awareness. The support for Map Kit is within the MonoTouch.Map Kit namespace, and the support for location is within the MonoTouch.CoreLocation namespace. The two namespaces are used together. You can download the code accompanying this article by clicking the Download button at the top of the page.

Maps

Users want to know where they are. They would like to know street names and where they are in location to other people they know. In addition to providing these capabilities in our app, we will need to get support for other items of location information. To provide these capabilities, we can use iOS's support for mapping via the Map Kit framework and for location information via the Core Location framework.

First off, let's dive into Map Kit. Map Kit allows a developer to embed a map interface in an application. Map Kit provides many features that we would expect, such as:

  • street-level information
  • satellite information
  • a combination of street-level and satellite information
  • the ability to zoom and pan the map programmatically
  • the ability to zoom and pan the map via touch events
  • annotating the map with custom information

Let's start using Map Kit. The first step is to create a new single-view iPhone project and open up the .xib file in the Xcode IDE, as shown in Figure 1.

Figure 1: Creating an iPhone project that uses Map Kit to add mapping to an app

You will need to add an MKMapView to the design surface and set up the necessary outlets. Along with doing this, developers will want to modify the Map View attributes. Specifically, developers will want to pay attention to the type value. Developers can set the values to Map (default), Satellite, or Hybrid. The type value can also be set programmatically via the .Map Kit property and the MKMapType enum.

By default, Map Kit will display a map like that in Figure 2 below, centered on a world location.

Figure 2: Default Map Kit map

Now that we've set up the map, let's go ahead and initialize it. We'll initialize the visible portion of a map with the .Region property of a map. Note: MapIt is the name of the outlet for the map that the code will use.

MapIt.Region = new MKCoordinateRegion(new CLLocationCoordinate2D(lat, lon), new MKCoordinateSpan(latDelta, lonDelta));

Here's a little bit of background. Many of the online map systems, such as Google and Bing, use a map system where a map is defined as a center point and a zoom level. The zoom level is typically an integer value with a minimum and maximum value based on a given location.

In iOS with MKMap Kit, an MKCoordinateRegion is what is passed to the region property. The two objects that make up the region are the center point and the span that needs to be displayed. The center point is fairly self-explanatory. The span is similar to the width and height values of a rectangle that will fit within the map's region. The span that is passed in is used as a starting point. Map Kit will use this region to determine the appropriate zoom level. A zoom level is chosen that will display the entire span passed in. Zoom levels are not arbitrary, so the span that is passed in will not be specifically displayed, merely used as a starting point. Once we have a center point and a span, we can initialize our map.

I'm sure that your next question is, "How do I programmatically move the map, as necessary?" This can be done one of two ways:

  • Method 1: Set the .Region property similarly to how we initialized the map. With the Region property, we'll define the center via the CLLocationCoordinate. The next question is, "How large should the map be that is displayed?" This is done by setting the MKCoordinateSpan. In this case, we'll initialize the span to show a .5 delta latitude and longitude.
  • Method 2: Set the center of the map via a call to the .SetCenterCoordinate() method.

Here's some code that will tie the initialization and the moving of the map together.

if ( !StartVal ) {
MapIt.SetCenterCoordinate(new CLLocationCoordinate2D(lat, lon), true);
}
else{
MapIt.Region = new MKCoordinateRegion(new CLLocationCoordinate2D(lat, lon),
new MKCoordinateSpan(.5, .5));
        StartVal = false;
}

In this source code example, if our code has never initialized the map, then we'll set up the map via the call to set the .Region property. If the map is already initialized, the call to SetCenterCoordinate will cause the map center to move. The CLLocationCordinate2D is fairly self-explanatory. It represents the new center of the map. The final parameter is a bool. If the value is true, the change is animated. If the value is false, the change is not animated. Since we want to have an attractive graphical display (and be cool in the iPhone world), we'll hard-code the value to true.

Finally, there are a few properties that are valuable when setting up a map:

  • UserInteractionEnabled. This is a Boolean property. When it is set to true, the map can respond to user interaction, such as a pinch or zoom.
  • ShowsUserLocation. This is a Boolean property. When it's set to true, the map will display the current user location. The location is displayed via a small blue pin along with an animated circle around the location of the device that it has determined.

Forward Geocoding

Forward geocoding is the process of taking a location that people understand into a latitude/longitude pair. Unfortunately, iOS 4 and earlier versions don't have built-in support for forward geocoding. Because of this, we'll use a Yahoo! API to perform forward geocoding asynchronously. In this case, to perform forward geocoding, our code will make an asynchronous request to a REST-based web service and then process the result using LINQ to XML. Figure 3 shows the code that does this.

Figure 3: Performing forward geocoding
public class Geocode
{
        public Geocode ()
        {
        }
        public void GetLatLonAsync(string Location, AsyncCallback callback)
        {
                // Don't worry, this is a dummy API key that I created.
                string YahooAPIKey = "aa4dMr6k";
                        string yahooUrl = String.Format("http://local.yahooapis.com/MapsService/V1/geocode?appid={0}&location={1}", YahooAPIKey, Location);
                        HttpWebRequest request = WebRequest.Create(yahooUrl) as HttpWebRequest;
                        request.Method = "GET";
                        request.ContentType = "application/xml";
                        request.BeginGetResponse(callback, request);                   
        }
        public void GetLatLonAsyncProcess(IAsyncResult iar, ref double? Lat, ref double? Lon, ref double? radiusMiles)
        {
                HttpWebRequest request = (HttpWebRequest)iar.AsyncState;
                        HttpWebResponse response;
                        response = (HttpWebResponse)request.EndGetResponse(iar);
                        System.IO.StreamReader strm = new System.IO.StreamReader(
                                response.GetResponseStream());
                        System.Xml.Linq.XDocument xd = XDocument.Load(strm);
                        XNamespace yahoolcl = "urn:yahoo:maps";
                        var searchR = (from result in xd.Descendants(yahoolcl + "ResultSet").Elements(yahoolcl + "Result")
                        where result != null
                                select new LocationResult
                                {
                                Latitude = Convert.ToDouble(result.Element( yahoolcl + "Latitude").Value),
                                        Longitude = Convert.ToDouble(result.Element( yahoolcl + "Longitude").Value),
                                Radius = CalculateRadius(result.Attribute("precision").Value)
                           }).ToList();
                        if ( searchR.Count > 0 )
                        {
                                Lat = searchR[0].Latitude;
                                Lon = searchR[0].Longitude;
                                radiusMiles = searchR[0].Radius;
                        }
                }
                private double CalculateRadius(string precision)
                {
                        // perform some calculation to determine
                        return result;
                }
        }

Forward Geocoding with iOS 5

With the release of iOS 5 and MonoTouch 5, we can now use the forward geocoding support built into iOS. With this, developers can take a human-language address and convert it into a latitude/longitude pair. In the code sample in Figure 4, we'll look up the location and then place the appropriate points on a map.

Figure 4: Looking up a location and placing relevant points on a map
void HandleSearchWhereSearchButtonClicked (object sender, EventArgs e)
                {
                        string sw = searchWhere.Text;
                        searchWhere.ResignFirstResponder();
                        CLGeocoder clg = new CLGeocoder();
                        clg.GeocodeAddress(sw, GeoCodeCompletionHandler);
                }

                void GeoCodeCompletionHandler(CLPlacemark[] placemarks, NSError error)
                {
                        int i = 0;
                        if ( oal.Count > 0 )
                        {
                                MapIt.RemoveAnnotations(oal.ToArray());
                        }
                        oal.Clear();
                        for(i = 0; i < placemarks.Length; i++)
                        {
                                var loc = placemarks[i].Location.Coordinate;
                                oal.Add(new ObjAnnotation(new
CLLocationCoordinate2D(loc.Latitude, loc.Longitude),
                                        placemarks[i].Name, string.Empty));
                        }
                        MapIt.AddAnnotationObjects(oal.ToArray());
                }

Figure 5 shows the results of this code.

Figure 5: Location results

There are a few things to note in this sample:

  • On the return call, there is no InvokeOnMainThread. Even though the call to geocode the address is done on an asynchronous thread, the callback actually runs on the UI thread.
  • The placemarks array can contain multiple items. It is my experience that multiple points come back less often than I would expect.
  • There has been discussion online that the iOS 5 geocoder is less accurate outside of North America and Western Europe. Developers will need to determine what geocoder works the best for them.

Reverse Geocoding

Reverse geocoding is the process of taking a latitude/longitude point and getting a physical address for the point. Reverse geocoding is not an exact science by any stretch of the imagination. From my personal experience, I have found that while sitting in my office in Knoxville, Tennessee, sometimes I will get the correct address for my location, whereas other times I will get a location that is within several miles/kilometers but not exactly correct. And sometimes, I will get cities that are over 100 miles away. Understand that reverse geocoding may not work 100 percent as expected all the time.

iOS provides the ability to perform reverse geocoding, by using the MKReverseDelegate class. The steps to use this class to get the location are:

  1. Inherit from the MKReverseGeocoderDelegate class.
  2. Override the method FoundWithPlacemark method. This method will be what iOS calls to pass information back to your application to determine the location.
  3. You will most likely want to pass this information to the logic of your application. This is done by passing a reference to the appdelegate of your application and then calling one of the exposed methods in your appdelegate.
  4. Reverse geocoding requires a connection to the public Internet. The lookup is performed on a service provided by Apple.
  5. Finally, the reverse geocoding process is asynchronous. We're running on a non-UI thread, so we need to call InvokeOnMainThread to update the user interface, as shown in Figure 6.
Figure 6: Calling InvokeOnMainThread
public class ReverseGeocoderDelegate : MKReverseGeocoderDelegate
        {
                private AppDelegate app;
                #region implemented abstract members of MonoTouch.Map Kit.MKReverseGeocoderDelegate
                public override void FailedWithError (MKReverseGeocoder geocoder, NSError error)
                {
                }
                public override void FoundWithPlacemark (MKReverseGeocoder geocoder, MKPlacemark placemark)
                {
                        var coord = geocoder.coordinate;
                        var lat = coord.Latitude;
                        var lon = coord.Longitude;
                        app.InvokeOnMainThread(delegate{
                                app.HandleMap(lat, lon, placemark.Title, String.Empty);
                        } );
                }
                #endregion
               
                public ReverseGeocoderDelegate ()
                {
                       
                }
               
                public ReverseGeocoderDelegate(AppDelegate val){
                        app = val;     
                }
        }

Annotating the Map

Adding points to a map provides information in a visual way that humans can easily process. This is an important step. After all, a picture is worth a thousand words. In iOS, adding points to a map is done via annotations. Unfortunately, many of the properties for the MKAnnotation class are read-only, so we'll create our own object. The process for doing this is:

  1. Create your own custom object that inherits from the MKAnnotation object.
  2. Override the constructor, so that we can pass in the latitude and longitude via the CLLocationCoordinate2D along with Title and Subtitle information, as shown in Figure 7.
Figure 7: Passing in latitude, longitude, Title, and Subtitle information
public class ObjAnnotation : MKAnnotation
        {
                public ObjAnnotation(){}
                       
                private CLLocationCoordinate2D _coordinate;
                private string _title, _subtitle;
                public override CLLocationCoordinate2D Coordinate {
                  get { return _coordinate; }
                  set { _coordinate = value; }
                }
                public override string Title {
                  get { return _title; }
                }
                public override string Subtitle {
                  get { return _subtitle; }
                }
               
                //#endregion
                /// <summary>
                /// Need this constructor to set the fields, since the public
                /// interface of this class is all READ-ONLY
                /// <summary>
                public ObjAnnotation (CLLocationCoordinate2D Coordinate,
                                        string Title, string SubTitle) : base()
                {
                  _coordinate=Coordinate;
                  _title=Title;
                  _subtitle=SubTitle;
                }
        }

Now that we have the MKAnnotation object, we'll need to manage these annotations on our map. This can be done via the Map Kit members:

  • .AddAnnotation (annotation object). This method will add an annotation object to the map.
  • .AddAnnotations (array of annotation objects). This method will add an array of annotation objects to the map.
  • .RemoveAnnotation (annotation object). This method will remove the specific annotation object from the map.
  • .RemoveAnnotations (array of annotation objects). This method will remove an array of annotation objects from the map.

Displaying Retail Locations

Displaying the retail locations on a map can be a very important thing for a retailer. To get these retail locations, we don't want to hard-code this data into our application. We'll need to call out to a web service to get the data and then display these points on our map.

In this example, we don't have a web service to call, so we'll use the Yahoo! local search services to get some data that we can place on a map, shown in the code in Figure 8.

Figure 8: Using Yahoo! local search services to get local-retailer data to place a map
void PerformYahooSearchAsync ()
{
term = searchTerm.Text;
        if ( !String.IsNullOrEmpty(term))
        {
                Console.WriteLine("Search Term :" + term);
                var center = new CLLocation(Latitude, Longitude);
                var radius = center.Distancefrom(new CLLocation(Latitude + deltaLatitude, Longitude));
                if ( ys == null )
                {
                        ys = new YahooSearch.YahooSearch(Latitude, Longitude, radius, term);
                }
                else{
                        ys.Latitude = Latitude;
                        ys.Longitude = Longitude;
                        ys.Radius = radius;
                        ys.Query = term;
                }
                ys.StartSearch(new AsyncCallback(YahooSearchProcessResult));
                }
        }

        void YahooSearchProcessResult(IAsyncResult iar)
        {
                srl = ys.ProcessRestXmlLINQHttpResponse(iar);
                var center = new CLLocation(Latitude, Longitude);
                foreach(YahooSearch.SearchResult sr in srl)
                {
                        sr.DistanceFromCenter = center.DistanceFrom(
new CLLocation(sr.Latitude, sr.Longitude));
                }
                MapIt.RemoveAnnotations(searchPoints.ToArray());
                searchPoints.Clear();
                foreach(YahooSearch.SearchResult sr in srl)
                {
                        var o = new     ObjAnnotation(
new CLLocationCoordinate2D(sr.Latitude, sr.Longitude),
                                sr.Description, "Meters from center: " +
                                sr.DistanceFromCenter.ToString());
                        searchPoints.Add(o);
                }
                InvokeOnMainThread(delegate{
                        MapIt.AddAnnotationObjects(searchPoints.ToArray());
                } );
        }

The sequence of events to perform the search and place them on the map is:

  1. We need to get the term that the user is going to search on. We'll use the .Text member of the UISearchBar to obtain this.
  2. To perform a search, we'll need an area to search on based on latitude and longitude, the area to search within -- which is calculated from the radius, the term that will be searched on, and finally an appkey to make the necessary calls.
  3. An interesting question is, how do we calculate the radius that we want to search on? Thankfully, the CLLocation object exposes a member named Distancefrom. Distancefrom allows a program to determine the distance between two points. We use this to determine the radius to search on.

Once we get our data back and created, we use async search via the Yahoo! Local Search API, as shown in Figure 9.

Figure 9: Async search via the Yahoo! Local Search API
public void StartSearch(AsyncCallback iac)
                {
                        object[] items = {Latitude, Longitude, Radius, appKey, Query};
                string Url = String.Format(SearchUrl, items);
                try
                {
                                HttpWebRequest request = WebRequest.Create(Url) as HttpWebRequest;
                                request.Method = "GET";
                                request.ContentType = "application/xml";
                                request.BeginGetResponse(iac, request);
                }
                catch
                {
                                //do something
                        throw;
                }
}
               
        public List<SearchResult> ProcessRestXmlLINQHttpResponse(IAsyncResult iar)
                {
                List<SearchResult> searchR;
                try
                {
                                HttpWebRequest request = (HttpWebRequest)iar.AsyncState;
                                HttpWebResponse response;
                                response = (HttpWebResponse)request.EndGetResponse(iar);
                                System.IO.StreamReader strm = new System.IO.StreamReader(
                                response.GetResponseStream());
                                System.Xml.Linq.XDocument xd = XDocument.Load(strm);
                                XNamespace yahoolcl = "urn:yahoo:lcl";
                                searchR = (from result in xd.Descendants(yahoolcl + "ResultSet").Elements(yahoolcl + "Result")
                                where result != null
                                select new SearchResult
                                {
                                        Name = result.Element( yahoolcl + "Title").Value,
                                Description = result.Element( yahoolcl + "Title").Value,
                                        Latitude = Convert.ToDouble(result.Element( yahoolcl + "Latitude").Value),
                                        Longitude = Convert.ToDouble(result.Element( yahoolcl + "Longitude").Value)
                                }).ToList<SearchResult>();                     
                        }
                        catch
                        {
                                //do something
                                throw;
                        }
                        return(searchR);
                        }

Figure 10 shows the output of the local-search code, displaying search results for "golf" on a map in my area.

Figure 10: Results of a local retailer search displayed on a map

Overlays

Overlays are similar to annotations. They are a special type of shape that can be added to a map. Overlays are available in iOS 4 and later versions. In our next example, a circle is drawn around the center of the map. This circle was created with an MKCircle object. The code used to create this circle starts with what is shown in Figure 11.

Figure 11: Snippet of code that uses the MKCircle object
if (mostRecentCircle != null )
        {
                MapIt.RemoveOverlay(mostRecentCircle); 
        }
        var objAn = new ObjAnnotation(new CLLocationCoordinate2D(lat, lon), Title, SubTitle);
        mostRecent = objAn;
        CLLocationCoordinate2D circleOverlay = new CLLocationCoordinate2D(lat, lon);
        double radiusInMeters = 5000d;  
        MKCircle circle = MKCircle.Circle(circleOverlay, radiusInMeters);    
        mvd.circle = circle;
        mostRecentCircle = circle;
        if ( !StartVal ) {
                MapIt.SetCenterCoordinate(new CLLocationCoordinate2D(lat, lon), true);
        }
        else{
                MapIt.Region = new MKCoordinateRegion(
new CLLocationCoordinate2D(lat, lon), new MKCoordinateSpan(.5, .5));
                StartVal = false;
        }
        MapIt.AddOverlay(circle);

This code is from our Main.cs file in the MonotouchMap KitIntro project. Figure 12 shows the displayed results: a point on a map displayed in the ZIP code 37922.

Figure 12: Map point with a circle drawn around it

And then finally, the MapView Delegate overrides the GetViewForOverlay. We'll use this to modify the color of our overlay, as follows:

          public override MKOverlayView GetViewForOverlay(MKMapView mapView, NSObject overlay)
          {
            if ((circle != null) && (_circleView == null))
            {
              _circleView = new MKCircleView(circle);
                  _circleView.Opaque = true;
              _circleView.FillColor = UIColor.FromRGBA(0.0f, 0.4f, 0.4f, .3f);
            }
            return _circleView;
          }

This code can be found in the MapViewDelegate.cs file in the MonotouchMap KitIntro solution, available in the downloadable code file accompanying this article.

Core Location

Many applications need to get the user's current location. This can be for social networking, turn-by-turn navigation, or any other series of needs. Our applications can tap into location information by using the MonoTouch.CoreLocation namespace. This namespace provides the following capabilities:

  • Location services . Location services allow an application to determine the latitude and longitude of the device, as well as changes to the location.
  • Region monitoring . Region monitoring allows an application to monitor boundary crossings. There are two ways to determine the device's current location:
    • iOS has a general-purpose configurable location service, which is available on all versions of iOS. This location service is available by the call to .StartMonitoringLocationChanges().
    • Starting with iOS 4, there is the .StartMonitoringSignificantLocationChanges() event, which provides a power-efficient location service. In this case, the data returned may not be as specific as desired. Therefore, it is important to verify that the returned data is acceptable within the application. In iOS 4, the StartMonitoringSignificantLocationChanges event is keyed off of changes to the cell towers used by the device.

The code to set up the Core Location Manager for location changes looks something like this:

private CLLocationManager clMgr;
…...........
clMgr = new CLLocationManager();
clMgr.DesiredAccuracy = 100;
clMgr.StartUpdatingLocation();
clMgr.UpdatedLocation += HandleClMgrUpdatedLocation;
.........................
void HandleClMgrUpdatedLocation (object sender, CLLocationUpdatedEventArgs e)
{
// do something with the event args that are passed in.
}

In this code, the Core Location Location Manager is created, its .DesiredAccuracy is set up in meters, and the UpdatedLocation event is set up. When the UpdatedLocation event occurs, our program can use the CLLocationUpdatedEventArgs to get information regarding the old location and the new location.

Now that we have the basics down for Core Location, we might want to test regarding Core Location. The two most valuable tests are:

  • Does the device support location services? This test can be set up via the Info.plist file. When we set the UIRequiredDeviceCapabilities key and add location services, the array of values will limit the application to being installed on a device that supports location services. If an application requires GPS services, then GPS should be added to the UIRequiredDeviceCapabilities key.
  • When an application is running, it needs to test to determine whether the user has turned on location services. This is done via the static method using .LocationServicesEnabled on the CLLocationManager class.

It's important to remember that location services must be turned on for each application. When an application makes a request regarding location services, iOS asks users for their OK for the app to do so, as in Figure 13.

Figure 13: iOS asking user's permission to allow an app to use location services

Region Monitoring

Changing location is valuable in many situations. Region monitoring is a concept similar to monitoring location changes. For example, it might be valuable to know when you are near an area where you should have the phone ringer turned down, such as an upscale restaurant. The steps to set up region monitoring are:

  1. Check the availability of region monitoring on the device. Also check to see whether region monitoring is enabled on the device.
  2. Create a region.
  3. Assign the event handlers for the .RegionLeft and .RegionEntered events.
  4. Begin monitoring on the region via the StartMonitoring method. Notice that there is a second parameter for the method, a desired accuracy.

Figure 14 shows the region-monitoring code.

Figure 14: Region-monitoring code
public partial class AppDelegate : UIApplicationDelegate
{
        // This method is invoked when the application has loaded its UI and it's ready to run
        private CLLocationManager cllm;
        public override bool FinishedLaunching (UIApplication app, NSDictionary options)
        {
                // If you have defined a view, add it here:
                // window.AddSubview (navigationController.View);
                cllm = new CLLocationManager();
                //Latitude and Longitude for Knoxville, TN, US
                var lat = 35.9605556;
                var lon = -83.9208333;
                if (CLLocationManager. RegionMonitoringAvailable &&
                        CLLocationManager. RegionMonitoringEnabled)
                {
                        CLRegion clr = new CLRegion(new CLLocationCoordinate2D(lat, lon),
                                CLLocation.AccuracyHundredMeters, "Knoxville");
                        cllm.RegionLeft += HandleCllmRegionLeft;
                        cllm.RegionEntered += HandleCllmRegionEntered;
                        cllm.StartMonitoring(clr, CLLocation.AccuracyHundredMeters);
                }
                window.MakeKeyAndVisible ();
       
                return true;
        }
        void HandleCllmRegionEntered (object sender, CLRegionEventArgs e)
        {
                Console.WriteLine (String.Format("Region entered: {0}", e.Region.Identifier));
        }
        void HandleCllmRegionLeft (object sender, CLRegionEventArgs e)
        {
                Console.WriteLine (String.Format("Region Left: {0}", e.Region.Identifier));
        }

What's the Cost?

Remember, we're doing mobile development. As long as we're making our networking calls asynchronously, we're doing the best that we can do. We don't have much control over the quality of our network connectivity. However, we do have some control over the amount of power that we use. The more data and the more often that we pull data across the antenna and communicate off device, the faster we will drain the battery. As a result, automatically calling a web service after a device moves location is probably a bad idea unless the application really needs it. Trust me, my wife hates it when I make too many web service calls with her iPhone and kill the battery after three hours; I never thought the beatings would stop.

We can't really control when a user changes the map. However, we can limit what we do when a map event occurs. Also, the more we use the GPS within the device, the faster we will drain the battery. Here are some tips for limiting battery usage:

  • Use the StartMonitoringSignifcantLocationChanges() method of the CLLocationManager as much as possible in your applications. This method is more power-efficient than StartMonitoringLocationChanges().
  • The .DesiredAccuracy property will ultimately determine how often the GPS services on the devices are called. The lower the number passed in, the faster the battery will be drained. The higher the number, the more slowly the battery will be drained.

With region monitoring, more things are happening when you have more regions, more calculations, and more monitoring. Therefore, the battery is going to be used more; I can just hear the poor battery on the device being used up and screaming in pain. So we want to limit the number of regions being monitored. There is an associated .StopMonitoring(CLRegion) method that will allow a program to no longer monitor a region.

Remember as you work through your application, users like their devices to run all day. Pay attention to how much power your application uses, which can sometimes be at odds with the requirements of an application.

Recent iOS 5 Updates

With the shipment of iOS 5, Apple shipped some exciting changes in the SDK:

  • Location in the emulator that will make testing much easier. Setting this up is much simpler. To do so, simply go to the iOS Simulator and select Debug, Location and select a location to set up. In my case, I have a custom location. When selecting a custom location, you will get a simple window to input the latitude and longitude, shown in Figure 15.
  • There are changes coming with the CLLocationManager StartMonitoringSignificantLocationChanges method that will allow it to key on other changes. The bottom line is that it will be more useful.
  • Forward geocoding will be added. With this, there won't be a need to go out to a geocoding service. In most of these examples, we used the Yahoo! geocoder. If your applications must support iOS 4 and earlier, the new forward geocoding support cannot be used. Also, there are some previously mentioned discussion items about the accuracy of geocoding in "out of the way" places. At the end of the day, it's up to the developer to determine the best geocode source for their use.

    Figure 15: Setting up a custom location in the iOS Simulator

Wrapping Up

As you've seen, you have a number of options for adding mapping and location-awareness features to mobile iOS applications developed with MonoTouch. With help from the topics discussed in this article plus the sample code I've provided, you'll be able to add maps, geocoding and reverse geocoding, annotations, retail location search results, device location services, and region monitoring to mobile apps. What you don't find here you will probably be able to find in one or more of the information sources listed in the References section at the end of this article.

Many thanks to:

  • Jason Trahan, @oLunatiko on Twitter, for pointing me to a workaround involving debugging at the time I needed it.
  • Craig Dunn (@conceptdev), Chris Hardy (@chrisntr), and my other co-authors for working with me on our book on MonoTouch. I learned a lot by working with them.
  • Bryan Costanich and Mike Bluestein for their books on MonoTouch. (See the References list below.)
  • Everyone at Xamarin who is associated with MonoTouch. It really is a good product. I'm amazed at their product and support.
  • Wrox for allowing us to produce a really good book.

References

Wallace B. "Wally" McClure (wallym@scalabledevelopment.com) is an ASPInsider, member of the national INETA Speakers Bureau, author of seven programming books, and a partner in Scalable Development. He blogs at www.morewally.com and co-hosts the ASP.NET Podcast (www.aspnetpodcast.com). Find Wally on twitter as @wbm.