Share via

.NET MAUI iOS Firebase Crashlytics Not Receiving Crash Reports (Analytics Working)

Vignesh Palthurai 40 Reputation points
2026-06-03T11:20:55.2433333+00:00

Hi @Nancy Vo (WICLOUD CORPORATION)
I'm integrating Firebase Analytics and Crashlytics in a .NET MAUI application using:

<PackageReference Include="Plugin.Firebase" Version="4.0.0" />
<PackageReference Include="Plugin.Firebase.Analytics" Version="4.0.0" />
<PackageReference Include="Plugin.Firebase.Crashlytics" Version="4.0.0" />

Current status:

  • Android Analytics - Working
  • Android Crashlytics - Working
  • iOS Analytics - Working
  • iOS Crashlytics - Not working

AppDelegate:

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    try
    {
        var path = NSBundle.MainBundle.PathForResource("GoogleService-Info", "plist");
        Console.WriteLine("PLIST PATH: " + path);

        CrossFirebase.Initialize("default");
    }
    catch (Exception ex)
    {
        Console.WriteLine("Firebase init failed: " + ex);
    }

    return base.FinishedLaunching(app, options);
}

iOS configuration:

<ItemGroup Condition="'$(TargetFramework)' == 'net10.0-ios'">
    <MauiAsset Include="Platforms/iOS/GoogleService-Info.plist"
               LogicalName="GoogleService-Info.plist" />
</ItemGroup>

Crash test method:

[RelayCommand]
private void TestFirebaseCrash()
{
    throw new Exception("Firebase Crash Test");
}

Additional information:

When I configure GoogleService-Info.plist as BundleResource, Analytics does not work and the following returns null:

var path = NSBundle.MainBundle.PathForResource("GoogleService-Info", "plist");
Console.WriteLine("PLIST PATH: " + path);

For Analytics to work, I had to configure the plist as MauiAsset as shown above.

With this setup:

Analytics events are successfully received in Firebase for iOS.

Firebase Crashlytics detects the iOS app.

Crashlytics dashboard shows "App detected. Waiting for a crash."

No fatal or non-fatal issues appear.

  • Tested on Browser Stack App Live (real iOS devices).

Is there any additional iOS-specific configuration required for Crashlytics?

Developer technologies | .NET | .NET Multi-platform App UI
0 comments No comments

Answer accepted by question author

Nancy Vo (WICLOUD CORPORATION) 5,865 Reputation points Microsoft External Staff Moderator
2026-06-16T02:42:16.5866667+00:00

Hello @Vignesh Palthurai ,

Sorry for the late response.

Please follow these steps:

  1. Choose a unique Bundle ID

In MauiApp2.csproj, set a unique bundle ID (free Apple accounts require it to be globally unique):

    <ApplicationId>com.nancy.mauiapp2</ApplicationId>
  1. Bundle the plist into the app

Place GoogleService-Info.plist in iOS and add to MauiApp2.csproj:

<ItemGroup Condition="'$(TargetFramework)' == 'net10.0-ios'">
  <BundleResource Include="Platforms\iOS\GoogleService-Info.plist">
    <Link>GoogleService-Info.plist</Link>
  </BundleResource>
</ItemGroup>

Note: The <Link> tag is critical — it places the file at the app bundle root where Firebase expects it.

  1. Add the following code to .csprj:
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
  <_ExportSymbolsExplicitly>false</_ExportSymbolsExplicitly>
</PropertyGroup>

<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
  <_ReferencesLinkerFlags Include="-u __mh_execute_header" />
</ItemGroup>
  1. Handle the dSYM warning (if it appears)

If you see a message like "1 unprocessed crash", it means the crash was captured, but Firebase needs the dSYM file to display a human-readable stack trace.

To upload it:

  • Locate the file: bin > Debug > net10.0-ios > MauiApp2.app.dSYM
  • In Firebase Console → Crashlytics → dSYMs tab → click Manage
  • Upload the .dSYM folder (zip it first)

After uploading, the crash will be fully symbolicated and readable.

Note: Please replace all private information such as my project name (MauiApp2) and ApplicationId with your own.

You can refer to my following example code:

