SDK Configuration
Auto capture is off by default. PassUserpilotOptions to UserpilotSdk.Setup(...). The same options object contains the platform SDK options and the MAUI auto capture options.
Call setup once after the app has created a MAUI application/window — for example from a startup, splash, or initialize screen. If setup runs before any window is available, call
UserpilotAutoCapture.ResumeAutoCapture() after the first window/page is available so the wrapper can attach to the current windows.
Screen Capture
WhenEnableScreenAutoCapture is true, the SDK emits $screen for MAUI navigation and page lifecycle changes:
Screen titles are resolved in this order:
- Current page
Title - Current Shell route/location
- Current page type name
Interaction Capture
WhenEnableInteractionAutoCapture is true, the SDK captures the following MAUI interactions:
Each interaction maps to a high-level analytics category (
tap, text_change, selection_change, value_change, view_presented) — that is the event name that reaches your analytics backend.
Text fields are debounced and never send typed text. Text-field payloads contain has_text, text_length, read_only, and obscured. Continuous controls such as Slider and Stepper are debounced so drag/update bursts send one settled event.
Every debounced interaction — text changes plus Slider/Stepper values — is flushed before a screen transition and before its control or page is torn down, so it is emitted ahead of the new screen event and keeps the previous screen in its hierarchy. Payloads are snapshotted at interaction time, so a flush that runs after the page has left its parent still reports the hierarchy the user actually interacted with.
Tab Selection
tab_selected uses the shared selection fields, so a tab switch is described like any other selection:
DatePicker and TimePicker are driven entirely by the MAUI change event, so their capture timing follows the platform. On Android the dialog commits the value synchronously when OK is tapped, so the event always lands. On iOS the picker is a wheel (UIDatePickerStyle.Wheels) and MAUI commits Date/Time from UIDatePicker.ValueChanged, which UIKit raises only once the wheel settles — see Limitations.
List Selection
CollectionView and ListView both emit list_item_selected with the shared selection fields, so one dashboard rule matches either control. target_class distinguishes them (CollectionView / ListView).
Both text fields come from the item’s own
ToString(), so give list items a meaningful override. Two gates control this:
target_textneedsEnableInteractionTextCapture(on by default) and honoursRedactText.selected_valueneedsEnableInteractionValueCapture(off by default) and is not affected byRedactText.
RedactText alone is not enough once value capture is on. Use IgnoreInteractions="True" to drop the event entirely, or leave EnableInteractionValueCapture off.
ListView is deprecated by MAUI (obsolete from Microsoft.Maui.Controls 10) and is captured for apps ported from Xamarin.Forms. Two differences from CollectionView:
- Capture is driven by
ItemTapped, so it works withSelectionMode="None"— the pattern where a row only navigates — and a programmaticSelectedItemassignment is not captured.CollectionViewusesSelectionChanged, which does report programmatic selection. - Re-tapping the already-selected row emits another event, because it is another tap.
CollectionViewemits nothing, because the selection did not change.
IsGroupingEnabled="True", selected_index and entry_count are resolved across the flattened rows, so they describe the row’s position among all rows rather than MAUI’s rendered row index, which counts group headers.
Component Value Gating
By default, user-input values are stripped from event payloads. Position and configuration metadata such asselected_index, entry_count, min, and max can still be sent because they describe control structure, not the selected value.
Set EnableInteractionValueCapture = true to include value fields:
Text input content is not captured even when value capture is enabled.
Presentation Capture
Modal pages pushed withNavigation.PushModalAsync(...) emit view_presented with:
For custom overlays such as bottom sheets, mark the overlay view with
PresentationSurface. The SDK emits view_presented when the marked view changes from hidden to visible:
PresentationTitle is used for target_text and either dialog_title or bottom_sheet_title when text capture is enabled.
Privacy Controls
Global flags govern what is captured:- Text capture —
EnableInteractionTextCapture - Accessibility label capture —
EnableInteractionAccessibilityLabelCapture - Value payload capture —
EnableInteractionValueCapture
target_text with ****; accessibility fields, hierarchy attributes, placeholders, and captured values are not masked by redaction.
Auto capture never sends typed input values verbatim — only field metadata such as has_text and text_length.
Add the Userpilot namespace to XAML before using attached properties:
UserpilotAutoCapture.RedactText
Redaction replaces the captured target_text with ****. The interaction event still fires — the control is still identified by type, position, and structure — only the human-readable text is masked.
RedactText="False" on a descendant to override a redacted ancestor:
UserpilotAutoCapture.IgnoreInteractions
Ignoring skips interaction events for a subtree. The app’s own handlers still run — only auto capture ignores those interactions. Route/page-driven screen events are not suppressed.
IgnoreInteractions="False" on a descendant to override an ignored ancestor.
View Tag and Accessibility Value
Enrich events without changing visible UI:ViewTag is always included when set and is not gated by accessibility capture.
Runtime Controls
Auto capture starts automatically fromSetup (based on options). To pause or resume collection at runtime without re-passing the config — for example a “private mode” — use:
StopAutoCapture() and Stop() are aliases for pause. ResumeAutoCapture() resumes using the options supplied during setup.
While paused, no screen or interaction events are recorded. Pausing auto capture does not enable manual Screen() calls: when EnableScreenAutoCapture is configured, manual and automatic screen tracking remain mutually exclusive.
Privacy markers only affect Userpilot auto capture. They do not disable MAUI controls, commands, gestures, or app event handlers.
Limitations
- Shell bottom tabs — Captured as
$screennavigation, not astab_selected.TabbedPageemitstab_selected. - Native
DisplayAlert,DisplayActionSheet,DisplayPromptAsync— Not captured asview_presented. The button that opened them is captured. - Bottom sheets — MAUI has no built-in bottom sheet. Mark custom sheet overlays with
PresentationSurface. ToolbarItem,MenuItem, context menus,SwipeViewactions — Not captured, exceptMenuFlyoutItem.- Scroll gestures, pull-to-refresh, pan, pinch, swipe, pointer, drag — Not captured.
WebView,Map,GraphicsView, Skia/custom drawn surfaces — Not captured internally.- Programmatic value changes — The SDK listens to MAUI events. Text changes are focus-gated; other control change events may be captured if the control raises its normal change event.
- iOS
DatePicker/TimePickerdismissed before the wheel settles — Not captured. UIKit raisesUIDatePicker.ValueChangedonly after the wheel settles, and MAUI commitsDate/Timefrom that event (UpdateMode.Immediately, the default) or from the Done button, both of which read the picker’s already-settled value. Tapping Done — or dismissing the picker — before the wheel settles leaves the property unchanged, so MAUI raises no change event and the host app keeps the old value too. Android’s dialog commits synchronously on OK, so the event always lands there.