MAUI использует SDK для .NET, который собран под netstandard2.0 и является тем же пакетом, что взяло бы приложение на WPF, WinForms, Avalonia или консольное; что он сообщает, смотрите в измерении приложения. Специфично для MAUI то, где вы его запускаете, где лежит его очередь и откуда экраны берут имена.

Исходники и демонстрационное настольное приложение, отчитывающееся само о себе, лежат в skomi-sdk-dotnet.

Один клиент на всю жизнь приложения

Зарегистрируйте его в MauiProgram.cs, чтобы у приложения был ровно один:

builder.Services.AddSingleton(_ => new SkomiClient(new SkomiOptions
{
    AppId = Guid.Parse("your-app-id"),          // из «Мои приложения» в Skomi
    AppVersion = AppInfo.Current.VersionString,
    StoragePath = FileSystem.Current.AppDataDirectory,
}));

AppVersion по умолчанию берётся из версии входной сборки, а в MAUI это не та версия, которую вы выложили в магазин. Та — AppInfo.Current.VersionString.

StoragePath решает, где живёт очередь без сети. FileSystem.Current.AppDataDirectory приватен для приложения на каждой цели MAUI и переживает обновление, чего путь к локальным данным приложения по умолчанию на мобильных надёжно не делает.

Освобождайте его при завершении. Это останавливает таймер и делает последнюю ограниченную попытку доставить; то, что не ушло сейчас, лежит на диске и уйдёт при следующем запуске. Если вы зарегистрировали его как singleton, контейнер внедрения зависимостей освободит его за вас при остановке хоста, но приложение MAUI чаще убивают, чем останавливают, и ровно поэтому очередь и существует.

Назовите экраны

skomi.Screen("checkout/review", new Dictionary<string, object?> { ["tier"] = "gold" });

Автоматического захвата имён страниц нет: класс страницы — то, как экран называет ваш код, а не то, как вы назвали бы его в отчёте. Естественное место в MAUI — один обработчик на навигацию:

Shell.Current.Navigated += (_, e) =>
    skomi.Screen(e.Current.Location.OriginalString.Trim('/'));

Это отправная точка, а не правило: строки маршрутов дают читаемые имена, только если ваши маршруты читаемы.

Ошибки

try
{
    await CheckoutAsync();
}
catch (Exception ex)
{
    skomi.CaptureError(ex, fatal: false, screenName: "checkout/review");
    ShowRetry();
}

Сбои группируются по типу, а не по сообщению, потому что в сообщении есть идентификаторы и суммы, делающие каждый случай уникальным.

Это не система отчётов о падениях. Она записывает те сбои, которые вы ей передали, и управляемые исключения, которые ей видны; нативное падение на Android или iOS уносит процесс с собой раньше, чем что-либо можно записать. Оставьте для этого собственную отчётность магазина, Crashlytics или Sentry; Skomi добавляет долю сессий без падений рядом с числами вашего выпуска и те ошибки, о которых вы решили сообщить.

Загрузки файлов сопоставления тоже нет, поэтому стек из минифицированной выпускной сборки приходит таким, каким его бросили.

Он никогда не бросает исключение в ваш код

Каждый публичный метод глотает и сообщает через OnError, который по умолчанию молчит, правильно для выпущенного приложения и неправильно, пока вы интегрируетесь:

OnError = (ex, stage) => Debug.WriteLine($"Skomi {stage}: {ex}"),

Ожидание согласия

new SkomiOptions { AppId = id, Enabled = false }

Enabled = false ничего не записывает и ничего не шлёт, так что клиент может существовать с запуска и начать собирать только после того, как кто-то согласился.

Проверьте, что работает

Запустите приложение, перейдите между двумя страницами и посмотрите в свою панель. Если ничего не пришло, сначала задайте OnError: идентификатор не того приложения и клиент, которого не освободили, — две обычные причины, и без него обе молчат.