.csprj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>net10.0-android</TargetFrameworks>
    <TargetFrameworks Condition="!$([MSBuild]::IsOSPlatform('linux'))">$(TargetFrameworks);net10.0-ios;net10.0-maccatalyst</TargetFrameworks>
    <TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net10.0-windows10.0.19041.0</TargetFrameworks>

    <!-- Note for MacCatalyst:
    The default runtime is maccatalyst-x64, except in Release config, in which case the default is maccatalyst-x64;maccatalyst-arm64.
    When specifying both architectures, use the plural <RuntimeIdentifiers> instead of the singular <RuntimeIdentifier>.
    The Mac App Store will NOT accept apps with ONLY maccatalyst-arm64 indicated;
    either BOTH runtimes must be indicated or ONLY macatalyst-x64. -->
    <!-- For example: <RuntimeIdentifiers>maccatalyst-x64;maccatalyst-arm64</RuntimeIdentifiers> -->

    <OutputType>Exe</OutputType>
    <RootNamespace>MauiApp2</RootNamespace>
    <UseMaui>true</UseMaui>
    <SingleProject>true</SingleProject>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>

    <!-- Enable XAML source generation for faster build times and improved performance.
         This generates C# code from XAML at compile time instead of runtime inflation.
         To disable, remove this line.
         For individual files, you can override by setting Inflator metadata:
           <MauiXaml Update="MyPage.xaml" Inflator="Default" /> (reverts to defaults: Runtime for Debug, XamlC for Release)
           <MauiXaml Update="MyPage.xaml" Inflator="Runtime" /> (force runtime inflation) -->
    <MauiXamlInflator>SourceGen</MauiXamlInflator>

    <!-- Display name -->
    <ApplicationTitle>MauiApp2</ApplicationTitle>

    <!-- App Identifier -->
    <ApplicationId>com.nancy.mauiapp2</ApplicationId>

    <!-- Versions -->
    <ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
    <ApplicationVersion>1</ApplicationVersion>

    <!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged -->
    <WindowsPackageType>None</WindowsPackageType>

    <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">15.0</SupportedOSPlatformVersion>
    <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">15.0</SupportedOSPlatformVersion>
    <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">23.0</SupportedOSPlatformVersion>
    <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
    <TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
  </PropertyGroup>

  <ItemGroup>
    <!-- App Icon -->
    <MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />

    <!-- Splash Screen -->
    <MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" />

    <!-- Images -->
    <MauiImage Include="Resources\Images\*" />
    <MauiImage Update="Resources\Images\dotnet_bot.png" Resize="True" BaseSize="300,185" />

    <!-- Custom Fonts -->
    <MauiFont Include="Resources\Fonts\*" />

    <!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
    <MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
  </ItemGroup>

  <ItemGroup>
    <None Remove="Platforms\Android\Resources\values\strings.xml" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Maui.Controls" Version="10.0.51" />
    <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="10.0.6" />
    <PackageReference Include="Plugin.Firebase" Version="4.0.0" />
    <PackageReference Include="Plugin.Firebase.Analytics" Version="4.0.0" />
    <PackageReference Include="Plugin.Firebase.Crashlytics" Version="4.0.0" />
  </ItemGroup>

  <ItemGroup Condition="'$(TargetFramework)' == 'net10.0-android'">
    <GoogleServicesJson Include="Platforms\Android\google-services.json" />
  </ItemGroup>

  <ItemGroup Condition="'$(TargetFramework)' == 'net10.0-ios'">
                <BundleResource Include="Platforms\iOS\GoogleService-Info.plist">
                        <Link>GoogleService-Info.plist</Link>
                </BundleResource>
  </ItemGroup>

  <PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
    <_ExportSymbolsExplicitly>false</_ExportSymbolsExplicitly>
  </PropertyGroup>

  <ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
    <_ReferencesLinkerFlags Include="-u __mh_execute_header" />
  </ItemGroup>

</Project>

MauiProgram.cs:


using Microsoft.Extensions.Logging;
using Microsoft.Maui.LifecycleEvents;

#if IOS || ANDROID
using Plugin.Firebase.Analytics;
using Plugin.Firebase.Crashlytics;
using Plugin.Firebase.Bundled.Shared;
#endif

#if IOS
using ObjCRuntime;
using Plugin.Firebase.Bundled.Platforms.iOS;
#elif ANDROID
using Plugin.Firebase.Bundled.Platforms.Android;
#endif

namespace MauiApp2;

public static class MauiProgram
{
    public static string? FirebaseInitError { get; private set; }
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();

        builder
            .UseMauiApp<App>()
            .RegisterFirebaseServices()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });

#if DEBUG
        builder.Logging.AddDebug();
