Shiny Controls v1.0 - The Ultra Control Suite for .NET MAUI & BlazorO...M...G!
Data Sync Releases
5.1.1 - July 1, 2026
Section titled “5.1.1 - July 1, 2026”FixAndroid
Fixed a startup deadlock (Android ANR) that occurred when an app registered an
IDataSyncDelegate that depends on IDataSyncManager. SyncInboxProcessor and the managed HttpClientDataSyncProcess eagerly injected IEnumerable<IDataSyncDelegate> in their constructors, and the singleton DataSyncManager / HttpClientDataSyncManager (registered via AddSingletonAsImplementedInterfaces) eagerly constructed those processors — forming a circular dependency (DataSyncManager → processor → delegates → IDataSyncManager) that the container’s factory forwarders hid from cycle detection, deadlocking the singleton root-cache lock during host build. Delegates are now resolved lazily from IServiceProvider at execution time (matching the Apple NSURLSession implementation), breaking the cycle. Affects Android and the cross-platform (Windows / Linux / macOS / base .NET / Blazor WASM) HttpClient path. No public API changes.5.0 — June 20, 2026
Section titled “5.0 — June 20, 2026”The library was redesigned from the ground up to match the platform-tier guarantees of Shiny.Net.Http. The new package is Shiny.Data.Sync (note the dot — Shiny.DataSync was the previous, retired SQLite-based prototype). The API and packaging are not compatible with the older version.
Feature
Outbox / inbox engine with platform-aware transports — iOS / Mac Catalyst on background
NSURLSession, Android on a foreground service, Windows / Linux / macOS / base .NET on HttpClient + connectivity loop, Blazor WASM on HttpClient + LocalStorageFeature
IDataSyncManager with Queue<T>, PullNow<T>, PullAll, GetPending, Cancel, CancelAll<T>, and CancelAll — operations are persisted per-app-launch and resume on relaunchFeature
ISyncEntity contract — a single Identifier property; no base classes, no attributes requiredFeature
Per-endpoint
SyncDirection: Both (default), PullOnly, PushOnlyFeature
Batch = true per endpoint — coalesces queued ops with last-write-wins semantics (Create + Update(s) → single Create with latest payload; trailing Delete wins) and POSTs a single /batch requestFeature
Pull scheduling via
MinPullInterval — TimeSpan.Zero (default) always pulls on scheduled passes, a positive value throttles, and null makes the endpoint manual only (scheduled pulls skip it; PullNow<T> always bypasses). Configurable cursor parameter (CursorParameter, default since)Feature
Overlapping pulls are coalesced per entity type — if a pull for a type is already in flight, a concurrent trigger (
SyncJob, connectivity restore, or PullNow<T>) is skipped rather than racing on the same cursorFeature
Per-verb URL overrides (
PullUrl, BatchUrl) and metered-connection gating (UseMeteredConnection)Feature
Exponential backoff with persisted
NextAttemptAt — MaxAttempts (default 5) and RetryBaseDelay (default 2s) per endpoint, capped at 60s, transient on 0 / 408 / 429 / 5xxFeature
Conflict resolution with
ConflictPolicy (AskDelegate / ServerWins / ClientWins) and delegate-driven ConflictResolution.AcceptRemote / KeepLocal / UseMergedFeature
Three removal strategies:
Verb = Delete items in the inbox, separate TombstoneUrl stream (string[] or { cursor, ids }), and client-side SoftDeletePredicate / ExpiryPredicateFeature
ISyncInterceptor for global request hooks (auth, signing, tracing) — runs before per-endpoint OnBeforeSend so endpoint logic still wins on header conflictsFeature
Unified
Activity event stream surfacing OutboxQueued / OutboxStarted / OutboxSent / OutboxFailed / OutboxConflict / OutboxRetryScheduled / OutboxCanceled / InboxPullStarted / InboxItemReceived / InboxPullCompleted / InboxPullFailed / TombstonesAppliedFeature
Typed events kept for fine-grained subscribers:
PendingCountChanged, UpdateReceived (per-op state transitions), PullCompleted (per-endpoint completion)Feature
AOT-safe JSON via
[ShinyJsonContext] source-generated module initializer wiring entities into the shared Shiny.Json.Default chain — no per-endpoint JsonTypeInfo plumbingFeature
SyncJob auto-registered with WithInternet(InternetAccess.Any) — periodic PullAll runs in the background without any manual AddJob registrationFeature
Named HttpClient
RestSyncTransport.HttpClientName ("Shiny.Data.Sync") — attach base address, Polly handlers, and signing handlers with IHttpClientFactoryFeature
Custom transports via
ISyncTransport — replace the default REST transport with gRPC, GraphQL, or anything elseFeature
Shiny.Data.Sync.Blazor for Blazor WebAssembly — LocalStorage-backed persistence and connectivity-driven HttpClient loop

