GPS
Getting Started
Section titled “Getting Started”Shiny makes realtime background location updates easy on MAUI. A Blazor WebAssembly
implementation is also available via Shiny.Locations.Blazor for foreground GPS in
the browser.
Platform Notes
Section titled “Platform Notes”| Feature | iOS | Android | Windows | Blazor (Web) |
|---|---|---|---|---|
| Foreground GPS | Full | Full | Full | Full |
| Background GPS | Full | Full (foreground service) | Not Supported | Not Supported |
| Geofencing | Full | Full | Not Supported | Not Supported |
Blazor WebAssembly Setup
Section titled “Blazor WebAssembly Setup”Install Shiny.Locations.Blazor and register it in your Program.cs:
using Shiny;
var builder = WebAssemblyHostBuilder.CreateDefault(args);// ...builder.Services.AddGps();// or with a (foreground-only) delegate:builder.Services.AddGps<MyGpsDelegate>();Then inject IGpsManager into your component just like on MAUI:
@inject IGpsManager GpsService
@code { protected override async Task OnInitializedAsync() { var status = await GpsService.RequestAccess(GpsRequest.Foreground); if (status == AccessState.Available) await GpsService.StartListener(GpsRequest.Foreground); }}The first call to RequestAccess will trigger the browser’s permission prompt.
A working sample lives at samples/Sample.Blazor/Pages/Gps.razor.
Starting/Stopping the GPS Service
Section titled “Starting/Stopping the GPS Service”IGpsManager gpsManager; // injected, resolved, etcawait gpsManager.StartListener(new GpsRequest{ UseBackground = true});
gpsManager.StopListening();Observing in the background
Section titled “Observing in the background”First, create the delegate that implements IGpsDelegate
public partial class MyGpsDelegate : Shiny.Locations.IGpsDelegate{ public MyGpsDelegate() { // like all other shiny delegates, dependency injection works here // treat this as a singleton }
public Task OnReading(IGpsReading reading) { // do something with the reading }}Controlling GPS Notification on Android
Section titled “Controlling GPS Notification on Android”#if ANDROIDpublic partial class MyGpsDelegate : Shiny.IAndroidForegroundServiceDelegate{ public void ConfigureNotification(AndroidX.Core.App.NotificationCompat.Builder builder) { builder .SetContentTitle("MyApp") .SetContentText("My App is following you!! images") .SetSmallIcon(Resource.Mipmap.youricon); }}#endifNext, let’s register that with your app builder/hosting/service collection
services.UseGps<MyGpsDelegate>();Lastly, last start it up
IGpsManager gpsManager; // injected, resolved, etcawait gpsManager.StartListener(new GpsRequest{ UseBackground = true});Stationary Detection
Section titled “Stationary Detection”All GPS providers automatically detect when the user is stationary and set GpsReading.IsStationary on each reading before it reaches your delegate or observable stream.
- iOS 18+ uses native
CLLocationUpdaterstationary detection provided by the OS. - iOS legacy and Android use a distance + time threshold algorithm: if the user moves less than a configurable number of meters within a configurable number of seconds, they are marked stationary.
The default thresholds are 10 meters and 30 seconds. You can configure these via the platform-specific request types:
#if ANDROIDvar request = new AndroidGpsRequest( BackgroundMode: GpsBackgroundMode.Realtime, StationaryMetersThreshold: 15, StationarySecondsThreshold: 60);#elif IOSvar request = new AppleGpsRequest( BackgroundMode: GpsBackgroundMode.Realtime, StationaryMetersThreshold: 15, StationarySecondsThreshold: 60);#endifThen check the reading in your delegate or observable:
gpsManager.WhenReading().Subscribe(reading =>{ if (reading.IsStationary) { // user is stationary }});Easier Delegate
Section titled “Easier Delegate”The platform mechanics don’t always play by the rules for time & distance filters that you may expect especially with a more ‘real time’ setup.
To work around these issues, you can use the GpsDelegate class which will handle the filtering for you.
public class MyGpsDelegate : GpsDelegate{ public MyGpsDelegate(ILogger<MyGpsDelegate> logger) : base(logger) { // if either of these filters pass, the OnGpsReading method will be called this.MinimumDistance = Distance.FromMeters(200); this.MinimumTime = TimeSpan.FromMinutes(1); }
protected override async Task OnGpsReading(GpsReading reading) {
}}AI Coding Assistant
Section titled “AI Coding Assistant”Step 1 — Add the marketplace:
claude plugin marketplace add shinyorg/skills Step 2 — Install the plugin:
claude plugin install shiny-client@shiny Step 1 — Add the marketplace:
copilot plugin marketplace add https://github.com/shinyorg/skills Step 2 — Install the plugin:
copilot plugin install shiny-client@shiny