#endif

        return builder.Build();
    }

    private static MauiAppBuilder RegisterFirebaseServices(this MauiAppBuilder builder)
    {
#if IOS || ANDROID
        builder.ConfigureLifecycleEvents(events =>
        {
#if IOS
            events.AddiOS(iOS => iOS.WillFinishLaunching((_, _) =>
            {
                try
                {
                    CrossFirebase.Initialize(CreateCrossFirebaseSettings());
                    CrossFirebaseCrashlytics.Current.SetCrashlyticsCollectionEnabled(true);
                    CrossFirebaseCrashlytics.Current.SendUnsentReports();

                    Runtime.MarshalManagedException += (_, args) =>
                    {
                        args.ExceptionMode = MarshalManagedExceptionMode.UnwindNativeCode;
                    };

                    AppDomain.CurrentDomain.UnhandledException += (_, args) =>
                    {
                        if (args.ExceptionObject is Exception ex)
                        {
                            CrossFirebaseCrashlytics.Current.Log($"Fatal: {ex.GetType().Name}: {ex.Message}");
                            CrossFirebaseCrashlytics.Current.RecordException(ex);
                        }
                    };
                }
                catch (Exception ex)
                {
                    FirebaseInitError = $"{ex.GetType().Name}: {ex.Message}";
                    System.Diagnostics.Debug.WriteLine($"Firebase init error: {ex}");
                }
                return true;
            }));
#elif ANDROID
            events.AddAndroid(android => android.OnCreate((activity, _) =>
            {
                CrossFirebase.Initialize(
                    activity,
                    () => Microsoft.Maui.ApplicationModel.Platform.CurrentActivity,
                    CreateCrossFirebaseSettings()
                );
                FirebaseAnalyticsImplementation.Initialize(activity);
            }));
#endif
        });

        builder.Services.AddSingleton(_ => CrossFirebaseAnalytics.Current);
        builder.Services.AddSingleton(_ => CrossFirebaseCrashlytics.Current);
#endif

        return builder;
    }

#if IOS || ANDROID
    private static CrossFirebaseSettings CreateCrossFirebaseSettings()
    {
        return new CrossFirebaseSettings(
            isAnalyticsEnabled: true,
            isCrashlyticsEnabled: true
        );
    }
#endif
}

MainPage.xaml:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiApp2.MainPage">
    <VerticalStackLayout Padding="30" Spacing="20" VerticalOptions="Center">
        <Label Text="Firebase Test App" FontSize="Large" HorizontalOptions="Center" />

        <Button Text="Log Analytics Event" Clicked="OnLogEventClicked" />

        <Button Text="Force Test Crash (Crashlytics)"
                Clicked="OnCrashClicked"
                BackgroundColor="Red" TextColor="White" />
    </VerticalStackLayout>
</ContentPage>

MainPae.xaml.cs:

using Plugin.Firebase.Analytics;
using Plugin.Firebase.Crashlytics;
#if IOS
using Foundation;
using System.Runtime.InteropServices;
#endif

namespace MauiApp2
{
    public partial class MainPage : ContentPage
    {
        int count = 0;

#if IOS
        [DllImport("libSystem.dylib")]
        private static extern void abort();
#endif

        public MainPage()
        {
            InitializeComponent();
        }

        private void OnLogEventClicked(object sender, EventArgs e)
        {
            CrossFirebaseAnalytics.Current.LogEvent("test_button_clicked",
                new Dictionary<string, object> { { "button", "analytics_test" } });

            DisplayAlert("Analytics", "Event sent to Firebase!!!", "OK");
        }

        private void OnCrashClicked(object sender, EventArgs e)
        {
            if (MauiProgram.FirebaseInitError != null)
            {
                DisplayAlert("Firebase Init FAILED", MauiProgram.FirebaseInitError, "OK");
                return;
            }

            if (CrossFirebaseCrashlytics.Current == null)
            {
                DisplayAlert("Crashlytics ERROR", "CrossFirebaseCrashlytics.Current is null — Firebase did not initialize.", "OK");
                return;
            }

            CrossFirebaseCrashlytics.Current.Log("About to trigger fatal test crash via abort()");

#if IOS
            abort();
#else
            throw new Exception("[Test] Fatal crash triggered by user");
#endif
        }
    }
}

AppDelegate.cs:

using Foundation;

namespace MauiApp2
{
    [Register("AppDelegate")]
    public class AppDelegate : MauiUIApplicationDelegate
    {
        protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
    }
}

I tested this on my end and it works. Please try and let me know the results. I'd happy to investigate further. User's image

