Skip to content
Document DB v12 - Improved Interceptors with Soft Delete Integration, AI protections, & Admin UI with Aspire Integration! How!?

Push (Server) Releases

Feature

Initial release — provider-agnostic server-side push core: IPushManager orchestrator, IPushProvider transport seam, IPushRepository persistence, IPushInterceptor pipeline, structured PushFilter targeting, rich PushNotification model, automatic dead-token pruning + token rotation, bounded-concurrency dispatch, and an in-memory repository + debug provider. AOT/trim-safe.

Feature

One package, four transports — APNs, FCM, Web Push and WNS all ship inside Shiny.Extensions.Push; there are no separate *.Apns / *.Fcm / *.WebPush / *.Wns packages. Each keeps its own namespace (Shiny.Extensions.Push.Apns / .Fcm / .WebPush / .Wns) and stays opt-in via AddApns / AddFcm / AddWebPush / AddWns, so you only pay for what you register. Shiny.Extensions.Push.DocumentDb stays a separate package (it pulls a third-party dependency). The implementation types you don’t inject directly (PushManager, InMemoryPushRepository, DebugPushProvider, PushMetrics) live in the Shiny.Extensions.Push.Infrastructure namespace — resolve the interfaces from the root namespace; you only need the Infrastructure import to name a concrete type, e.g. AddProvider<DebugPushProvider>().

Feature

Direct APNs provider — HTTP/2 with token-based .p8 / ES256 JWT auth (no FCM dependency), per-registration sandbox/production, cached provider JWT, pooled HTTP/2 connection, Apple-specific payload options, and multi-app keyed registration via AddApns("key", …) + AppId.

Feature

WNS / Windows provider — delivers to Windows devices via WNS using the modern Windows App SDK / Microsoft Entra (Azure AD) auth model (AddWns with TenantId / ClientId / ClientSecret; OAuth2 client-credentials token, cached). Sends a ToastGeneric toast by default (or a raw/tile/badge payload via WindowsPushOptions) to the channel URI, maps 410/404 to dead-token pruning and 406/429 to rate-limited, and supports multi-app keyed registration.

Feature

Shiny.DocumentDb persistence (Shiny.Extensions.Push.DocumentDb) — IPushRepository over any DocumentDb backend with O(1) token-keyed writes; SQLite integration-tested.

Feature

MetricsSystem.Diagnostics.Metrics counters (push.notifications.sent/.failed/.skipped, push.tokens.pruned) and a push.send.duration histogram under the meter Shiny.Extensions.Push, tagged by platform/provider/status. OpenTelemetry-ready.

Feature

Provider batching / FCM multicast — a new optional IPushBatchProvider capability lets a transport deliver one notification to many devices in a single call. FcmProvider implements it (up to 500 devices per multipart /batch request), so broadcasts and topic fan-out use far fewer round trips. Enabled by default (PushManagerOptions.EnableBatching); devices are grouped by the identical notification instance, so per-device localization falls back to per-device sends. Per-device pruning, token rotation, metrics, and OnSent/OnFailed are all preserved.

Feature

Runtime configuration provider (dynamic / multi-tenant) — beyond the static AddApns(o => …) registration, credentials can be supplied at send time through an IPushConfigurationProvider, keyed by DeviceRegistration.AppId, so apps and keys can be onboarded, offboarded, or rotated without an app restart. Register your provider once with UsePushConfiguration<T>() — it’s scoped, so it can depend on scoped services like an EF DbContext — and opt each transport in with its no-argument overload (AddApns() / AddFcm() / AddWebPush() / AddWns()). The library reuses each app’s minted APNs JWT / OAuth bearer on the transport’s normal token lifetime and re-reads your current config on refresh, so a rotated key is picked up within one window — no version bookkeeping. FCM batches are split per app. An unknown/unconfigured AppId returns a failed result ("app not configured for …") rather than delivering.

Feature

Event receivers — a new IPushEventReceiver observes the send lifecycle without altering it (telemetry, receipts, dead-letter capture, dashboards). Register zero or more with AddEventReceiver<T>(). Hooks: OnBatchStarted, per-device OnSent/OnFailed, and OnBatchFinished (with the aggregate PushSendResult). Unlike an IPushInterceptor it can’t skip or mutate; and OnFailed fires for every failed device — including the normalized failures that never throw (TokenExpired, InvalidToken, RateLimited) — carrying the full PushDeliveryResult. A receiver that throws is logged and swallowed, never breaking a batch.

Feature

Live Activities (ActivityKit)Apple.LiveActivity turns any push into a Live Activity update: the APNs transport switches to apns-push-type: liveactivity, the <bundle-id>.push-type.liveactivity topic, and an aps body of event/timestamp/content-state. LiveActivityPush.Start/Update/End build the three lifecycle pushes with their required fields enforced. Content state is typed via LiveActivityValue (implicit conversions for string/int/long/double/bool/DateTimeOffset, plus Array/Object/Json) because the widget’s Swift ContentState is a Codable struct — a number sent as a string silently fails to decode. Dates default to Swift’s reference epoch (2001-01-01), overridable with LiveActivityDateEncoding. Also supported: StaleDate, DismissalDate, RelevanceScore, alerting updates, and push-to-start’s RequestPushToken/InputPushChannel.

Feature

Live Activity token safetyDeviceRegistration.TokenKind records whether a token is an ordinary device token, a push-to-start token, or a per-activity update token, and PushFilter.TokenKind defaults to Device. Without this a plain Broadcast would target Live Activity tokens, APNs would reject them with DeviceTokenNotForTopic, and the manager would prune them as invalid. RegisterLiveActivityToken, SendLiveActivity (which infers the kind from the event) and SendLiveActivityToTokens are the entry points. One DeviceId can now hold a device token and its Live Activity tokens side by side.

Feature

APNs broadcast channels (iOS 18+)ApnsBroadcastClient creates, reads, lists and deletes broadcast channels and sends one Live Activity push to every subscriber, instead of one push per activity token. Resolved from DI keyed by the same app key as AddApns.