ironservices-maui/README.md

18 KiB

IronServices.Maui

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

NuGet License: MIT

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.