User's image

User's image

If any part of my explanation helped address your question, I would greatly appreciate it if you could follow the instructions here. This can also help other community members facing similar scenarios. Thank you so much.

Was this answer helpful?

1 person found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. Nancy Vo (WICLOUD CORPORATION) 5,865 Reputation points Microsoft External Staff Moderator
    2026-06-04T08:16:40.73+00:00

    Hello @Vignesh Palthurai ,

    Thanks for reaching out.

    You can refer to the following steps:

    1. On iOS, throw new Exception() is treated as a managed .NET exception. The Mono runtime catches it before Crashlytics can detect it. I recommend replacing this:
    [RelayCommand]
    private void TestFirebaseCrash()
    {
        throw new Exception("Firebase Crash Test");
    }
    

    into this:

    [RelayCommand]
    private void TestFirebaseCrash()
    {
        CrossFirebaseCrashlytics.Current.TestIt();
    }
    
    [RelayCommand]
    private void TestFirebaseNonFatal()
    {
        try
        {
            throw new Exception("Firebase Crash Test");
        }
        catch (Exception ex)
        {
            CrossFirebaseCrashlytics.Current.RecordException(ex);
        }
    }
    
    1. FinishedLaunching fires too late for Crashlytics to properly set up its crash handlers. It must be initialized in WillFinishLaunching instead.

    Additionally, calling CrossFirebase.Initialize("default") with a string parameter may cause a "Default app has already been configured" crash. I recommend moving all Firebase initialization into MauiProgram.cs:

    using Microsoft.Extensions.Logging;
    using Microsoft.Maui.LifecycleEvents;
    
    #if IOS || ANDROID
    using Plugin.Firebase.Analytics;
    using Plugin.Firebase.Crashlytics;
    #endif
    
    #if IOS
    using Plugin.Firebase.Core.Platforms.iOS;
    #elif ANDROID
    using Plugin.Firebase.Core.Platforms.Android;
    #endif
    
    namespace MauiApp2;
    
    public static class MauiProgram
    {
        public static MauiApp CreateMauiApp()
        {
            var builder = MauiApp.CreateBuilder();
    
            builder
                .UseMauiApp<App>()
                .RegisterFirebaseServices()
                .ConfigureFonts(fonts =>
                {
                    fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                    fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
                });
    
    #if DEBUG
            builder.Logging.AddDebug();
    #endif
    
            return builder.Build();
        }
    
        private static MauiAppBuilder RegisterFirebaseServices(this MauiAppBuilder builder)
        {
    #if IOS || ANDROID
            builder.ConfigureLifecycleEvents(events =>
            {
    #if IOS
                events.AddiOS(iOS => iOS.WillFinishLaunching((_, _) =>
                {
                    CrossFirebase.Initialize();
                    CrossFirebaseCrashlytics.Current.SetCrashlyticsCollectionEnabled(true);
                    return false;
                }));
    #elif ANDROID
                events.AddAndroid(android => android.OnCreate((activity, _) =>
                {
                    CrossFirebase.Initialize(activity, () =>
                        Microsoft.Maui.ApplicationModel.Platform.CurrentActivity);
                    FirebaseAnalyticsImplementation.Initialize(activity);
                }));
    #endif
            });
    
            builder.Services.AddSingleton(_ => CrossFirebaseAnalytics.Current);
            builder.Services.AddSingleton(_ => CrossFirebaseCrashlytics.Current);
    #endif
            return builder;
        }
    }
    

    And keep AppDelegate.cs simple:

    [Register("AppDelegate")]
    public class AppDelegate : MauiUIApplicationDelegate
    {
        protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
    }
    
    1. I suggest creating an exported_symbols.txt file at Platforms/iOS/exported_symbols.txt. The iOS linker hides a symbol called __mh_execute_header, which Crashlytics requires to identify the app’s entry point.
    • Create the file with the following content:
    __mh_execute_header
    
    • And reference it in .csproj:
    <PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
        <MtouchExtraArgs>-exported_symbols_list $(MSBuildProjectDirectory)/Platforms/iOS/exported_symbols.txt</MtouchExtraArgs>
    </PropertyGroup>
    

    Before testing again, please rebuild and check logs:

    rm -rf bin obj
    dotnet build -f net10.0-ios
    

    Please share the results or logs afterward, and I’ll be happy to help further if needed.

    I hope this addresses your question. If this response was helpful, please consider following the guidance to provide feedback.

    Was this answer helpful?


Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.