To avoid the afore-mentioned disadvantages, we will create an example without Xamarin.Mobile. While an Android app benefits from increased accuracy, speed and battery life, the iOS performance will be almost identical and the source code is similarly short.
The shared project
Within the shared App class we define a locationLabel. Currently, its Text contains a placeholder string. After determining the location, it will show the GPS coordinates.
static readonly Label locationLabel = new Label {
Text = "Determining your location...",
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand,
};
The constructor of App initializes the MainPage with just that locationLabel.
MainPage = new ContentPage { Content = locationLabel };
Now we define two public static methods to enable the shared code to display either the determined location or an error string. The first one, DisplayLocation, takes two double arguments and assigns a formatted string to the label text.
public static void DisplayLocation(double latitude, double longitude)
{
locationLabel.Text = string.Format("Latitude: {0}\nLongitude: {1}", latitude, longitude);
}The second method is for displaying a plain error string:
public static void DisplayError(string message)
{
locationLabel.Text = message;
}
The Android project
Before implementing a location-aware example app, we need to make a few preparations for the Android project:
- Add the Xamarin.GooglePlayServices.Location from nuget.org to the Android project. (Dependencies like the Android support libraries and other Xamarin.GooglePlayServices packages will be added automatically.)
- To avoid compiler errors on Android, you might need to set the target Android version to at least API 23 (Project Options → Build → Android Application → Target Android version) and the Java heap size to “1G” (Project Options → Android Build → Advanced → Java heap size).
- Add the permission “AccessFineLocation” (Project Options → Build → Android Application → Required permissions).
public class MainActivity : FormsApplicationActivity,
GoogleApiClient.IConnectionCallbacks,
GoogleApiClient.IOnConnectionFailedListener
The first interface is implemented with the two methods OnConnected and OnConnectionSuspended. When successfully connected to the location services, we get the last device location and – if not null – ask the App to display the result.
public void OnConnected(Bundle connectionHint)
{
var lastLocation = LocationServices.FusedLocationApi.GetLastLocation(googleApiClient);
if (lastLocation != null)
App.DisplayLocation(lastLocation.Latitude, lastLocation.Longitude);
}
When the connection gets suspended, we ask the App class to display an error. Here we could also display or even evaluate the cause. See the Google Play Services documentation7 for more information on error handling.
public void OnConnectionSuspended(int cause)
{
App.DisplayError("Couldn't determine position. Connection suspended.");
}
The second interface requires an implementation of OnConnectionFailed, which passes an error to the Appas well. Again, we could make use of the argument result to get more information about the connection.
public void OnConnectionFailed(ConnectionResult result)
{
App.DisplayError("Couldn't determine position. Connection failed.");
}
The OnConnected method above already uses the googleApiClient. It is defined as a member of MainActivity
GoogleApiClient googleApiClient;
and initialized within OnCreate somewhere before loading the application.
googleApiClient = new GoogleApiClient.Builder(this)
.AddConnectionCallbacks(this)
.AddOnConnectionFailedListener(this)
.AddApi(LocationServices.API)
.Build();
Finally, we only need to connect the client when starting the activity
protected override void OnStart()
{
base.OnStart();
googleApiClient.Connect();
}and disconnect it when stopping the activity:
protected override void OnStop()
{
googleApiClient.Disconnect();
base.OnStop();
}
The iOS project
On iOS we first need a locationManager, which is a member of the AppDelegate.
readonly CLLocationManager locationManager = new CLLocationManager();
Within FinishedLaunching we need to ask for permission to access the device location – if the iOS version is greater or equal than 8.0.
if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
locationManager.RequestWhenInUseAuthorization();
Since iOS 8 we need to add the following two items to the “Info.plist” file again:
...
<key>NSLocationAlwaysUsageDescription</key>
<string>Can we use your location</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>We are using your location</string>
</dict>
</plist>
Afterwards, we add an event handler for any location updates. If the list of locations is not empty, we read the last entry and pass its coordinates to DisplayLocation in the shared project.
locationManager.LocationsUpdated += (s, e) => {
if (e.Locations.Any()) {
var coordinate = e.Locations.Last().Coordinate;
App.DisplayLocation(coordinate.Latitude, coordinate.Longitude);
} else
App.DisplayError("Couldn't determine position");
};
Similarly to Android, we start the location updates when the app is activiated (i.e. the app gets into the foreground)
public override void OnActivated(UIApplication uiApplication)
{
base.OnActivated(uiApplication);
locationManager.StartUpdatingLocation();
}and stop them when the app is resigned (i.e. the app is closed or moved to the background)
public override void OnResignActivation(UIApplication uiApplication)
{
locationManager.StopUpdatingLocation();
base.OnResignActivation(uiApplication);
}