Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for .NET Hot Reload #232

Merged
merged 23 commits into from
Oct 1, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
cc30e70
Add CommunityToolkitMetadataUpdateHandler
TheCodeTraveler Jun 11, 2023
f1831b3
Include `Type[]` in `ReloadApplication` event
TheCodeTraveler Jun 13, 2023
22f22bb
Add `HotReloadHandler`
TheCodeTraveler Jun 13, 2023
ae1f9de
`dotnet format`
TheCodeTraveler Jun 14, 2023
8059a5c
Merge branch 'main' into Add-C#-HotReload-Support
TheCodeTraveler Jun 27, 2023
0feaede
Change `Type[]` -> `IReadOnlyList<Type>`
TheCodeTraveler Jun 28, 2023
f683257
Update HotReloadHandler.cs
TheCodeTraveler Jun 28, 2023
691609b
Update HotReloadHandler.cs
TheCodeTraveler Jun 28, 2023
20571e0
Update CommunityToolkit.Maui.Markup.csproj
TheCodeTraveler Jun 28, 2023
b91d188
Update CommunityToolkitMetadataUpdateHandler.cs
TheCodeTraveler Jun 28, 2023
00744d6
Merge branch 'main' into Add-C#-HotReload-Support
TheCodeTraveler Jun 28, 2023
07910a5
Update samples/CommunityToolkit.Maui.Markup.Sample/HotReloadHandler.cs
TheCodeTraveler Jun 28, 2023
5a5f7af
Add `ICommunityToolkitHotReloadHandler.cs`
TheCodeTraveler Jun 28, 2023
18c9ba9
Add `Window` support
TheCodeTraveler Jun 30, 2023
4877e48
Replace `#if DEBUG` with `[Conditional("DEBUG")]`
TheCodeTraveler Jun 30, 2023
2da5231
Reference `IServiceProvider` via `Application.Current?.Handler.MauiCo…
TheCodeTraveler Jun 30, 2023
ffcd077
Goto correct shell route
TheCodeTraveler Jun 30, 2023
793364e
Leverage overloaded method
TheCodeTraveler Jun 30, 2023
438ea6a
Merge branch 'main' into Add-C#-HotReload-Support
TheCodeTraveler Jul 1, 2023
483733b
Merge branch 'main' into Add-C#-HotReload-Support
TheCodeTraveler Jul 6, 2023
af30db7
Merge branch 'main' into Add-C#-HotReload-Support
TheCodeTraveler Sep 26, 2023
04bac84
Change `Debug.WriteLine` -> `Trace.WriteLine`
TheCodeTraveler Oct 1, 2023
7c9f3a5
Add Null Coalescing Operator to `Handler`
TheCodeTraveler Oct 1, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions samples/CommunityToolkit.Maui.Markup.Sample/HotReloadHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System.Diagnostics.CodeAnalysis;

namespace CommunityToolkit.Maui.Markup.Sample;

class HotReloadHandler : ICommunityToolkitHotReloadHandler
{
public async void OnHotReload(IReadOnlyList<Type> types)
TheCodeTraveler marked this conversation as resolved.
Show resolved Hide resolved
{
if (Application.Current?.MainPage is not Page mainPage)
TheCodeTraveler marked this conversation as resolved.
Show resolved Hide resolved
{
return;
TheCodeTraveler marked this conversation as resolved.
Show resolved Hide resolved
}

foreach (var type in types)
{
if (type.IsSubclassOf(typeof(Page)))
{
if (mainPage is AppShell shell)
{
var visiblePage = shell.CurrentPage;

await mainPage.Dispatcher.DispatchAsync(async () =>
{
await Shell.Current.GoToAsync(type.Name, false);
TheCodeTraveler marked this conversation as resolved.
Show resolved Hide resolved
TheCodeTraveler marked this conversation as resolved.
Show resolved Hide resolved
Shell.Current.Navigation.RemovePage(visiblePage);
});

break;
}
else
{
if (TryGetModalStackPage(out var modalPage))
{
await mainPage.Dispatcher.DispatchAsync(async () =>
{
await mainPage.Navigation.PopModalAsync(false);
await mainPage.Navigation.PushModalAsync(modalPage, false);
TheCodeTraveler marked this conversation as resolved.
Show resolved Hide resolved
});
}
else
{
await mainPage.Dispatcher.DispatchAsync(async () =>
{
await mainPage.Navigation.PopAsync(false);
await mainPage.Navigation.PushAsync(modalPage, false);
});
}

break;
}
}
}
}


static bool TryGetModalStackPage([NotNullWhen(true)] out Page? page)
{
page = Application.Current?.MainPage?.Navigation.ModalStack.LastOrDefault();
return page is not null;
}
}
3 changes: 3 additions & 0 deletions samples/CommunityToolkit.Maui.Markup.Sample/MauiProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ public static MauiApp CreateMauiApp()
builder.Services.AddTransient<SettingsPage, SettingsViewModel>();
builder.Services.AddTransient<NewsDetailPage, NewsDetailViewModel>();

// C# Hot Reload Handler
builder.Services.AddSingleton<ICommunityToolkitHotReloadHandler, HotReloadHandler>();

return builder.Build();

static TimeSpan sleepDurationProvider(int attemptNumber) => TimeSpan.FromSeconds(Math.Pow(2, attemptNumber));
Expand Down
22 changes: 22 additions & 0 deletions src/CommunityToolkit.Maui.Markup/AppBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,35 @@
/// </summary>
public static class AppBuilderExtensions
{
static IServiceProvider serviceProvider =>
#if WINDOWS
MauiWinUIApplication.Current.Services;
#elif ANDROID
MauiApplication.Current.Services;
#elif IOS || MACCATALYST
MauiUIApplicationDelegate.Current.Services;
#elif TIZEN
throw new NotImplementedException();
TheCodeTraveler marked this conversation as resolved.
Show resolved Hide resolved
#else
throw new InvalidOperationException($"{nameof(IServiceProvider)} requires a platform specific implementation");
#endif

TheCodeTraveler marked this conversation as resolved.
Show resolved Hide resolved
/// <summary>
/// Initializes the .NET MAUI Community Toolkit Markup Library
/// </summary>
/// <param name="builder"><see cref="MauiAppBuilder"/> generated by <see cref="MauiApp"/> </param>
/// <returns><see cref="MauiAppBuilder"/> initialized for <see cref="CommunityToolkit.Maui.Markup"/></returns>
public static MauiAppBuilder UseMauiCommunityToolkitMarkup(this MauiAppBuilder builder)
{
#if DEBUG
TheCodeTraveler marked this conversation as resolved.
Show resolved Hide resolved
CommunityToolkitMetadataUpdateHandler.ReloadApplication += ReloadApplication;
#endif
return builder;
}

static void ReloadApplication(object? sender, IReadOnlyList<Type> e)
{
var hotReloadHandler = serviceProvider.GetService<ICommunityToolkitHotReloadHandler>();
hotReloadHandler?.OnHotReload(e);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFrameworks>net7.0;net7.0-android;net7.0-ios;net7.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net7.0-windows10.0.19041.0</TargetFrameworks>
<TargetFrameworks Condition="'$(IncludeTizenTargetFrameworks)' == 'true'">$(TargetFrameworks);net7.0-tizen</TargetFrameworks>
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#if DEBUG
[assembly: System.Reflection.Metadata.MetadataUpdateHandler(typeof(CommunityToolkit.Maui.Markup.CommunityToolkitMetadataUpdateHandler))]
namespace CommunityToolkit.Maui.Markup;

static class CommunityToolkitMetadataUpdateHandler
{
static readonly WeakEventManager reloadApplicationEventHandler = new();

public static event EventHandler<IReadOnlyList<Type>> ReloadApplication
{
add => reloadApplicationEventHandler.AddEventHandler(value);
remove => reloadApplicationEventHandler.RemoveEventHandler(value);
}

static void UpdateApplication(Type[]? types)
{
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
reloadApplicationEventHandler.HandleEvent(null, types?.ToList() ?? Enumerable.Empty<Type>(), nameof(ReloadApplication));
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
}
}
#endif

/// <summary>
/// Interface for handling C# Hot Reload requests
/// </summary>
public interface ICommunityToolkitHotReloadHandler
{
/// <summary>
/// Executes when C# Hot Reload is invoked
/// </summary>
/// <param name="types">A <see cref="IReadOnlyList{Type}"/> contianing the types that have been changed in code.</param>
void OnHotReload(IReadOnlyList<Type> types);
}