name: shiny-jobs description: Background job scheduling and execution for .NET MAUI (iOS/Android native OS schedulers) and in-process jobs for plain .NET, Linux, macOS, and Blazor WASM using Shiny.Jobs auto_invoke: true triggers: - background job - job manager - scheduled job - periodic job - background task - job registration - IJobManager - IJob - JobRegistration - Shiny.Jobs - foreground job - job constraints - job scheduling
Shiny Jobs
Shiny.Jobs provides cross-platform background job scheduling and execution. On iOS and Android it uses the native OS schedulers (BGTaskScheduler / AndroidX WorkManager); on Windows it uses COM-activated background tasks. On plain .NET targets (Linux, macOS server, Blazor WASM, console, etc.) it runs an in-process managed JobManager driven by a recurring timer — jobs run only while the host process is alive; there is no OS-level scheduler on those targets. All platforms support the same constraints (network availability, charging, battery).
Blazor WASM caveat: background jobs only run while the tab is open and foregrounded. Service Worker / Periodic Background Sync cannot invoke C# because the SW has no access to the Blazor WASM runtime. For true background HTTP work on Blazor, use
Shiny.Net.Http.Blazor.
When to Use This Skill
- The user wants to schedule background work that runs periodically
- The user needs to run tasks when the app is not in the foreground
- The user asks about job constraints (network, charging, battery)
- The user wants to register or manage background jobs
- The user needs foreground job execution on a timer
- The user asks about
IJobManager,IJob,JobRegistration, or related types
Library Overview
| Item | Value |
|---|---|
| NuGet | Shiny.Jobs |
| Namespace | Shiny.Jobs (types); Shiny (registration extensions) |
| Platforms | iOS, Android, Windows (native OS); Linux, macOS, Blazor WASM, .NET base (in-process) |
Setup
Register jobs during service configuration. Each job is registered by its CLR type using AddJob<TJob> with optional fluent configuration. The job's Type is its unique identity — there is no separate string identifier.
using Shiny;
using Shiny.Jobs;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder.Services.AddJob<MySyncJob>(r => r
.WithForeground()
.WithInternet(InternetAccess.Any)
.WithBatteryNotLow()
);
return builder.Build();
}
}
AddJob<TJob> registers the infrastructure on first call (job manager + lifecycle task + battery/connectivity dependencies), then adds your job. Subsequent calls only add jobs.
iOS Setup
Add the following background task identifiers to your Info.plist under BGTaskSchedulerPermittedIdentifiers:
com.shiny.jobcom.shiny.jobpowercom.shiny.jobnetcom.shiny.jobpowernet
Also enable the processing background mode.
Android Setup
No additional manifest setup is required. Shiny.Jobs uses AndroidX WorkManager under the hood (minimum periodic interval: 15 minutes). If you want wake-lock support for RunTask and RunJob(runAsTask: true), add the WAKE_LOCK permission to your AndroidManifest.xml.
Windows Setup
Windows uses COM-activated in-process background tasks. Call ShinyJobsBackgroundTask.RegisterComServer() once in your App constructor before any trigger registration, and declare a matching windows.comServer extension in your appx manifest. Note: no background-mode triggering exists today on Windows — tasks fire while the process is running.
Plain .NET Setup (Linux, macOS, Blazor WASM, Console)
On the base .NET TFM there is no native OS scheduler — Shiny runs an in-process managed JobManager on a recurring timer (default 30s; configurable via the static JobManager.Interval property, minimum 15s, maximum 5 minutes). Jobs only execute while the host process is alive.
There is no separate Shiny.Jobs.Blazor package — reference Shiny.Jobs directly on all plain .NET targets (Blazor WASM included). You must register an IBattery and IConnectivity implementation; AddJob<TJob>(...) auto-registers them via AddConnectivity()/AddBattery() from the appropriate platform support package if present.
using Shiny;
using Shiny.Jobs;
// Linux / console — battery + connectivity come from Shiny.Core.Linux (via AddConnectivity/AddBattery)
// Blazor WASM — from Shiny.Support.DeviceMonitoring.Blazor
services.AddJob<MySyncJob>(r => r
.WithForeground()
.WithInternet(InternetAccess.Any)
);
Blazor WASM caveat: the in-process JobManager only runs while the tab is open. Background tabs are throttled (~1 min timer floor on Chromium), may be frozen after ~5 minutes, and iOS Safari kills background WASM aggressively. There is no way to run C# jobs via Service Worker Background Sync because the SW has no access to the WASM runtime. For background HTTP work specifically, use Shiny.Net.Http.Blazor.
Code Generation Instructions
When generating job-related code, follow these conventions:
- Implement
IJobdirectly for simple jobs, or inherit from theJobabstract base class for jobs that need a minimum interval between runs (MinimumTime). - The job's CLR type is its identity — do not invent a separate string identifier. Pass
typeof(TJob)toRunJob(...); iterateGetJobs()for the registered set. - Use constructor injection — job classes are resolved through DI. Inject dependencies through the constructor.
IJob.Runtakes onlyCancellationToken— there is noJobInfoargument. If a job needs configuration, inject it through the constructor or read from a settings store.- Honor the
CancellationToken— the platform may cancel a run on time-budget expiry; checkcancelToken.ThrowIfCancellationRequested()between work units. - Use
WithForeground()when the job should also execute periodically while the app is in the foreground (driven byJobLifecycleTask— platform builds only). - Use
WithInternet(InternetAccess.Any | Unmetered)to require connectivity before the job runs. - Use
WithCharging()/WithBatteryNotLow()for power-sensitive jobs — be aware these may delay a job indefinitely under hostile conditions. - Call
RequestAccess()(on the concreteJobManagerviaAbstractJobManager) before relying on background execution, so you can guide the user if a platform configuration is missing. - Register jobs at host build time via
services.AddJob<TJob>(...). There is no public runtime-register API onIJobManager.
Conventions
- Job class names should end with
Job(e.g.,DataSyncJob,CleanupJob). - Place job classes in a
Jobs/folder or namespace within the project. - Always handle exceptions inside
Rungracefully; unhandled exceptions are caught by the manager, logged, and surfaced via the returnedJobRunResult. - Job instances are resolved per run from the DI container — keep them scoped-safe (no long-lived per-job state outside DI).
Best Practices
- Check
RequestAccess()on startup and guide the user if background jobs are not available (AccessState.NotSetup,AccessState.Restricted,AccessState.Denied, etc.). - Keep jobs short-lived — background execution time is limited by the OS, especially on iOS where
BGProcessingTaskbudgets are short. - Use constraints wisely —
DeviceCharging = trueorBatteryNotLow = truemeans the job may not run for extended periods under hostile conditions. - Prefer
InternetAccess.AnyoverUnmeteredunless the job transfers large amounts of data. - Use the
Jobbase class withMinimumTimewhen the OS may invoke your job more often than necessary and you want a simple in-memory throttle. - Use
RunJob(typeof(TJob), runAsTask: true)for event-driven on-demand runs —runAsTask: truewraps the run in iOSBeginBackgroundTask/ Android partial wake-lock for extended execution. - Use
RunTask(name, work)for ad-hoc one-shot work that is not a registered job (still gets platform extended-execution wrapping on iOS/Android).