In a previous article, I talked about setting up Prism with Shiny in a Xamarin Forms project. Now I will extend on this by adding location tracking support, which is a very common feature in enterprise applications. This article will show you how to do it step by step using Shiny.
Let’s start!
1. Install the Shiny.Locations package
2-Create a new folder called Delegates and add GPSListener class/interface
using System; | |
using Shiny.Locations; | |
namespace ShinyPrismSample.Delegates | |
{ | |
public class GpsReadingEventArgs : EventArgs | |
{ | |
public IGpsReading Reading { get; } | |
public GpsReadingEventArgs(IGpsReading reading) | |
{ | |
Reading = reading; | |
} | |
} | |
public interface IGpsListener | |
{ | |
event EventHandler<GpsReadingEventArgs> OnReadingReceived; | |
} | |
} |
using System; | |
using System.Threading.Tasks; | |
using Shiny.Locations; | |
namespace ShinyPrismSample.Delegates | |
{ | |
public class GpsListener : IGpsListener | |
{ | |
public event EventHandler<GpsReadingEventArgs> OnReadingReceived; | |
void UpdateReading(IGpsReading reading) | |
{ | |
OnReadingReceived?.Invoke(this, new GpsReadingEventArgs(reading)); | |
} | |
public class LocationDelegate : IGpsDelegate | |
{ | |
IGpsListener _gpsListener; | |
public LocationDelegate(IGpsListener gpsListener) | |
{ | |
_gpsListener = gpsListener; | |
} | |
public Task OnReading(IGpsReading reading) | |
{ | |
(_gpsListener as GpsListener)?.UpdateReading(reading); | |
return Task.CompletedTask; | |
} | |
} | |
} | |
} |
This listener will handle our location updates since it has an event handler that gets invoked each time we get a new GPS reading.
3-In the ShinyAppStartup file register GPS listener as a singleton and also register Shiny GPS service.
using Microsoft.Extensions.DependencyInjection; | |
using Prism.DryIoc; | |
using Shiny; | |
using Shiny.Prism; | |
using ShinyPrismSample.Delegates; | |
using static ShinyPrismSample.Delegates.GpsListener; | |
namespace ShinyPrismSample | |
{ | |
public class ShinyAppStartup : PrismStartup | |
{ | |
public ShinyAppStartup(): base(PrismContainerExtension.Current) | |
{ | |
} | |
protected override void ConfigureServices(IServiceCollection services) | |
{ | |
services.AddSingleton<IGpsListener, GpsListener>(); | |
services.UseGps<LocationDelegate>(); | |
} | |
} | |
} |
4-In the ViewModel inject the IGpsManager and the IGpsListener.
The GPSManager has two main methods StartListener and StopListener which start/stop the GPS tracking. To gather location updates will subscribe to GpsListener event called OnReadingReceived that gets called whenever location has changed.
using System; | |
using System.ComponentModel; | |
using System.Diagnostics; | |
using Prism.Navigation; | |
using Shiny.Locations; | |
using ShinyPrismSample.Delegates; | |
namespace ShinyPrismSample.ViewModels | |
{ | |
public class MainPageViewModel : INavigatedAware, INotifyPropertyChanged, IDestructible | |
{ | |
IGpsListener _gpsListener; | |
IGpsManager _gpsManager; | |
public event PropertyChangedEventHandler PropertyChanged; | |
public string LocationMessage { get; set; } | |
public MainPageViewModel(IGpsManager gpsManager,IGpsListener gpsListener) | |
{ | |
_gpsManager = gpsManager; | |
_gpsListener = gpsListener; | |
_gpsListener.OnReadingReceived += OnReadingReceived; | |
} | |
void OnReadingReceived(object sender, GpsReadingEventArgs e) | |
{ | |
LocationMessage = $"{e.Reading.Position.Latitude}, {e.Reading.Position.Longitude}"; | |
Debug.WriteLine(LocationMessage); | |
} | |
public void OnNavigatedFrom(INavigationParameters parameters) | |
{ | |
} | |
public async void OnNavigatedTo(INavigationParameters parameters) | |
{ | |
if (_gpsManager.IsListening) | |
{ | |
await _gpsManager.StopListener(); | |
} | |
await _gpsManager.StartListener(new GpsRequest | |
{ | |
UseBackground = true, | |
Priority = GpsPriority.Highest, | |
Interval = TimeSpan.FromSeconds(5), | |
ThrottledInterval = TimeSpan.FromSeconds(3) //Should be lower than Interval | |
}); | |
} | |
public void Destroy() | |
{ | |
_gpsListener.OnReadingReceived -= OnReadingReceived; | |
} | |
} | |
} |
5-On iOS project Info.plist add the NSLocationAlwaysUsageDescription permission and enable the location updates background mode.
<key>NSLocationAlwaysUsageDescription</key> | |
<string>Your message</string> |

6-On Android project manifest file be sure to add the ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION permissions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.crossgeeks.shinyprismsample">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
<application android:label="ShinyPrismSample.Android"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
</manifest>
<?xml version="1.0" encoding="utf-8"?> | |
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.crossgeeks.shinyprismsample"> | |
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" /> | |
<application android:label="ShinyPrismSample.Android"></application> | |
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> | |
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> | |
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> | |
</manifest> |
Check the full source code here.
Happy Shining!!