Как не закрывать iOS приложение на .NET MAUI при необработанном исключении?

Я реализовал глобальный обработчик исключений, который в случае любого необработанного исключения не закрывает приложение, а отображает сообщение пользователю, что произошла какая-нибудь ошибка, после чего приложение продолжает работать.
Это очень удобно, ведь не надо всё оборачивать в try/catch.
На Android и Windows всё работает как надо, но вот на iOS (наверное и на MacOS) приложение просто закрывается, а не продолжает работать.
У меня есть вот такой код, который я подглядел здесь:

AppDomain.CurrentDomain.UnhandledException += (_, args) => (args.ExceptionObject as Exception).DisplayError();
#if ANDROID
Android.Runtime.AndroidEnvironment.UnhandledExceptionRaiser += (sender, args) =>
{
    args.Handled = true;
    args.Exception.DisplayError();
};
#elif WINDOWS
AppDomain.CurrentDomain.FirstChanceException += (_, args) => lastFirstChanceException = args.Exception;
Microsoft.UI.Xaml.Application.Current.UnhandledException += (sender, args) =>
{
    Exception exception = args.Exception;
    if (exception.StackTrace == null)
    {
        exception = lastFirstChanceException;
    }
    args.Handled = true;
    args.Exception.DisplayError();
};
#endif

Но вот только на iOS это не работает так, как надо, и приложение в любом случае закрывается при любом необработанном исключении.
Немного покопавшись, я нашел нечто, что помогло мне приблизится к ожидаемому результату:

AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
{
    (args.ExceptionObject as Exception).DisplayError();
    #if IOS || MACCATALYST
    Foundation.NSRunLoop.Current.RunUntil(Foundation.NSDate.DistantFuture);
    #endif
};
#if ANDROID
Android.Runtime.AndroidEnvironment.UnhandledExceptionRaiser += (sender, args) =>
{
    args.Handled = true;
    args.Exception.DisplayError();
};
#elif WINDOWS
AppDomain.CurrentDomain.FirstChanceException += (_, args) => lastFirstChanceException = args.Exception;
Microsoft.UI.Xaml.Application.Current.UnhandledException += (sender, args) =>
{
    Exception exception = args.Exception;
    if (exception.StackTrace == null)
    {
        exception = lastFirstChanceException;
    }
    args.Handled = true;
    args.Exception.DisplayError();
};
#endif

После добавления Foundation.NSRunLoop.Current.RunUntil(Foundation.NSDate.DistantFuture) всё стало работать почти так, как надо. При необработанном исключении действительно отображается сообщение с ошибкой, при этом приложение продолжает работать!
Но есть один нюанс... Это работает лишь один раз! Если произойдёт второе необработанное исключение, то приложение просто закроется.

Способ, который я нашёл, это просто какой-то костыль, который я нашел здесь. Причём другие способы не оказали никакого эффекта. Вот я и решил спросить у вас, знает ли кто, что мне делать в такой ситуации.


Ответы (1 шт):

Автор решения: Егор

Я ничего толкового не нашёл в интернете, а если и находил, то там лишь говорили, что приложение в любом случае закроется.
После небольших тестов я понял, что Foundation.NSRunLoop.Current.RunUntil(Foundation.NSDate.DistantFuture) можно заменить на Foundation.NSRunLoop.Current.Run(), а так же, что этот метод не завершает свою работу, пока не произойдёт другая ошибка.
То есть метод, который вызывается в событии AppDomain.CurrentDomain.UnhandledException будет работать вечно, вызывая самого себя, будет что-то типа рекурсии, из-за чего ошибка во второй раз не будет отображена пользователю и приложение закроется.
Ну и я решил зайти в Platforms/IOS/Program.cs и написать там это:

public sealed class Program
{
    private static void Main(string[] args)
    {
        bool launching = true;
        while (true)
        {
            try
            {
                if (launching)
                {
                    launching = false;
                    UIApplication.Main(args, null, typeof(AppDelegate));
                }
                else NSRunLoop.Current.Run();
            }
            catch (Exception ex)
            {
                ex.DisplayError();
            }
        }
    }
}

То есть, когда приложение только запускается, то вызывается UIApplication.Main, который отрисовывает приложение, а когда происходит исключение, то мы его ловим, затем отображаем пользователю, после чего отрисовкой приложения занимается NSRunLoop.Current.Run и так идёт по кругу.
Это единственный рабочий вариант, но не без подвоха!
Подвох заключается в том, что исключение может произойти в момент того, как отрисовывается какой-нибудь UI-элемент, из-за чего он просто сломается, например: если во время навигации на какую-нибудь страницу где-нибудь произойдёт исключение, то кнопка навигации назад перестанет работать правильно и при нажатии на неё приложение зависнет. Это значит, что придётся проводить дополнительные махинации, чтобы всё это вернуть на круги своя.
На Windows и Android это не происходит, то есть это побочный эффект данного обходного путя.

Ну а подпись на событие AppDomain.CurrentDomain.UnhandledException в билдах для iOS и MacOS можно исключить из-за ненадобности.

→ Ссылка