IronServices.Maui
MAUI controls and services for IronServices (IronLicensing, IronNotify, IronTelemetry).

Installation
<PackageReference Include="IronServices.Maui" Version="1.0.0" />
Quick Start
1. Register Services
// MauiProgram.cs
builder.Services.AddIronServices();
// Or with custom URLs
builder.Services.AddIronServices(options =>
{
options.LicensingUrl = "https://ironlicensing.com";
options.NotifyUrl = "https://ironnotify.com";
options.TelemetryUrl = "https://irontelemetry.com";
});
2. Use Controls
<ContentPage xmlns:iron="clr-namespace:IronServices.Maui.Controls;assembly=IronServices.Maui">
<iron:LoginView Client="{Binding IronClient}" LoginSuccess="OnLoginSuccess" />
</ContentPage>
Controls
| Control |
Type |
Description |
LoginView |
ContentView |
Drop-in login form |
LicenseActivationView |
ContentView |
License key entry and activation |
AppLogView |
ContentPage |
Exception and telemetry log viewer |
UserJourneyView |
ContentPage |
User journey visualization with Timeline/Tree/Flow modes |
LoginView
A drop-in login form for IronServices authentication.
Type: ContentView (embeddable)
Properties
| Property |
Type |
Default |
Description |
Client |
IronServicesClient |
null |
Required. The client instance for authentication. |
Title |
string |
"Sign In" |
Header text above the form. |
Subtitle |
string |
"Enter your credentials..." |
Subheader text. |
Logo |
ImageSource |
null |
Optional logo image. |
ShowRegisterLink |
bool |
true |
Show "Sign Up" link. |
ShowForgotPasswordLink |
bool |
true |
Show "Forgot Password?" link. |
Events
| Event |
Args |
Description |
LoginSuccess |
LoginSuccessEventArgs |
Fired on successful login. Contains UserId, Email, DisplayName. |
LoginFailed |
LoginFailedEventArgs |
Fired on failed login. Contains Error message. |
RegisterRequested |
EventArgs |
User tapped "Sign Up" link. |
ForgotPasswordRequested |
EventArgs |
User tapped "Forgot Password?" link. |
Example
// XAML
<iron:LoginView x:Name="LoginView"
Client="{Binding IronClient}"
Title="Welcome Back"
Logo="logo.png"
ShowRegisterLink="True"
LoginSuccess="OnLoginSuccess"
RegisterRequested="OnRegisterRequested" />
// Code-behind
private async void OnLoginSuccess(object sender, LoginSuccessEventArgs e)
{
await DisplayAlert("Success", $"Welcome, {e.DisplayName}!", "OK");
await Shell.Current.GoToAsync("//main");
}
private async void OnRegisterRequested(object sender, EventArgs e)
{
await Shell.Current.GoToAsync("//register");
}
LicenseActivationView
A license key entry and activation form.
Type: ContentView (embeddable)
Properties
| Property |
Type |
Default |
Description |
LicenseManager |
ILicenseManager |
null |
Required. The license manager instance. |
Title |
string |
"Activate License" |
Header text. |
Subtitle |
string |
"Enter your license key..." |
Subheader text. |
LicenseKey |
string |
null |
Pre-filled license key (two-way binding). |
ShowHelpLink |
bool |
true |
Show help link below form. |
HelpUrl |
string |
"https://..." |
URL opened when help tapped. |
Events
| Event |
Args |
Description |
LicenseActivated |
LicenseActivatedEventArgs |
Activation succeeded. Contains License info. |
ActivationFailed |
LicenseActivationFailedEventArgs |
Activation failed. Contains Error message. |
HelpRequested |
EventArgs |
User tapped help link. |
Methods
| Method |
Returns |
Description |
ValidateAsync() |
Task<bool> |
Validate the entered license key without activating. |
Example
// XAML
<iron:LicenseActivationView x:Name="ActivationView"
LicenseManager="{Binding LicenseManager}"
LicenseActivated="OnLicenseActivated" />
// Code-behind
private async void OnLicenseActivated(object sender, LicenseActivatedEventArgs e)
{
var license = e.License;
await DisplayAlert("Activated",
$"License: {license.Tier}\nExpires: {license.ExpiresAt:d}", "OK");
}
AppLogView
A full-page log viewer for exceptions and telemetry events. Shows queued items from TelemetryClient.OfflineQueue.
Type: ContentPage (standalone page, use with NavigationPage)
Properties
| Property |
Type |
Default |
Description |
TelemetryClient |
TelemetryClient |
null |
Client to pull queued logs from. Auto-refreshes on set. |
Title |
string |
"App Logs" |
Page title (shown in navigation bar). |
ShowShareButton |
bool |
true |
Enable Share toolbar button. |
ShowClearButton |
bool |
true |
Enable Clear toolbar button. |
EnableLiveUpdates |
bool |
false |
Auto-refresh when new items are added (polls every 2s). |
Events
| Event |
Args |
Description |
LogsCleared |
EventArgs |
User cleared the log list. |
LogsShared |
EventArgs |
User shared the logs. |
LogSelected |
LogItem |
User selected a log entry. |
LogsRefreshed |
EventArgs |
Log list was refreshed. |
Methods
| Method |
Returns |
Description |
RefreshLogs() |
void |
Reload logs from TelemetryClient queue. |
AddLog(type, title, message, stackTrace?) |
void |
Add a manual log entry. |
AddException(Exception) |
void |
Add an exception as a log entry. |
ClearLogs() |
void |
Clear displayed logs (not the queue). |
ClearAllLogs() |
void |
Clear displayed logs AND the offline queue. |
ExportLogs() |
string |
Export all logs as formatted text. |
GetLogs() |
IReadOnlyList<LogItem> |
Get current log items. |
LogCount |
int |
Number of log items. |
LogItem Properties
| Property |
Type |
Description |
Timestamp |
DateTime |
UTC timestamp. |
Type |
string |
"exception", "message", "journey_start", "step_start", etc. |
Title |
string |
Exception type name or message title. |
Message |
string |
Log message. |
StackTrace |
string? |
Stack trace for exceptions. |
JourneyId |
string? |
Associated user journey ID. |
UserId |
string? |
Associated user ID. |
TypeDisplay |
string |
Short type label: "ERROR", "MSG", "START", etc. |
TypeColor |
Color |
Color for type badge. |
TimestampDisplay |
string |
Formatted local time (HH:mm:ss). |
Example
// Navigate to log view
await Navigation.PushAsync(new AppLogView
{
TelemetryClient = _telemetryClient,
Title = "Error Logs",
EnableLiveUpdates = true
});
// With event handling
var logView = new AppLogView { TelemetryClient = _telemetryClient };
logView.LogsCleared += (s, e) => Debug.WriteLine("Logs cleared");
logView.LogSelected += (s, item) => Debug.WriteLine($"Selected: {item.Title}");
await Navigation.PushAsync(logView);
// Manual log entries (without TelemetryClient)
var logView = new AppLogView();
logView.AddException(ex);
logView.AddLog("warning", "Network Issue", "Connection timeout after 30s");
Toolbar
- Refresh (in header): Reloads from queue.
- Share (Primary): Exports logs as text and opens system share sheet.
- Clear (Secondary/overflow): Clears displayed logs.
UserJourneyView
A full-page user journey viewer with multiple visualization modes. Reconstructs journeys from TelemetryClient log items and displays steps, breadcrumbs, exceptions, and metadata.
Type: ContentPage (standalone page, use with NavigationPage)
View Modes
| Mode |
Description |
Timeline |
Vertical list with nested steps shown with indentation (default). |
Tree |
Expandable tree hierarchy with collapsible journey/step nodes. |
Flow |
Horizontal flowchart-style diagram showing step progression. |
Properties
| Property |
Type |
Default |
Description |
TelemetryClient |
TelemetryClient |
null |
Client to pull journey data from. Auto-refreshes on set. |
Title |
string |
"User Journeys" |
Page title. |
ViewMode |
JourneyViewMode |
Timeline |
Current visualization mode. |
EnableLiveUpdates |
bool |
false |
Auto-refresh when new items are added (polls every 2s). |
ShowShareButton |
bool |
true |
Enable Share toolbar button. |
ShowClearButton |
bool |
true |
Enable Clear toolbar button. |
UserIdFilter |
string? |
null |
Filter to show only journeys for a specific user. |
Events
| Event |
Args |
Description |
JourneySelected |
JourneyItem |
User selected a journey from the list. |
StepSelected |
StepItem |
User selected a step in the detail panel. |
JourneysRefreshed |
EventArgs |
Journey list was refreshed. |
JourneysCleared |
EventArgs |
Journeys were cleared. |
JourneysShared |
EventArgs |
Journeys were exported/shared. |
Methods
| Method |
Returns |
Description |
RefreshJourneys() |
void |
Reload journeys from TelemetryClient. |
ClearJourneys() |
void |
Clear displayed journeys. |
ClearAllJourneys() |
void |
Clear displayed journeys AND the TelemetryClient queue. |
ExportJourneys() |
string |
Export all journeys as formatted text. |
GetJourneys() |
IReadOnlyList<JourneyItem> |
Get current journey items. |
JourneyCount |
int |
Number of journeys. |
JourneyItem Properties
| Property |
Type |
Description |
JourneyId |
string |
Unique journey identifier. |
Name |
string |
Journey name (e.g., "Checkout Flow"). |
UserId |
string? |
Associated user ID. |
UserEmail |
string? |
Associated user email. |
Status |
JourneyDisplayStatus |
InProgress, Completed, Failed, Abandoned. |
StartTime |
DateTime |
When the journey started. |
EndTime |
DateTime? |
When the journey ended (null if in progress). |
DurationMs |
double? |
Duration in milliseconds. |
DurationDisplay |
string |
Formatted duration: "125ms", "2.3s", "1.5m". |
TimeAgo |
string |
Relative time: "just now", "5m ago", "2h ago". |
StatusDisplay |
string |
Human-readable status text. |
StatusColor |
Color |
Color for status badge (Blue/Green/Red/Gray). |
StatusIcon |
string |
Icon text: "...", "OK", "X", "-". |
Steps |
ObservableCollection<StepItem> |
Top-level steps in this journey. |
Breadcrumbs |
List<BreadcrumbItem> |
Breadcrumbs captured during journey. |
Exceptions |
List<ExceptionItem> |
Exceptions captured during journey. |
Metadata |
Dictionary<string, object> |
Custom metadata. |
StepCount |
int |
Total steps (including nested). |
FailedStepCount |
int |
Number of failed steps. |
HasFailedSteps |
bool |
Whether any steps failed. |
HasExceptions |
bool |
Whether any exceptions occurred. |
HasUser |
bool |
Whether a user is associated. |
StepItem Properties
| Property |
Type |
Description |
StepId |
string |
Unique step identifier. |
ParentStepId |
string? |
Parent step ID for nested steps. |
Name |
string |
Step name (e.g., "Process Payment"). |
Category |
string? |
Category: "business", "technical", "navigation". |
Status |
StepDisplayStatus |
InProgress, Completed, Failed, Skipped. |
FailureReason |
string? |
Reason for failure. |
StartTime |
DateTime |
When the step started. |
EndTime |
DateTime? |
When the step ended. |
DurationMs |
double? |
Duration in milliseconds. |
DurationDisplay |
string |
Formatted duration. |
StatusColor |
Color |
Color for status indicator. |
StatusIcon |
string |
Icon text. |
ChildSteps |
ObservableCollection<StepItem> |
Nested child steps. |
NestingLevel |
int |
Depth level for indentation. |
IndentMargin |
Thickness |
Calculated margin for indentation. |
BreadcrumbItem Properties
| Property |
Type |
Description |
Timestamp |
DateTime |
When captured. |
Category |
string |
Breadcrumb category. |
Message |
string |
Breadcrumb message. |
Level |
string |
Log level: "info", "warning", "error", "debug". |
LevelColor |
Color |
Color based on level. |
Data |
Dictionary<string, object>? |
Additional data. |
ExceptionItem Properties
| Property |
Type |
Description |
Timestamp |
DateTime |
When captured. |
ExceptionType |
string |
Exception type name. |
Message |
string |
Exception message. |
StackTrace |
string? |
Stack trace. |
StepId |
string? |
Step ID where exception occurred. |
HasStackTrace |
bool |
Whether stack trace is available. |
Example
// Basic usage
await Navigation.PushAsync(new UserJourneyView
{
TelemetryClient = _telemetryClient,
EnableLiveUpdates = true
});
// With view mode and filtering
var journeyView = new UserJourneyView
{
TelemetryClient = _telemetryClient,
ViewMode = JourneyViewMode.Tree,
UserIdFilter = currentUser.Id
};
await Navigation.PushAsync(journeyView);
// Event handling
var journeyView = new UserJourneyView { TelemetryClient = _telemetryClient };
journeyView.JourneySelected += (s, journey) =>
{
Debug.WriteLine($"Selected: {journey.Name} ({journey.StepCount} steps)");
};
journeyView.StepSelected += (s, step) =>
{
Debug.WriteLine($"Step: {step.Name} - {step.StatusDisplay}");
};
await Navigation.PushAsync(journeyView);
Detail Panel
When a journey is selected, a detail panel shows:
- Status, Duration, User - Summary info
- Steps - List of steps with status indicators and timing
- Breadcrumbs - Timestamped breadcrumb trail
- Exceptions - Exception details (tap to copy)
- Metadata - Key-value pairs
- Copy to Clipboard - Export journey details
Toolbar
- Timeline/Tree/Flow buttons - Switch view modes
- Share (Primary) - Export journeys as text
- Clear (Secondary) - Clear displayed journeys
- Refresh - Reload from TelemetryClient
Services
MauiSecureTokenStorage
Secure token storage using MAUI SecureStorage. Automatically registered when using AddIronServices().
// Automatically used by IronServicesClient
builder.Services.AddIronServices();
// Or manually
services.AddSingleton<ITokenStorage, MauiSecureTokenStorage>();
ServiceCollectionExtensions
// Default URLs
services.AddIronServices();
// Custom URLs
services.AddIronServices(options =>
{
options.LicensingUrl = "https://licensing.myapp.com";
options.NotifyUrl = "https://notify.myapp.com";
options.TelemetryUrl = "https://telemetry.myapp.com";
});
Registers:
MauiSecureTokenStorage as ITokenStorage
IronServicesClient as singleton
Complete Example
// MauiProgram.cs
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder.UseMauiApp<App>();
// Register IronServices
builder.Services.AddIronServices();
// Register TelemetryClient for logging
builder.Services.AddSingleton(sp => new TelemetryClient(new TelemetryOptions
{
Dsn = "your-dsn-here",
EnableOfflineQueue = true
}));
return builder.Build();
}
// LoginPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:iron="clr-namespace:IronServices.Maui.Controls;assembly=IronServices.Maui">
<ScrollView>
<iron:LoginView x:Name="LoginControl"
Client="{Binding IronClient}"
Logo="app_logo.png"
Title="Welcome"
LoginSuccess="OnLoginSuccess" />
</ScrollView>
</ContentPage>
// LoginPage.xaml.cs
public partial class LoginPage : ContentPage
{
public LoginPage(IronServicesClient client)
{
InitializeComponent();
LoginControl.Client = client;
}
private async void OnLoginSuccess(object sender, LoginSuccessEventArgs e)
{
await Shell.Current.GoToAsync("//main");
}
}
// SettingsPage.xaml.cs - Navigate to logs
private async void OnViewLogsClicked(object sender, EventArgs e)
{
var telemetry = Handler.MauiContext.Services.GetService<TelemetryClient>();
await Navigation.PushAsync(new AppLogView
{
TelemetryClient = telemetry,
EnableLiveUpdates = true
});
}
// SettingsPage.xaml.cs - Navigate to journeys
private async void OnViewJourneysClicked(object sender, EventArgs e)
{
var telemetry = Handler.MauiContext.Services.GetService<TelemetryClient>();
await Navigation.PushAsync(new UserJourneyView
{
TelemetryClient = telemetry,
EnableLiveUpdates = true,
ViewMode = JourneyViewMode.Timeline
});
}
Styling
All controls use MAUI resource dictionaries. Override these colors in your App.xaml:
<Color x:Key="Primary">#512BD4</Color>
<Color x:Key="Secondary">#DFD8F7</Color>
<Color x:Key="Gray100">#E1E1E1</Color>
<Color x:Key="Gray200">#C8C8C8</Color>
<Color x:Key="Gray400">#919191</Color>
<Color x:Key="Gray500">#6E6E6E</Color>
<Color x:Key="Gray600">#404040</Color>
<Color x:Key="Gray700">#374151</Color>
<Color x:Key="Gray800">#1F2937</Color>
<Color x:Key="Gray900">#212121</Color>
<Color x:Key="White">#FFFFFF</Color>
Controls support light/dark themes via AppThemeBinding.
Platform Support
| Platform |
Minimum Version |
| Android |
API 21 (5.0) |
| iOS |
15.0 |
| macOS |
15.0 |
| Windows |
10.0.17763.0 |
Dependencies
IronServices.Client - Core API client
IronLicensing.Client - License validation
IronTelemetry.Client - Telemetry, journeys, and logging
Microsoft.Maui.Controls - MAUI framework
License
MIT License - see LICENSE file.