Skip to content
Welcome to our documentation!

GPS

  • GitHub stars for shinyorg/shiny
  • NuGet downloads for Shiny.Locations
  • NuGet downloads for Shiny.Locations.Blazor
Frameworks
.NET MAUI
Blazor
Operating Systems
Android
iOS
Windows
Web

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.

FeatureiOSAndroidWindowsBlazor (Web)
Foreground GPSFullFullFullFull
Background GPSFullFull (foreground service)Not SupportedNot Supported
GeofencingFullFullNot SupportedNot Supported
Shiny.LocationsNuGet package Shiny.Locations
Shiny.Hosting.MauiNuGet package Shiny.Hosting.Maui

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.

IGpsManager gpsManager; // injected, resolved, etc
await gpsManager.StartListener(new GpsRequest
{
UseBackground = true
});
gpsManager.StopListening();

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
}
}
#if ANDROID
public 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);
}
}
#endif

Next, let’s register that with your app builder/hosting/service collection

services.UseGps<MyGpsDelegate>();

Lastly, last start it up

IGpsManager gpsManager; // injected, resolved, etc
await gpsManager.StartListener(new GpsRequest
{
UseBackground = true
});

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 CLLocationUpdater stationary 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 ANDROID
var request = new AndroidGpsRequest(
BackgroundMode: GpsBackgroundMode.Realtime,
StationaryMetersThreshold: 15,
StationarySecondsThreshold: 60
);
#elif IOS
var request = new AppleGpsRequest(
BackgroundMode: GpsBackgroundMode.Realtime,
StationaryMetersThreshold: 15,
StationarySecondsThreshold: 60
);
#endif

Then check the reading in your delegate or observable:

gpsManager.WhenReading().Subscribe(reading =>
{
if (reading.IsStationary)
{
// user is stationary
}
});

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)
{
}
}
claude plugin marketplace add shinyorg/skills
claude plugin install shiny-client@shiny
copilot plugin marketplace add https://github.com/shinyorg/skills
copilot plugin install shiny-client@shiny
View shiny-client Plugin