diff --git a/README.md b/README.md index 2674519..47711bd 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,33 @@ `Nalu.Maui` provides a set of classes to help you with everyday challenges encountered while working with .NET MAUI. +### Core [![Nalu.Maui.Core NuGet Package](https://img.shields.io/nuget/v/Nalu.Maui.Core.svg)](https://www.nuget.org/packages/Nalu.Maui.Core/) [![Nalu.Maui NuGet Package Downloads](https://img.shields.io/nuget/dt/Nalu.Maui.Core)](https://www.nuget.org/packages/Nalu.Maui.Core/) + +The core library is intended to provide a set of common use utilities. + +Have you ever noticed that when the user backgrounds the app on iOS, the app is suspended, and the network requests will fail due to `The network connection was lost`? + +This is really annoying: it forces us to implement complex retry logic, especially considering that the request may have already hit the server. + +To solve this issue, we provide a `NSUrlBackgroundSessionHttpMessageHandler` to be used in your `HttpClient` to allow http request to continue even when the app is in the background. + +```csharp +#if IOS + var client = new HttpClient(new NSUrlBackgroundSessionHttpMessageHandler()); +#else + var client = new HttpClient(); +#endif +``` + +To make this work, you need to change your `AppDelegate` as follows: +```csharp +[Export("application:handleEventsForBackgroundURLSession:completionHandler:")] +public virtual void HandleEventsForBackgroundUrl(UIApplication application, string sessionIdentifier, Action completionHandler) + => NSUrlBackgroundSessionHttpMessageHandler.HandleEventsForBackgroundUrl(application, sessionIdentifier, completionHandler); +``` + +**Check out the [Core Wiki](core.html) for more information**. + ### Navigation [![Nalu.Maui.Navigation NuGet Package](https://img.shields.io/nuget/v/Nalu.Maui.Navigation.svg)](https://www.nuget.org/packages/Nalu.Maui.Navigation/) [![Nalu.Maui NuGet Package Downloads](https://img.shields.io/nuget/dt/Nalu.Maui.Navigation)](https://www.nuget.org/packages/Nalu.Maui.Navigation/) The MVVM navigation service offers a straightforward and robust method for navigating between pages and passing parameters. diff --git a/Samples/Nalu.Maui.Sample/MauiProgram.cs b/Samples/Nalu.Maui.Sample/MauiProgram.cs index 8b63e43..a615f8e 100644 --- a/Samples/Nalu.Maui.Sample/MauiProgram.cs +++ b/Samples/Nalu.Maui.Sample/MauiProgram.cs @@ -64,10 +64,24 @@ public static MauiApp CreateMauiApp() #if DEBUG builder.Logging.AddDebug(); + builder.Logging.SetMinimumLevel(LogLevel.Debug); #endif builder.Services.AddTransientPopup(); + builder.Services.AddKeyedSingleton("dummyjson", (_, _) => + { +#if IOS + var client = new HttpClient(new NSUrlBackgroundSessionHttpMessageHandler()) +#else + var client = new HttpClient() +#endif + { + BaseAddress = new Uri("https://dummyjson.com/") + }; + return client; + }); + return builder.Build(); } } diff --git a/Samples/Nalu.Maui.Sample/PageModels/OnePageModel.cs b/Samples/Nalu.Maui.Sample/PageModels/OnePageModel.cs index 022084a..74bbb30 100644 --- a/Samples/Nalu.Maui.Sample/PageModels/OnePageModel.cs +++ b/Samples/Nalu.Maui.Sample/PageModels/OnePageModel.cs @@ -1,5 +1,6 @@ namespace Nalu.Maui.Sample.PageModels; +using System.Net.Http.Json; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; @@ -8,14 +9,81 @@ public class AnimalModel public string Name { get; set; } = null!; } -public partial class OnePageModel(INavigationService navigationService) : ObservableObject +public partial class OnePageModel( + INavigationService navigationService, + [FromKeyedServices("dummyjson")] HttpClient httpClient) : ObservableObject { private static int _instanceCount; - public AnimalModel Animal { get; } = new AnimalModel { Name = "Dog" }; + [ObservableProperty] + private string? _result1; + + [ObservableProperty] + private string? _result2; + + [ObservableProperty] + private string? _result3; + + [ObservableProperty] + private string? _result4; + + public AnimalModel Animal { get; } = new() { Name = "Dog" }; public int InstanceCount { get; } = Interlocked.Increment(ref _instanceCount); [RelayCommand(AllowConcurrentExecutions = false)] private Task PushThreeAsync() => navigationService.GoToAsync(Navigation.Relative().Push()); + + [RelayCommand] + private async Task SendRequestAsync() + { + Result1 = null; + Result2 = null; + Result3 = null; + Result4 = null; + + var parallelRequestTask1 = SendRequestAsync(CreateTestLongRequest(5000)); + Result1 = "waiting..."; + var parallelRequestTask2 = SendRequestAsync(CreateTestLongRequest(4000)); + Result2 = "waiting..."; + + Result1 = await parallelRequestTask1; + Result2 = await parallelRequestTask2; + + var followUpRequest = SendRequestAsync(CreateTestLongRequest(5000)); + Result3 = "waiting..."; + Result3 = await followUpRequest; + + var followUpRequest2 = SendRequestAsync(CreateTestLongRequest(999999)); + Result4 = "waiting..."; + Result4 = await followUpRequest2; + } + + private async Task SendRequestAsync(HttpRequestMessage requestMessage) + { + string result; + try + { + var responseMessage = await httpClient.SendAsync(requestMessage); + result = await responseMessage.Content.ReadAsStringAsync(); + } + catch (Exception e) + { + result = e.Message; + } + + return result; + } + + /// + /// Creates a request to an endpoint that will delay the response. + /// See https://dummyjson.com/docs#intro-test for more information. + /// + private HttpRequestMessage CreateTestLongRequest(int delayMs = 5000) => + new() + { + RequestUri = new Uri($"test?delay={delayMs}", UriKind.Relative), + Method = HttpMethod.Post, + Content = JsonContent.Create(Animal), + }; } diff --git a/Samples/Nalu.Maui.Sample/Pages/OnePage.xaml b/Samples/Nalu.Maui.Sample/Pages/OnePage.xaml index 8441e78..362c3c2 100644 --- a/Samples/Nalu.Maui.Sample/Pages/OnePage.xaml +++ b/Samples/Nalu.Maui.Sample/Pages/OnePage.xaml @@ -17,6 +17,46 @@ Aspect="AspectFit" Margin="0,16,0,24"/> +