Search code examples
c#iosunity-game-enginefirebase-cloud-messagingapple-push-notifications

Unity app hangs / crashes upon Firebase Messaging token event initialization


I have an IOS build of my mobile app made in the Unity engine which always hangs when I test on any apple device I have available. By hang, I mean the Unity splash logo appears as normal but then my first scene does not load, instead presenting just black. Closing the app presents a crash report dialogue.

There is also a released android version which functions without issues including this one and firebase cloud messaging works fine there.

I have followed the official documentation to the letter and the issue occurs when the below code are included:

public void Start()
{
    Firebase.Messaging.FirebaseMessaging.TokenReceived += OnTokenReceived;
    Firebase.Messaging.FirebaseMessaging.MessageReceived += OnMessageReceived;
}
    
public void OnTokenReceived(object sender, TokenReceivedEventArgs token) => Debug.Log("Received Registration Token: " + token.Token);
    
public void OnMessageReceived(object sender, MessageReceivedEventArgs e) => Debug.Log("Received a new message from: " + e.Message.From);

This code is included in a class that exists in the first scene loaded and is run as soon as the Start method is called automatically.

Additional details: Unity Version - 2022.3.29 (Running on and built with Windows 11) Firebase SDK Version - 12.0.0 (Also tested 11.6.0) Firebase packages installed - Analytics, Crashlytics & Messaging

IOS build folder copied to a Macbook running MacOS Sonoma 14.5 with Xcode.

IOS Testing devices = Iphone 12 Pro Max (IOS 17.4) & Ipad 8th Gen (IpadOS 17.5)

Testing using TestFlight.

As soon as I remove the two lines in the Start method, the app will run normally, however then notifications do not work.

I also have a custom push notifications authorization class which appears to work fine as the notification authorization dialoge appears when the app starts and that code still runs normally when the above is removed without hanging.

IOS build process involves building the app in Unity with IOS as build target, copying to Macbook, installing pod file from terminal with Cocoapods then opening in Xcode with newly generated project file. I then sign with my provisioning profile, include the capabilities (Push Notifications, Background Modes with remote notifications checked). I also add UserNotifications.Framework to the correct assembly. Then I archive to AppStoreConnect to access in TestFlight.

I have set up the APN key, certificates and profile and linked to Firebase Console which is also reporting the analytics normally.

I am unable to obtain any logs from Xcode or run the app in Xcode as I do not have physical access to the Macbook. It is stored in a remote location andonly used for signing and building. I am limited to debugging mostly through trial and error, in which I narrowed it down to the FirebaseMessaging TokenReceived and MessageReceived lines.

I have already spent a lot of time scouring the internet for solutions, similar questions etc. Any help would be extremely appreciated!


Solution

  • I eventually have figured out the answer to this.

    The messaging tokens were being subscribed to before Firebase had finished initialising.

    To fix the issue, I moved the event subscribers out of the Start() method into their own method and then set up an action event in the initialiser class linked to a static boolean that determines if Firebase has been successfully initialised. Once initialised, it then invokes the event, the messaging class listens for it then calls the subscriber method when the event is received.

    Since then it has not crashed at all.

    Here is the code for the FB Initialiser:

    public class FirebaseSetup : MonoBehaviour
    {
        private static bool isFirebaseReady;
        public static event Action OnFirebaseReady;
        public static bool FirebaseReady
        {
            get => isFirebaseReady;
    
            set
            {
                if (isFirebaseReady != value)
                {
                    isFirebaseReady = value;
                    OnFirebaseReady?.Invoke();
                }
            }
        }
    
        public static void CheckIfFBReady()
        {
            FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task => {
                var dependencyStatus = task.Result;
                if (dependencyStatus == DependencyStatus.Available)
                {
    
                    FirebaseApp app = FirebaseApp.DefaultInstance;
    
                    Crashlytics.ReportUncaughtExceptionsAsFatal = true;
    
                    isFirebaseReady = true;
                    FirebaseAnalytics.LogEvent(FirebaseAnalytics.EventLogin);
    
                    print("Firebase setup initialised successfully");
                }
                else
                {
                    isFirebaseReady = false;
                    Debug.LogError(string.Format(
                      "Could not resolve all Firebase dependencies: {0}", dependencyStatus));
                }
            });
        }
    }
    

    Here is the code for the Messaging class:

        public class FBMessaging : MonoBehaviour
        {
        #if !UNITY_EDITOR
            private void OnEnable() => FirebaseSetup.OnFirebaseReady += GetTokenEvent;
    
            private void OnDisable() => FirebaseSetup.OnFirebaseReady -= GetTokenEvent;
    
            private void GetTokenEvent()
            {
                FirebaseMessaging.TokenReceived += OnTokenReceived;
                FirebaseMessaging.MessageReceived += OnMessageReceived;
            }
    
            private void OnDestroy()
            {
                FirebaseMessaging.TokenReceived -= OnTokenReceived;
                FirebaseMessaging.MessageReceived -= OnMessageReceived;
            }
    
            private void OnTokenReceived(object sender, TokenReceivedEventArgs token) => Debug.Log("Received Registration Token: " + token.Token);
    
            private void OnMessageReceived(object sender, MessageReceivedEventArgs e) => Debug.Log("Received a new message from: " + e.Message.From);
        #endif
        }
    

    I have no idea why it only crashed on IOS and not Android with the previous setup but at least it is working on all now.

    I hope if anyone stumbles on this issue in the future that this helps.