Skip to content
Shiny Controls v1.0 - The Ultra Control Suite for .NET MAUI & BlazorO...M...G!

HTTP Transfers Releases

Feature
Transfer progress surfaces. New services.AddTransferProgress() mirrors background transfers onto whatever surface the platform offers - an iOS Live Activity, or the Android foreground-service notification - from one TransferProgressManager on every platform. It subscribes to UpdateReceived at startup, coalesces the progress firehose to one update a second, aggregates a batch into a single figure, and starts/updates/retires the surface, including when iOS relaunches the app in the background to finish a transfer. Nothing goes in your transfer delegate. What is shown is configured with the TransferProgressFields flags (file name, direction, percent, bytes, speed, time remaining, host) and TransferProgressShortStatus; wording is overridable through ITransferProgressDelegate. See Transfer Progress.
FixAndroid
Transfers no longer post two notifications. Android needs a foreground service to move bytes in the background, and that service must post a notification - but PerTransferNotificationStrategy posted its progress as a second notification, leaving the user with a progress entry alongside a redundant “Shiny service is continuing to transfer data in the background”. The new ForegroundNotificationRenderer re-posts the service’s own notification id instead, so there is exactly one.
EnhancementAndroid
The transfer notification is promoted to an Android 16 live update on API 36+ - Notification.ProgressStyle with requestPromotedOngoing and setShortCriticalText, which earns the status bar chip and always-on-display treatment. Android 8-15 degrades to an ordinary determinate progress bar.
EnhancementiOS
Progress can be emitted as a self-animating time range rather than a fraction (ProjectTimeRemaining, on by default). A background NSURLSession delivers no callbacks at all while the app is suspended, so a fraction-based bar freezes for most of a long transfer; a range keeps advancing with no app involvement, anchored in the past so the bar already sits at the true fraction rather than snapping to zero on every update. Falls back to a fraction when the transfer is stalled, paused, of unknown size, or the estimate exceeds MaximumProjection.
Feature
ITransferProgressRenderer is public: register your own and the same manager drives it, so you inherit the aggregation, coalescing and lifetime and implement only the drawing. TransferProgressContentBuilder is public and static too - FormatBytes, FormatRate, FormatDuration and FormatPercent are reusable in ordinary in-app progress UI.
BREAKINGChoreAndroid
PerTransferNotificationStrategy is now [Obsolete]. It still works, but it is the source of the duplicate notification above - replace it with services.AddTransferProgress().
FixAndroid
AddHttpTransfers<TDelegate>() now registers the platform HttpTransferManager on Android instead of the in-process HttpClientHttpTransferManager, so transfers run inside the dataSync foreground service (HttpTransferService) under OS ownership. This fixes two field issues: transfers stalling or being lost when the app is backgrounded or the process is killed (the service now redelivers its intent and re-arms whenever pending transfers exist), and a queue wedge where a transfer queued while the loop was draining stuck in Pending until the next Queue() call. Windows keeps the managed manager; iOS/Mac Catalyst are unchanged. Note: Android 14+ requires FOREGROUND_SERVICE_DATA_SYNC in the app manifest or the service start throws SecurityException.
EnhancementAndroid
New opt-in HttpTransferService.UseShortService = true promotes the Android foreground service as FOREGROUND_SERVICE_TYPE_SHORT_SERVICE (Android 14+) instead of dataSync. This drops the FOREGROUND_SERVICE_DATA_SYNC permission and the Google Play policy declaration dataSync requires, at the cost of a ~3-minute promotion window — a fit for burst-transfer apps and a bad fit for long downloads, hence opt-in. The default is unchanged, and Android 13-and-lower devices ignore the flag.
FixAndroid
HttpTransferService now implements Service.onTimeout (both the API 34 shortService overload and the API 35 typed one). Android 15 put dataSync on a ~6-hour daily budget delivered through the same callback; an unhandled timeout previously produced an ANR (“A foreground service of … did not stop within its timeout”). The service now logs and stops cleanly on timeout, leaving pending transfers in the repository so the existing machinery re-arms the service on the next Queue() or app start.
FixAndroid
Fixed a startup deadlock (Android ANR) that occurred when an app registered an IHttpTransferDelegate — such as one derived from the HttpTransferDelegate base class — that depends on IHttpTransferManager. The managed HttpClientHttpTransferProcess eagerly injected IEnumerable<IHttpTransferDelegate> in its constructor, forming a circular dependency (HttpClientHttpTransferManager → HttpClientHttpTransferProcess → delegates → IHttpTransferManager) that the DI container’s AddSingletonAsImplementedInterfaces 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 implementation), breaking the cycle. No public API changes.
Feature
IHttpTransferManager now exposes Pause(identifier) and Resume(identifier). Pause stops a transfer without cancelling it - the transfer stays queued and reports HttpTransferState.Paused. Downloads resume from where they left off (HTTP Range on the managed platforms, native NSUrlSessionTask.Suspend()/Resume() on iOS/Mac Catalyst). Uploads are stopped but not resumable, so resuming an upload restarts it from the beginning. A user-paused transfer is no longer auto-resumed on app relaunch.
FeatureBlazor
Pause/Resume on Blazor WASM is best-effort: a queued transfer is marked paused so the Service Worker drain skips it, and resume re-queues it. An already in-flight Service Worker fetch() cannot be aborted (it runs to completion), and downloads are not resumable (whole-Blob fetch), so a resumed download restarts.
Enhancement
Resumable downloads are confirmed across all managed platforms (Android, Windows, Linux, macOS, plain .NET) via HTTP Range (206 Partial ContentFileMode.Append); iOS resumes natively through the background NSUrlSession. Partial files are preserved on pause and only deleted on cancel.
Enhancement
The managed transfer loop now obtains its HttpClient from IHttpClientFactory via a named client (HttpClientHttpTransferProcess.HttpClientName = "Shiny.Net.Http") instead of constructing one directly - consistent with Shiny.Data.Sync. Apps can configure it (timeouts, default headers, primary handler, Polly, etc.) with services.AddHttpClient("Shiny.Net.Http").ConfigureHttpClient(...) after registering transfers. Applies to Android, Windows, Linux, macOS, and plain .NET (iOS/Mac Catalyst use NSUrlSession).
FixiOS
Background NSUrlSession is no longer nulled out when the in-flight transfer count reaches zero. Dropping the reference without invalidating could lead to a duplicate-identifier crash (A background URLSession with identifier X already exists!) the next time a transfer was queued, or silently swap the delegate instance the system held.
FixiOS
IHandleEventsForBackgroundUrl.Handle now compares the incoming sessionIdentifier against the manager’s session identifier (returning false on mismatch) and forces the NSUrlSession to materialize before storing the completion handler, so the delegate is wired up before the OS redelivers background events after a launch.
BREAKING
Rx removed. IHttpTransferManager no longer exposes WhenUpdateReceived(), WatchCount(), or any other IObservable<T> members. Subscribe to the new C# events UpdateReceived and CountChanged instead. WatchTransfer(identifier) now returns Task<HttpTransferResult> directly (await it, no Subscribe). The HttpClient.Upload/Download foreground helpers return Task and accept an Action<TransferProgress>? onProgress callback.
Enhancement
Non-iOS HTTP transfer processes consolidated into a single managed HttpClientHttpTransferProcess. Android, Windows, Linux, macOS, and plain .NET now share one driver; the platform-specific Android HttpTransferProcess and Windows HttpTransferProcess have been removed.
Feature
Plain .NET (base TFM) support — Shiny.Net.Http now ships a managed HttpClientHttpTransferManager for Linux, macOS, and any non-iOS/Android/Windows .NET host. Backed by HttpClient + IConnectivity, the loop wakes immediately on connectivity changes and supports resumable downloads via HTTP Range requests (206 Partial ContentFileMode.Append). Uploads always restart on failure. A default JSON filesystem repository is registered automatically so queued transfers survive process restarts. Cancelled downloads clean up partial files on disk. Register with services.AddHttpClientTransfers<TDelegate>(), and supply an IConnectivity implementation (e.g. AddConnectivity() from Shiny.Core.Linux or Shiny.Core.Blazor).
EnhancementAndroid
Background transfers now run on the same unified managed HttpClient + IConnectivity loop as the other non-iOS platforms (hosted inside the existing foreground service), bringing resumable downloads via HTTP Range requests to Android.
Feature
New Shiny.Net.Http.Blazor package — background HTTP transfers for Blazor WebAssembly using the Service Worker Background Sync API. Queued transfers are persisted to IndexedDB and drained by a Service Worker via fetch() even while the browser tab is closed (where supported). Download bodies are stored back in IndexedDB; the C# manager reconciles results and fires IHttpTransferDelegate callbacks when the tab reopens. Registered via services.AddBlazorHttpTransfers<TDelegate>(). Ship the bundled ./_content/Shiny.Net.Http.Blazor/http-transfer-sw.js SW or importScripts it from your own service worker. v1 limitations: downloads are not resumable (SW fetches return a whole Blob); upload bodies are base64-bridged through JS interop; Background Sync is Chromium-only (Firefox/Safari fall back to foreground drain).
Enhancement
RemoteFileName property on AzureBlobStorageUploadRequest allows overriding the uploaded file name
Enhancement
Azure Blob Storage URI now includes the file path
Enhancement
AWS S3 support added
Feature
Windows support added (No Background Support at this time)
Fix
HttpTransferMonitor now uses thread safe BindingList
FixAndroid
File uploads now check to make sure file exists before queuing and downloads directories are checked to ensure they exist before queuing
FixiOS
Send filenames with special characters properly and improved form data upload
Enhancement
Transfers can now be UploadMultipart, UploadRaw (body is raw bytes), or Download - this necessary for sending directly to Azure Blob Storage
Enhancement
AzureBlobStorageRequest.CreateForAzureBlobStorage static helper method
Enhancement
New HttpTransferDelegate allows you to set retries and detect denied authorization allowing you to refresh your token and issue a new request
EnhancementiOS
Allow fine tuned control of the nsurlsessionconfiguration and the mutable native request on iOS via implementing (& registering) INativeConfigurator
FixAndroid
Ensure HTTP transfer foreground service does start multiple times
FixAndroid
Check for the presence of FOREGROUND_SERVICE_DATA_SYNC on API 34
FixAndroid
More aggressive retrying of transfers in queue
FixAndroid
Appropriate amount of wizardary applied to remove foreground service notifications
Fix
Race condition when subscribed to ShinySubject based subjects
Enhancement
Rewritten API makes it easier than ever to monitor metrics of your transfers
EnhancementAndroid
Now supports persistent progress notifications
Enhancement
You can pass AppleHttpTransferRequest & AndroidHttpTransferRequest to the HttpTransferManager to customize the native request