Walkthrough - Using Background Location

PDF for offline use
Sample Code:
Related SDKs:

Let us know how you feel about this

Translation Quality


0/250

In this example, we are going to build an iOS Location application that prints information about our current location: latitude, longitude, and other parameters to the screen. This application will demonstrate how to properly perform location updates while the application is either Active or Backgrounded.

During the walkthrough, we will touch on some key backgrounding concepts, including registering an app as a background-necessary application, suspending UI updates when the app is backgrounded, and working with the WillEnterBackground and WillEnterForeground AppDelegate methods.

Walkthrough

Application set up

  1. First, create a new iOS > App > Single View Application (C#). Let's call it Location, and ensure that both iPad and iPhone have been selected.
  2. A location application qualifies as a background-necessary application in iOS. We can register the application as a Location application by editing the Info.plist file for our project.
  3. Under Solution Explorer, double click on the Info.plist file to open it, and scroll to the bottom of the list. Place a check by both the Enable Background Modes and the Location Updates checkboxes.

    In Xamarin Studio, it will look like something like this:

    In Visual Studio, Info.plist needs to be updated manually by adding the following key/value pair:

    <key>UIBackgroundModes</key>
        <array>
            <string>location</string>
        </array>
  4. Now that the application is registered, we need to get location data from the device to our application. In iOS, we can do this with the help of a CLLocationManager. The CLLocationManager provides a key class of Core Location and will raise events giving us location updates.

  5. In our code, let’s create a new class called LocationManager that provides a single place for various screens and code to subscribe to location updates. In the LocationManager class, make an instance of the CLLocationManager called LocMgr:

    public class LocationManager
    {
      protected CLLocationManager locMgr;
    
      public LocationManager (){
        this.locMgr = new CLLocationManager();
        this.locMgr.PausesLocationUpdatesAutomatically = false; 
    
        // iOS 8 has additional permissions requirements
        if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) {
          locMgr.RequestAlwaysAuthorization (); // works in background
          //locMgr.RequestWhenInUseAuthorization (); // only in foreground
        }
    
        if (UIDevice.CurrentDevice.CheckSystemVersion (9, 0)) {
           locMgr.AllowsBackgroundLocationUpdates = true;
        }
      }
    
      public CLLocationManager LocMgr{
        get { return this.locMgr; }
      }
    }
    

    We have also set a number of properties and permissions on the CLLocationManager:

    • PausesLocationUpdatesAutomatically – This is a Boolean that can be set depending on whether the system is allowed to pause location updates. On some device it defaults to true, which can cause the device to stop getting background location updates after about 15 minutes.
    • RequestAlwaysAuthorization - You should pass this method to give the app user the option to allow the location to be accessed in the background. RequestWhenInUseAuthorization can also be passed if you wish to give the user the option to allow the location to be accessed only when the app is in the foreground.
    • AllowsBackgroundLocationUpdates This is a Boolean property, introduced in iOS 9 that can be set to allow an app to receive location updates when suspended.

    iOS 8 also requires an entry in the Info.plist file to show the user as part of the authorization request.
    Add a key NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription with a string that will be displayed to the user in the alert that requests location data access.

    iOS 9 requires that when using AllowsBackgroundLocationUpdates the Info.plist includes the key UIBackgroundModes with the value location. If you have completed step 2 of this walkthrough, this should already been in your Info.plist file.

  6. Inside the LocationManager class, create a method called StartLocationUpdates with the following code. We will use this to set the update parameters and start receiving location updates from the CLLocationManager:
  7. if (CLLocationManager.LocationServicesEnabled) {
      //set the desired accuracy, in meters
      LocMgr.DesiredAccuracy = 1;
      LocMgr.LocationsUpdated += (object sender, CLLocationsUpdatedEventArgs e) =>
      {
          // fire our custom Location Updated event
          LocationUpdated (this, new LocationUpdatedEventArgs (e.Locations [e.Locations.Length - 1]));
      };
      LocMgr.StartUpdatingLocation();
    }
    

    There are several important things happening in this method. First, we perform a check to see if the application has access to location data on the device. We verify this by calling LocationServicesEnabled on the CLLocationManager. This method will return false if the user has denied the application access to location information.

    Next, we will tell the location manager how often to update. CLLocationManager provides many options for filtering and configuring location data, including the frequency of updates. In this example, we set the DesiredAccuracy to update whenever the location changes by a meter. For more information on configuring location update frequency and other preferences, refer to the CLLocationManager Class Reference in the Apple documentation.

    Finally, call StartUpdatingLocation on the CLLocationManager instance. This tells the location manager to get an initial fix on the current location, and to start sending updates

  8. So far, we’ve created a location manager, specified the kinds of data we want to receive, and have got an initial fix on our location. Now we need a way to get the location data to our View. We can do this with a custom event that takes a CLLocation as an argument:
  9. // event for the location changing
      public event EventHandler<LocationUpdatedEventArgs> LocationUpdated = delegate { };

    The next step is to subscribe to location updates from the CLLocationManager, and raise the custom LocationUpdated event when new location data becomes available, passing in the location as an argument. To do this, create a new class LocationUpdateEventArgs.cs. This code is accessible within the main application and returns the device location when the event is raised:

    public class LocationUpdatedEventArgs : EventArgs
    {
        CLLocation location;
    
        public LocationUpdatedEventArgs(CLLocation location)
        {
           this.location = location;
        }
    
        public CLLocation Location
        {
           get { return location; }
        }
    }
    

    User Interface

  10. Now we’re ready to build the screen that will display our location. Double-click on the Main.storyboard file to open them in the iOS Designer.
  11. In iOS Designer, drag several labels onto the screen to act as placeholders the location information we want to display. In our example, we’ve wired up labels for latitude, longitude, altitude, course, and speed.

    The layout should resemble the following:

  12. In the Solution Pad, double-click the ViewController.cs file and edit it to create a new instance of the LocationManager and call StartLocationUpdateson it. Change the code to look like the following:
  13. 
    #region Computed Properties
    public static bool UserInterfaceIdiomIsPhone {
                get { return UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone; }
            }
    
    public static LocationManager Manager { get; set;}
    #endregion
    
    #region Constructors
    public ViewController (IntPtr handle) : base (handle)
    {
    // As soon as the app is done launching, begin generating location updates in the location manager
        Manager = new LocationManager();
        Manager.StartLocationUpdates();
    }
    
    #endregion

    This will start the location updates on application start-up, although no data will be displayed. We’ll address this in the next step.

  14. Now that we have location updates, we can begin updating the screen with location data. The following method gets the location from our LocationUpdated event and prints it to the screen:
  15. #region Public Methods
    public void HandleLocationChanged (object sender, LocationUpdatedEventArgs e)
    {
        // Handle foreground updates
        CLLocation location = e.Location;
    
        LblAltitude.Text = location.Altitude + " meters";
        LblLongitude.Text = location.Coordinate.Longitude.ToString ();
        LblLatitude.Text = location.Coordinate.Latitude.ToString ();
        LblCourse.Text = location.Course.ToString ();
        LblSpeed.Text = location.Speed.ToString ();
    
        Console.WriteLine ("foreground updated");
    }
    
    #endregion

    We still need to subscribe to the LocationUpdated event in our AppDelegate, and call the new method to update the UI. We can do this in ViewDidLoad, right after the StartLocationUpdates call:

    public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();
    
        // It is better to handle this with notifications, so that the UI updates
        // resume when the application re-enters the foreground!
        Manager.LocationUpdated += HandleLocationChanged;
    
    }

    Now, if we run it, the application should look something like this:

    Handling Active and Background states

  16. The application is outputting location updates while it is Active; it’s time to see what happens when the app enters the background. Let’s start by overriding the AppDelegatemethods that track application state changes, so that the application can notify us when it transitions between the foreground and the background:
  17. public override void DidEnterBackground (UIApplication application)
    {
      Console.WriteLine ("App entering background state.");
    }
    
    public override void WillEnterForeground (UIApplication application)
    {
      Console.WriteLine ("App will enter foreground");
    }

    We will need to verify that location updates continue when the app is in the background. Add the following code in the LocationManager to continuously print updated location data to the application output:

    public class LocationManager
    {
      public LocationManager ()
      {
        ...
        LocationUpdated += PrintLocation;
      }
      ...
    
      //This will keep going in the background and the foreground
      public void PrintLocation (object sender, LocationUpdatedEventArgs e) {
        CLLocation location = e.Location;
        Console.WriteLine ("Altitude: " + location.Altitude + " meters");
        Console.WriteLine ("Longitude: " + location.Coordinate.Longitude);
        Console.WriteLine ("Latitude: " + location.Coordinate.Latitude);
        Console.WriteLine ("Course: " + location.Course);
        Console.WriteLine ("Speed: " + location.Speed);
      }
    }
  18. Our app works pretty well, but there’s one major issue. If we attempt to update the UI when the app is backgrounded, iOS will terminate it. So we need to make sure that when the app goes into the background, we unsubscribe to location updates in our screen and stop updating it.
  19. iOS provides us notifications when the app is about to transition to a different application states. In this case, we can subscribe to the ObserveDidEnterBackground Notification.

    The following code snippet shows how to use a Notification to let the View know when to halt UI updates. This will go in ViewDidLoad:

    UIApplication.Notifications.ObserveDidEnterBackground ((sender, args) => {
      Manager.LocationUpdated -= HandleLocationChanged;
    });

    When the app is running, the output will look something like this:

  20. The application prints location updates to the screen when operating in the foreground, and continues to print data to the application output window while operating in the background.
  21. Only one outstanding issue remains: our screen starts UI updates when the app is first loaded, but it has no way of knowing when the app has re-entered the foreground. If the backgrounded application is brought back into the foreground, UI updates won’t resume.

    To fix this, we nest our call to start UI updates inside another Notification, which will fire when the application enters the Active state:

    UIApplication.Notifications.ObserveDidBecomeActive ((sender, args) => {
      Manager.LocationUpdated += HandleLocationChanged;
    });

    Now the UI will begin updating when the application is first started, and resume updating any time the app comes back into the foreground.

In this walkthrough, we built a well-behaved, background-aware iOS application that prints location data to both the screen and the application output window.

Xamarin Workbook

If it's not already installed, install the Xamarin Workbooks app first. The workbook file should download automatically, but if it doesn't, just click to start the workbook download manually.