diff --git a/.gitignore b/.gitignore
index 39f97d76..211222aa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,11 +9,17 @@ local.settings.json
# Integration test secrets
appsettings.Secrets.json
+.nuspec/
+.buildtasks/
+templatesTest/
+
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
+/src/Controls/tests/TestCases.HostApp/MauiProgram.user.cs
+SandboxAppium/
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
@@ -294,3 +300,42 @@ __pycache__/
*.btm.cs
*.odx.cs
*.xsd.cs
+
+# VS Code files for those working on multiple tools
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+!.vscode/*.code-snippets
+
+# Local History for Visual Studio Code
+.history/
+
+# Built Visual Studio Code Extensions
+*.vsix
+
+# Windows Installer files from build outputs
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Android Studio
+.gradle/
+.idea/
+local.properties
+
+# Directory Build overrides for local setups
+Directory.Build.Override.props
+
+# Only the "snapshots" directory should be added to Git, not the "snapshots-diff" directory
+snapshots-diff/
+
+#install of dotnet version
+/.dotnet
+.dotnet
+temp
+.packages
+/src/Templates/.tempTemplateOutput
diff --git a/Microsoft.Extensions.Configuration.AzureAppConfiguration.sln b/Microsoft.Extensions.Configuration.AzureAppConfiguration.sln
index f2ce3e8a..2d245c14 100644
--- a/Microsoft.Extensions.Configuration.AzureAppConfiguration.sln
+++ b/Microsoft.Extensions.Configuration.AzureAppConfiguration.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.3.32503.460
+# Visual Studio Version 18
+VisualStudioVersion = 18.0.11205.157
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.AzureAppConfiguration", "src\Microsoft.Extensions.Configuration.AzureAppConfiguration\Microsoft.Extensions.Configuration.AzureAppConfiguration.csproj", "{7B793D44-EC46-4C12-B71F-3A5005290C75}"
EndProject
@@ -26,6 +26,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Properties", "Properties",
AzureAppConfigurationRules.ruleset = AzureAppConfigurationRules.ruleset
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MauiAppConfigDemo", "examples\MauiAppConfigDemo\MauiAppConfigDemo.csproj", "{A9301C34-C40D-ECFA-7F7A-DF6D5CD72FDB}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -68,6 +70,10 @@ Global
{A6C611F1-D687-4262-8904-828C239CF2E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A6C611F1-D687-4262-8904-828C239CF2E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A6C611F1-D687-4262-8904-828C239CF2E5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A9301C34-C40D-ECFA-7F7A-DF6D5CD72FDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A9301C34-C40D-ECFA-7F7A-DF6D5CD72FDB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A9301C34-C40D-ECFA-7F7A-DF6D5CD72FDB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A9301C34-C40D-ECFA-7F7A-DF6D5CD72FDB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/examples/MauiAppConfigDemo/App.xaml b/examples/MauiAppConfigDemo/App.xaml
new file mode 100644
index 00000000..4f7602f6
--- /dev/null
+++ b/examples/MauiAppConfigDemo/App.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/MauiAppConfigDemo/App.xaml.cs b/examples/MauiAppConfigDemo/App.xaml.cs
new file mode 100644
index 00000000..79c7cbff
--- /dev/null
+++ b/examples/MauiAppConfigDemo/App.xaml.cs
@@ -0,0 +1,16 @@
+using MauiAppConfigDemo.Services;
+
+namespace MauiAppConfigDemo;
+
+public partial class App : Application
+{
+ public App()
+ {
+ InitializeComponent();
+ }
+
+ protected override Window CreateWindow(IActivationState? activationState)
+ {
+ return new Window(new AppShell());
+ }
+}
diff --git a/examples/MauiAppConfigDemo/AppShell.xaml b/examples/MauiAppConfigDemo/AppShell.xaml
new file mode 100644
index 00000000..28fb9a32
--- /dev/null
+++ b/examples/MauiAppConfigDemo/AppShell.xaml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
diff --git a/examples/MauiAppConfigDemo/AppShell.xaml.cs b/examples/MauiAppConfigDemo/AppShell.xaml.cs
new file mode 100644
index 00000000..e25538e1
--- /dev/null
+++ b/examples/MauiAppConfigDemo/AppShell.xaml.cs
@@ -0,0 +1,9 @@
+namespace MauiAppConfigDemo;
+
+public partial class AppShell : Shell
+{
+ public AppShell()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/examples/MauiAppConfigDemo/GlobalXmlns.cs b/examples/MauiAppConfigDemo/GlobalXmlns.cs
new file mode 100644
index 00000000..b2f85eab
--- /dev/null
+++ b/examples/MauiAppConfigDemo/GlobalXmlns.cs
@@ -0,0 +1,2 @@
+[assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/maui/global", "MauiAppConfigDemo")]
+[assembly: XmlnsDefinition("http://schemas.microsoft.com/dotnet/maui/global", "MauiAppConfigDemo.Pages")]
diff --git a/examples/MauiAppConfigDemo/MainPage.xaml b/examples/MauiAppConfigDemo/MainPage.xaml
new file mode 100644
index 00000000..af96bbd0
--- /dev/null
+++ b/examples/MauiAppConfigDemo/MainPage.xaml
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/MauiAppConfigDemo/MainPage.xaml.cs b/examples/MauiAppConfigDemo/MainPage.xaml.cs
new file mode 100644
index 00000000..1963e382
--- /dev/null
+++ b/examples/MauiAppConfigDemo/MainPage.xaml.cs
@@ -0,0 +1,65 @@
+using MauiAppConfigDemo.Services;
+
+namespace MauiAppConfigDemo;
+
+public partial class MainPage : ContentPage
+{
+ private readonly ConfigurationService _configService;
+
+ public MainPage(ConfigurationService configService)
+ {
+ InitializeComponent();
+ _configService = configService;
+ LoadConfiguration();
+ }
+
+ protected override void OnAppearing()
+ {
+ base.OnAppearing();
+
+ // Trigger configuration refresh when page appears
+ Task.Run(async () =>
+ {
+ await _configService.RefreshConfigurationAsync();
+ MainThread.BeginInvokeOnMainThread(() => LoadConfiguration());
+ });
+ }
+
+ private async void LoadConfiguration()
+ {
+ // Get all configuration settings
+ var settings = _configService.GetSettings();
+
+ // Check feature flags
+ var isPromotionEnabled = await _configService.IsPromotionEnabledAsync();
+ var isHotelEnabled = await _configService.IsHotelBookingEnabledAsync();
+
+ // Display welcome message
+ WelcomeLabel.Text = settings.WelcomeMessage;
+
+ // Configure promotional banner (using feature flag)
+ PromotionalBanner.IsVisible = isPromotionEnabled && !string.IsNullOrEmpty(settings.PromotionText);
+ if (PromotionalBanner.IsVisible)
+ {
+ PromotionText.Text = settings.PromotionText;
+ }
+
+ // Show/hide hotel booking based on feature flag
+ HotelBookingCard.IsVisible = isHotelEnabled;
+
+ // Show app info
+ AppInfoLabel.Text = $"v{settings.AppVersion} | {settings.ApiUrl}";
+ }
+
+ private async void OnHotelBookingTapped(object? sender, EventArgs e)
+ {
+ await DisplayAlert("Hotel Booking", "Hotel booking feature - powered by Azure App Configuration!", "OK");
+ }
+
+ private async void OnRefreshing(object? sender, EventArgs e)
+ {
+ await _configService.RefreshConfigurationAsync();
+ LoadConfiguration();
+ RefreshView.IsRefreshing = false;
+ }
+}
diff --git a/examples/MauiAppConfigDemo/MauiAppConfigDemo.csproj b/examples/MauiAppConfigDemo/MauiAppConfigDemo.csproj
new file mode 100644
index 00000000..7ed0bcf7
--- /dev/null
+++ b/examples/MauiAppConfigDemo/MauiAppConfigDemo.csproj
@@ -0,0 +1,76 @@
+
+
+
+ net9.0-android;net9.0-ios;net9.0-maccatalyst
+ $(TargetFrameworks);net9.0-windows10.0.19041.0
+
+
+
+
+
+
+ Exe
+ MauiAppConfigDemo
+ true
+ true
+ enable
+ enable
+
+
+ MauiAppConfigDemo
+
+
+ com.companyname.mauiappconfigdemo
+
+
+ 1.0
+ 1
+
+
+ None
+
+ 15.0
+ 15.0
+ 21.0
+ 10.0.17763.0
+ 10.0.17763.0
+ 6.5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/MauiAppConfigDemo/MauiAppConfigDemo.sln b/examples/MauiAppConfigDemo/MauiAppConfigDemo.sln
new file mode 100644
index 00000000..0cbdab5f
--- /dev/null
+++ b/examples/MauiAppConfigDemo/MauiAppConfigDemo.sln
@@ -0,0 +1,24 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.2.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MauiAppConfigDemo", "MauiAppConfigDemo.csproj", "{D247C551-248B-F28B-4759-309E9CB85EC3}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D247C551-248B-F28B-4759-309E9CB85EC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D247C551-248B-F28B-4759-309E9CB85EC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D247C551-248B-F28B-4759-309E9CB85EC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D247C551-248B-F28B-4759-309E9CB85EC3}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {E77D804B-C8DE-4155-9C26-1BCD7E440CDF}
+ EndGlobalSection
+EndGlobal
diff --git a/examples/MauiAppConfigDemo/MauiProgram.cs b/examples/MauiAppConfigDemo/MauiProgram.cs
new file mode 100644
index 00000000..d51b0c8f
--- /dev/null
+++ b/examples/MauiAppConfigDemo/MauiProgram.cs
@@ -0,0 +1,51 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+using Microsoft.FeatureManagement;
+using MauiAppConfigDemo.Services;
+
+namespace MauiAppConfigDemo;
+
+public static class MauiProgram
+{
+ public static MauiApp CreateMauiApp()
+ {
+ var builder = MauiApp.CreateBuilder();
+ builder
+ .UseMauiApp()
+ .ConfigureFonts(fonts =>
+ {
+ fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
+ fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
+ });
+
+ var afdEndpoint = new Uri("https://YOUR-AFD-ENDPOINT.azurefd.net");
+
+ builder.Configuration.AddAzureAppConfiguration(options =>
+ {
+ options.ConnectAzureFrontDoor(afdEndpoint)
+ .SelectSnapshot("TravelAppSnapshot")
+ .Select("TravelApp:*")
+ .UseFeatureFlags(featureFlagOptions =>
+ {
+ featureFlagOptions.Select("TravelApp.*")
+ .SetRefreshInterval(TimeSpan.FromMinutes(1));
+ })
+ .ConfigureRefresh(refreshOptions =>
+ {
+ refreshOptions.RegisterAll()
+ .SetRefreshInterval(TimeSpan.FromMinutes(1));
+ });
+ });
+
+ builder.Services.AddAzureAppConfiguration();
+ builder.Services.AddSingleton();
+ builder.Services.AddFeatureManagement();
+ builder.Services.AddTransient();
+
+#if DEBUG
+ builder.Logging.AddDebug();
+#endif
+
+ return builder.Build();
+ }
+}
diff --git a/examples/MauiAppConfigDemo/Models/AppSettings.cs b/examples/MauiAppConfigDemo/Models/AppSettings.cs
new file mode 100644
index 00000000..e27b574f
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Models/AppSettings.cs
@@ -0,0 +1,15 @@
+namespace MauiAppConfigDemo.Models;
+
+///
+/// Application settings loaded from Azure App Configuration.
+///
+public class AppSettings
+{
+ public string ApiUrl { get; set; } = "Unable to load API URL from Azure App Configuration";
+
+ public string AppVersion { get; set; } = "Unable to load App Version from Azure App Configuration";
+
+ public string WelcomeMessage { get; set; } = "Travel Booking configuration failed to load from Azure App Configuration.";
+
+ public string PromotionText { get; set; } = string.Empty;
+}
diff --git a/examples/MauiAppConfigDemo/Platforms/Android/AndroidManifest.xml b/examples/MauiAppConfigDemo/Platforms/Android/AndroidManifest.xml
new file mode 100644
index 00000000..e9937ad7
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Platforms/Android/AndroidManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/MauiAppConfigDemo/Platforms/Android/MainActivity.cs b/examples/MauiAppConfigDemo/Platforms/Android/MainActivity.cs
new file mode 100644
index 00000000..17d61d0e
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Platforms/Android/MainActivity.cs
@@ -0,0 +1,10 @@
+using Android.App;
+using Android.Content.PM;
+using Android.OS;
+
+namespace MauiAppConfigDemo;
+
+[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
+public class MainActivity : MauiAppCompatActivity
+{
+}
diff --git a/examples/MauiAppConfigDemo/Platforms/Android/MainApplication.cs b/examples/MauiAppConfigDemo/Platforms/Android/MainApplication.cs
new file mode 100644
index 00000000..2bb663fa
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Platforms/Android/MainApplication.cs
@@ -0,0 +1,15 @@
+using Android.App;
+using Android.Runtime;
+
+namespace MauiAppConfigDemo;
+
+[Application]
+public class MainApplication : MauiApplication
+{
+ public MainApplication(IntPtr handle, JniHandleOwnership ownership)
+ : base(handle, ownership)
+ {
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
diff --git a/examples/MauiAppConfigDemo/Platforms/Android/Resources/values/colors.xml b/examples/MauiAppConfigDemo/Platforms/Android/Resources/values/colors.xml
new file mode 100644
index 00000000..c04d7492
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Platforms/Android/Resources/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #512BD4
+ #2B0B98
+ #2B0B98
+
\ No newline at end of file
diff --git a/examples/MauiAppConfigDemo/Platforms/MacCatalyst/AppDelegate.cs b/examples/MauiAppConfigDemo/Platforms/MacCatalyst/AppDelegate.cs
new file mode 100644
index 00000000..f41963c1
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Platforms/MacCatalyst/AppDelegate.cs
@@ -0,0 +1,9 @@
+using Foundation;
+
+namespace MauiAppConfigDemo;
+
+[Register("AppDelegate")]
+public class AppDelegate : MauiUIApplicationDelegate
+{
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
diff --git a/examples/MauiAppConfigDemo/Platforms/MacCatalyst/Entitlements.plist b/examples/MauiAppConfigDemo/Platforms/MacCatalyst/Entitlements.plist
new file mode 100644
index 00000000..de4adc94
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Platforms/MacCatalyst/Entitlements.plist
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ com.apple.security.app-sandbox
+
+
+ com.apple.security.network.client
+
+
+
+
diff --git a/examples/MauiAppConfigDemo/Platforms/MacCatalyst/Info.plist b/examples/MauiAppConfigDemo/Platforms/MacCatalyst/Info.plist
new file mode 100644
index 00000000..72689771
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Platforms/MacCatalyst/Info.plist
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UIDeviceFamily
+
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/examples/MauiAppConfigDemo/Platforms/MacCatalyst/Program.cs b/examples/MauiAppConfigDemo/Platforms/MacCatalyst/Program.cs
new file mode 100644
index 00000000..1c68cfd2
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Platforms/MacCatalyst/Program.cs
@@ -0,0 +1,15 @@
+using ObjCRuntime;
+using UIKit;
+
+namespace MauiAppConfigDemo;
+
+public class Program
+{
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+}
diff --git a/examples/MauiAppConfigDemo/Platforms/Tizen/Main.cs b/examples/MauiAppConfigDemo/Platforms/Tizen/Main.cs
new file mode 100644
index 00000000..4eccc1e3
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Platforms/Tizen/Main.cs
@@ -0,0 +1,16 @@
+using System;
+using Microsoft.Maui;
+using Microsoft.Maui.Hosting;
+
+namespace MauiAppConfigDemo;
+
+class Program : MauiApplication
+{
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+
+ static void Main(string[] args)
+ {
+ var app = new Program();
+ app.Run(args);
+ }
+}
diff --git a/examples/MauiAppConfigDemo/Platforms/Tizen/tizen-manifest.xml b/examples/MauiAppConfigDemo/Platforms/Tizen/tizen-manifest.xml
new file mode 100644
index 00000000..f7236f02
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Platforms/Tizen/tizen-manifest.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+ maui-appicon-placeholder
+
+
+
+
+ http://tizen.org/privilege/internet
+
+
+
+
\ No newline at end of file
diff --git a/examples/MauiAppConfigDemo/Platforms/Windows/App.xaml b/examples/MauiAppConfigDemo/Platforms/Windows/App.xaml
new file mode 100644
index 00000000..c4661445
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Platforms/Windows/App.xaml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/examples/MauiAppConfigDemo/Platforms/Windows/App.xaml.cs b/examples/MauiAppConfigDemo/Platforms/Windows/App.xaml.cs
new file mode 100644
index 00000000..b2236726
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Platforms/Windows/App.xaml.cs
@@ -0,0 +1,24 @@
+using Microsoft.UI.Xaml;
+
+// To learn more about WinUI, the WinUI project structure,
+// and more about our project templates, see: http://aka.ms/winui-project-info.
+
+namespace MauiAppConfigDemo.WinUI;
+
+///
+/// Provides application-specific behavior to supplement the default Application class.
+///
+public partial class App : MauiWinUIApplication
+{
+ ///
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
+ ///
+ public App()
+ {
+ this.InitializeComponent();
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
+
diff --git a/examples/MauiAppConfigDemo/Platforms/Windows/Package.appxmanifest b/examples/MauiAppConfigDemo/Platforms/Windows/Package.appxmanifest
new file mode 100644
index 00000000..7797c3ed
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Platforms/Windows/Package.appxmanifest
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+ $placeholder$
+ User Name
+ $placeholder$.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/MauiAppConfigDemo/Platforms/Windows/app.manifest b/examples/MauiAppConfigDemo/Platforms/Windows/app.manifest
new file mode 100644
index 00000000..a94cbf11
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Platforms/Windows/app.manifest
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+ true/PM
+ PerMonitorV2, PerMonitor
+
+
+
diff --git a/examples/MauiAppConfigDemo/Platforms/iOS/AppDelegate.cs b/examples/MauiAppConfigDemo/Platforms/iOS/AppDelegate.cs
new file mode 100644
index 00000000..f41963c1
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Platforms/iOS/AppDelegate.cs
@@ -0,0 +1,9 @@
+using Foundation;
+
+namespace MauiAppConfigDemo;
+
+[Register("AppDelegate")]
+public class AppDelegate : MauiUIApplicationDelegate
+{
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
diff --git a/examples/MauiAppConfigDemo/Platforms/iOS/Info.plist b/examples/MauiAppConfigDemo/Platforms/iOS/Info.plist
new file mode 100644
index 00000000..0004a4fd
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Platforms/iOS/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ LSRequiresIPhoneOS
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/examples/MauiAppConfigDemo/Platforms/iOS/Program.cs b/examples/MauiAppConfigDemo/Platforms/iOS/Program.cs
new file mode 100644
index 00000000..1c68cfd2
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Platforms/iOS/Program.cs
@@ -0,0 +1,15 @@
+using ObjCRuntime;
+using UIKit;
+
+namespace MauiAppConfigDemo;
+
+public class Program
+{
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+}
diff --git a/examples/MauiAppConfigDemo/Platforms/iOS/Resources/PrivacyInfo.xcprivacy b/examples/MauiAppConfigDemo/Platforms/iOS/Resources/PrivacyInfo.xcprivacy
new file mode 100644
index 00000000..24ab3b43
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Platforms/iOS/Resources/PrivacyInfo.xcprivacy
@@ -0,0 +1,51 @@
+
+
+
+
+
+ NSPrivacyAccessedAPITypes
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryFileTimestamp
+ NSPrivacyAccessedAPITypeReasons
+
+ C617.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategorySystemBootTime
+ NSPrivacyAccessedAPITypeReasons
+
+ 35F9.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryDiskSpace
+ NSPrivacyAccessedAPITypeReasons
+
+ E174.1
+
+
+
+
+
+
diff --git a/examples/MauiAppConfigDemo/README.md b/examples/MauiAppConfigDemo/README.md
new file mode 100644
index 00000000..98621b5c
--- /dev/null
+++ b/examples/MauiAppConfigDemo/README.md
@@ -0,0 +1,446 @@
+# MAUI App with Azure App Configuration via Azure Front Door
+
+This project demonstrates how to integrate Azure App Configuration with a .NET MAUI application using Azure Front Door for global distribution and anonymous access.
+
+## Project Overview
+
+A simple travel booking app that demonstrates:
+- **Hybrid Configuration**: Snapshot (stable settings) + dynamic key-values
+- **Feature Flags**: Server-controlled feature toggles
+- **Cross-Platform**: Runs on Android, iOS, macOS Catalyst, and Windows
+
+## Prerequisites
+
+- .NET 9.0 SDK
+- Visual Studio 2022 or Visual Studio Code with .NET MAUI workload
+- Azure subscription with App Configuration resource
+- Azure Front Door instance configured for your App Configuration. Follow instructions at `https://aka.ms/appconfig/afdsetup`
+
+---
+
+## Step-by-Step Implementation
+
+### 1. Create New MAUI App
+
+Start with the default .NET MAUI template:
+
+```bash
+dotnet new maui -n MauiAppConfigDemo
+cd MauiAppConfigDemo
+```
+
+### 2. Add NuGet Packages
+
+Add the required packages to `MauiAppConfigDemo.csproj`:
+
+```xml
+
+
+
+
+```
+
+Run:
+```bash
+dotnet restore
+```
+
+### 3. Create Models Folder and AppSettings Class
+
+**Create:** `Models/AppSettings.cs`
+
+```csharp
+namespace MauiAppConfigDemo.Models;
+
+///
+/// Application settings loaded from Azure App Configuration.
+///
+public class AppSettings
+{
+ public string ApiUrl { get; set; } = "Unable to load API URL from Azure App Configuration";
+ public string AppVersion { get; set; } = "Unable to load App Version from Azure App Configuration";
+ public string WelcomeMessage { get; set; } = "Travel Booking configuration failed to load from Azure App Configuration.";
+ public string PromotionText { get; set; } = string.Empty;
+}
+```
+
+### 4. Create Services Folder and ConfigurationService
+
+**Create:** `Services/ConfigurationService.cs`
+
+```csharp
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Configuration.AzureAppConfiguration;
+using Microsoft.FeatureManagement;
+using MauiAppConfigDemo.Models;
+
+namespace MauiAppConfigDemo.Services;
+
+///
+/// Provides strongly-typed access to Azure App Configuration.
+/// Handles both snapshot (stable) and dynamic configuration.
+/// Feature flags are managed via IFeatureManager.
+///
+public class ConfigurationService
+{
+ private readonly IConfiguration _configuration;
+ private readonly IFeatureManager _featureManager;
+ private readonly IEnumerable _refreshers;
+
+ public ConfigurationService(IConfiguration configuration, IFeatureManager featureManager, IConfigurationRefresherProvider refresherProvider)
+ {
+ _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
+ _featureManager = featureManager ?? throw new ArgumentNullException(nameof(featureManager));
+ _refreshers = refresherProvider?.Refreshers ?? throw new ArgumentNullException(nameof(refresherProvider));
+ }
+
+ ///
+ /// Gets app settings from Azure App Configuration.
+ ///
+ public AppSettings GetSettings()
+ {
+ var settings = new AppSettings();
+ _configuration.GetSection("TravelApp").Bind(settings);
+ return settings;
+ }
+
+ ///
+ /// Checks if hotel booking feature is enabled.
+ ///
+ public async Task IsHotelBookingEnabledAsync()
+ {
+ return await _featureManager.IsEnabledAsync("TravelApp.HotelBooking");
+ }
+
+ ///
+ /// Checks if promotional banner is enabled.
+ ///
+ public async Task IsPromotionEnabledAsync()
+ {
+ return await _featureManager.IsEnabledAsync("TravelApp.ShowPromotion");
+ }
+
+ ///
+ /// Triggers a configuration refresh on demand.
+ ///
+ public async Task RefreshConfigurationAsync()
+ {
+ foreach (var refresher in _refreshers)
+ {
+ _ = refresher.TryRefreshAsync();
+ }
+ }
+}
+```
+
+### 5. Update MauiProgram.cs
+
+**Replace** the default `MauiProgram.cs` with:
+
+```csharp
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+using Microsoft.FeatureManagement;
+using MauiAppConfigDemo.Services;
+
+namespace MauiAppConfigDemo;
+
+public static class MauiProgram
+{
+ public static MauiApp CreateMauiApp()
+ {
+ var builder = MauiApp.CreateBuilder();
+ builder
+ .UseMauiApp()
+ .ConfigureFonts(fonts =>
+ {
+ fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
+ fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
+ });
+
+ // Configure Azure App Configuration via Azure Front Door
+ var afdEndpoint = new Uri("https://YOUR-AFD-ENDPOINT.azurefd.net");
+
+ builder.Configuration.AddAzureAppConfiguration(options =>
+ {
+ options.ConnectAzureFrontDoor(afdEndpoint)
+ .SelectSnapshot("TravelAppSnapshot") // Load snapshot
+ .Select("TravelApp:*") // Load key-values
+ .UseFeatureFlags(featureFlagOptions =>
+ {
+ featureFlagOptions.Select("TravelApp.*") // Load feature flags
+ .SetRefreshInterval(TimeSpan.FromMinutes(1));
+ })
+ .ConfigureRefresh(refreshOptions =>
+ {
+ refreshOptions.RegisterAll()
+ .SetRefreshInterval(TimeSpan.FromMinutes(1));
+ });
+ });
+
+ builder.Services.AddAzureAppConfiguration();
+ builder.Services.AddSingleton();
+ builder.Services.AddFeatureManagement();
+ builder.Services.AddTransient();
+
+#if DEBUG
+ builder.Logging.AddDebug();
+#endif
+
+ return builder.Build();
+ }
+}
+```
+
+**Important:** Replace `YOUR-AFD-ENDPOINT.azurefd.net` with your actual Azure Front Door endpoint.
+
+### 6. Update MainPage.xaml
+
+**Replace** the default `MainPage.xaml` with:
+
+```xaml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### 7. Update MainPage.xaml.cs
+
+**Replace** the default `MainPage.xaml.cs` with:
+
+```csharp
+using MauiAppConfigDemo.Services;
+
+namespace MauiAppConfigDemo;
+
+public partial class MainPage : ContentPage
+{
+ private readonly ConfigurationService _configService;
+
+ public MainPage(ConfigurationService configService)
+ {
+ InitializeComponent();
+ _configService = configService;
+ LoadConfiguration();
+ }
+
+ protected override void OnAppearing()
+ {
+ base.OnAppearing();
+
+ // Trigger configuration refresh when page appears
+ Task.Run(async () =>
+ {
+ await _configService.RefreshConfigurationAsync();
+ MainThread.BeginInvokeOnMainThread(() => LoadConfiguration());
+ });
+ }
+
+ private async void LoadConfiguration()
+ {
+ var settings = _configService.GetSettings();
+
+ var isPromotionEnabled = await _configService.IsPromotionEnabledAsync();
+ var isHotelEnabled = await _configService.IsHotelBookingEnabledAsync();
+
+ WelcomeLabel.Text = settings.WelcomeMessage;
+
+ PromotionalBanner.IsVisible = isPromotionEnabled && !string.IsNullOrEmpty(settings.PromotionText);
+ if (PromotionalBanner.IsVisible)
+ {
+ PromotionText.Text = settings.PromotionText;
+ }
+
+ HotelBookingCard.IsVisible = isHotelEnabled;
+
+ AppInfoLabel.Text = $"v{settings.AppVersion} | {settings.ApiUrl}";
+ }
+
+ private async void OnHotelBookingTapped(object? sender, EventArgs e)
+ {
+ await DisplayAlert("Hotel Booking", "Hotel booking feature - powered by Azure App Configuration!", "OK");
+ }
+
+ private async void OnRefreshing(object? sender, EventArgs e)
+ {
+ await _configService.RefreshConfigurationAsync();
+ LoadConfiguration();
+ RefreshView.IsRefreshing = false;
+ }
+}
+```
+
+### 8. Update AppShell.xaml (Optional)
+
+Simplify to single page shell:
+
+```xaml
+
+
+
+
+
+
+```
+
+---
+
+## Azure App Configuration Setup
+
+### Create a Configuration Snapshot
+
+Create a snapshot named `TravelAppSnapshot` which should have the following key-values:
+
+| Key | Value | Label |
+|-----|-------|--------------|
+| `TravelApp:ApiUrl` | `https://api.travelapp.com/v1` | (No Label) |
+| `TravelApp:AppVersion` | `1.0.0` | (No Label) |
+
+### Create Key-Values
+
+Add the following key-values directly (not in snapshot):
+
+| Key | Value | Label |
+|-----|-------|-------|
+| `TravelApp:WelcomeMessage` | `Welcome to Travel Booking powered by Azure App Configuration!` | (No Label) |
+| `TravelApp:PromotionText` | `Book now and save 20% on your first hotel!` | (No Label) |
+
+### Create Feature Flags
+
+Add the following feature flags:
+
+| Feature Flag Name | State |
+|-------------------|-------|
+| `TravelApp.HotelBooking` | On |
+| `TravelApp.ShowPromotion` | On |
+
+### Configure Azure Front Door
+
+1. Configure your App Configuration store to expose the required key-values through Azure Front Door. Follow instructions at `https://aka.ms/appconfig/afdsetup`.
+2. Update `MauiProgram.cs` with your AFD endpoint (e.g., `https://xxxxx.azurefd.net`)
+
+---
+
+## Sample runs from Android emulator
+
+Landing page when AppConfig feature flag `TravelApp.HotelBooking` is enabled
+
+
+
+Clicking on Hotel Booking card
+
+
+
+Refreshing app after disabling `TravelApp.HotelBooking` feature flag in App Configuration portal
+
+
+
+---
+
+## Troubleshooting
+
+### Configuration doesn't load
+- Verify Azure Front Door endpoint URL is correct
+- Check for AFD configuration warnings in AppConfig portal and fix the issues if any.
+- Make sure the correct scoping filters are set when configuring the AFD endpoint. These filters (for key-values, snapshots, and feature flags) define the regex rules that block requests that don't match specified filters. If your app can’t access its configuration, review AFD rules to find any blocking regex patterns. Update the rule with the right filter or create a new AFD endpoint from the App Configuration portal.
+
+### Configuration doesn't refresh
+- Azure Front Door manages caching behavior, so updates from App Configuration aren’t immediately available to the app. Even if your app checks for changes every minute, AFD may serve cached data until its own cache expires. For example, if AFD caches for 10 minutes, your app won’t see updates for at least 10 minutes, even though it keeps requesting every minute. This design ensures eventual consistency, not real-time updates, which is expected for any CDN-based solutions. Learn more about [caching with Azure Front Door](https://learn.microsoft.com/en-us/azure/frontdoor/front-door-caching).
+
+---
+
+## Additional Resources
+
+- [Azure App Configuration Documentation](https://learn.microsoft.com/azure/azure-app-configuration/)
+- [.NET MAUI Documentation](https://learn.microsoft.com/dotnet/maui/)
+- [Feature Management Documentation](https://learn.microsoft.com/azure/azure-app-configuration/feature-management)
+- [Azure Front Door Documentation](https://learn.microsoft.com/azure/frontdoor/)
+
+---
diff --git a/examples/MauiAppConfigDemo/Resources/AppIcon/appicon.svg b/examples/MauiAppConfigDemo/Resources/AppIcon/appicon.svg
new file mode 100644
index 00000000..9d63b651
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Resources/AppIcon/appicon.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/examples/MauiAppConfigDemo/Resources/AppIcon/appiconfg.svg b/examples/MauiAppConfigDemo/Resources/AppIcon/appiconfg.svg
new file mode 100644
index 00000000..21dfb25f
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Resources/AppIcon/appiconfg.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/examples/MauiAppConfigDemo/Resources/Fonts/OpenSans-Regular.ttf b/examples/MauiAppConfigDemo/Resources/Fonts/OpenSans-Regular.ttf
new file mode 100644
index 00000000..bf60ae56
Binary files /dev/null and b/examples/MauiAppConfigDemo/Resources/Fonts/OpenSans-Regular.ttf differ
diff --git a/examples/MauiAppConfigDemo/Resources/Fonts/OpenSans-Semibold.ttf b/examples/MauiAppConfigDemo/Resources/Fonts/OpenSans-Semibold.ttf
new file mode 100644
index 00000000..ad71502a
Binary files /dev/null and b/examples/MauiAppConfigDemo/Resources/Fonts/OpenSans-Semibold.ttf differ
diff --git a/examples/MauiAppConfigDemo/Resources/Images/dotnet_bot.png b/examples/MauiAppConfigDemo/Resources/Images/dotnet_bot.png
new file mode 100644
index 00000000..1d1b981e
Binary files /dev/null and b/examples/MauiAppConfigDemo/Resources/Images/dotnet_bot.png differ
diff --git a/examples/MauiAppConfigDemo/Resources/Raw/AboutAssets.txt b/examples/MauiAppConfigDemo/Resources/Raw/AboutAssets.txt
new file mode 100644
index 00000000..89dc758d
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Resources/Raw/AboutAssets.txt
@@ -0,0 +1,15 @@
+Any raw assets you want to be deployed with your application can be placed in
+this directory (and child directories). Deployment of the asset to your application
+is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
+
+
+
+These files will be deployed with your package and will be accessible using Essentials:
+
+ async Task LoadMauiAsset()
+ {
+ using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
+ using var reader = new StreamReader(stream);
+
+ var contents = reader.ReadToEnd();
+ }
diff --git a/examples/MauiAppConfigDemo/Resources/Splash/splash.svg b/examples/MauiAppConfigDemo/Resources/Splash/splash.svg
new file mode 100644
index 00000000..21dfb25f
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Resources/Splash/splash.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/examples/MauiAppConfigDemo/Resources/Styles/Colors.xaml b/examples/MauiAppConfigDemo/Resources/Styles/Colors.xaml
new file mode 100644
index 00000000..30307a5d
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Resources/Styles/Colors.xaml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+ #512BD4
+ #ac99ea
+ #242424
+ #DFD8F7
+ #9880e5
+ #2B0B98
+
+ White
+ Black
+ #D600AA
+ #190649
+ #1f1f1f
+
+ #E1E1E1
+ #C8C8C8
+ #ACACAC
+ #919191
+ #6E6E6E
+ #404040
+ #212121
+ #141414
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/MauiAppConfigDemo/Resources/Styles/Styles.xaml b/examples/MauiAppConfigDemo/Resources/Styles/Styles.xaml
new file mode 100644
index 00000000..d4dded0b
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Resources/Styles/Styles.xaml
@@ -0,0 +1,440 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/MauiAppConfigDemo/Screenshots/HotelBookingDisabled.png b/examples/MauiAppConfigDemo/Screenshots/HotelBookingDisabled.png
new file mode 100644
index 00000000..6300460d
Binary files /dev/null and b/examples/MauiAppConfigDemo/Screenshots/HotelBookingDisabled.png differ
diff --git a/examples/MauiAppConfigDemo/Screenshots/HotelBookingEnabled.png b/examples/MauiAppConfigDemo/Screenshots/HotelBookingEnabled.png
new file mode 100644
index 00000000..66a2fea5
Binary files /dev/null and b/examples/MauiAppConfigDemo/Screenshots/HotelBookingEnabled.png differ
diff --git a/examples/MauiAppConfigDemo/Screenshots/HotelBookingFeature.png b/examples/MauiAppConfigDemo/Screenshots/HotelBookingFeature.png
new file mode 100644
index 00000000..07546fe1
Binary files /dev/null and b/examples/MauiAppConfigDemo/Screenshots/HotelBookingFeature.png differ
diff --git a/examples/MauiAppConfigDemo/Services/ConfigurationService.cs b/examples/MauiAppConfigDemo/Services/ConfigurationService.cs
new file mode 100644
index 00000000..f133fad9
--- /dev/null
+++ b/examples/MauiAppConfigDemo/Services/ConfigurationService.cs
@@ -0,0 +1,62 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Configuration.AzureAppConfiguration;
+using Microsoft.FeatureManagement;
+using MauiAppConfigDemo.Models;
+
+namespace MauiAppConfigDemo.Services;
+
+///
+/// Provides strongly-typed access to Azure App Configuration.
+/// Handles both snapshot (stable) and dynamic configuration.
+/// Feature flags are managed via IFeatureManager.
+///
+public class ConfigurationService
+{
+ private readonly IConfiguration _configuration;
+ private readonly IFeatureManager _featureManager;
+ private readonly IEnumerable _refreshers;
+
+ public ConfigurationService(IConfiguration configuration, IFeatureManager featureManager, IConfigurationRefresherProvider refresherProvider)
+ {
+ _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
+ _featureManager = featureManager ?? throw new ArgumentNullException(nameof(featureManager));
+ _refreshers = refresherProvider?.Refreshers ?? throw new ArgumentNullException(nameof(refresherProvider));
+ }
+
+ ///
+ /// Gets app settings from Azure App Configuration.
+ ///
+ public AppSettings GetSettings()
+ {
+ var settings = new AppSettings();
+ _configuration.GetSection("TravelApp").Bind(settings);
+ return settings;
+ }
+
+ ///
+ /// Checks if hotel booking feature is enabled.
+ ///
+ public async Task IsHotelBookingEnabledAsync()
+ {
+ return await _featureManager.IsEnabledAsync("TravelApp.HotelBooking");
+ }
+
+ ///
+ /// Checks if promotional banner is enabled.
+ ///
+ public async Task IsPromotionEnabledAsync()
+ {
+ return await _featureManager.IsEnabledAsync("TravelApp.ShowPromotion");
+ }
+
+ ///
+ /// Triggers a configuration refresh on demand.
+ ///
+ public async Task RefreshConfigurationAsync()
+ {
+ foreach (var refresher in _refreshers)
+ {
+ _ = refresher.TryRefreshAsync();
+ }
+ }
+}