Running this in a detached expo app can sometimes deadlock from thread issues.
I've replaced `dispatch_sync(dispatch_get_main_queue(), ^{});` with `RCTUnsafeExecuteOnMainQueueSync(^{});` to fix this.
I only hit the error in the `RNFirebaseStorage.m` but it seems like the `RNFirebaseAnalytics.m` and `RNFirebase.m` modules could also have this same issue.
`RCTUnsafeExecuteOnMainQueueSync` checks to see if the method is being executed on the main thread before trying to sync to the main thread. If you try to sync on the main thread whilst on the main thread, you hit a deadlock.
You can read more about it here: https://stackoverflow.com/questions/12379059/why-is-this-dispatch-sync-call-freezing?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
The only testing I did was in a seperate detached ExpoKit project. Running this function would cause the crash.
```js
await firebase
.storage()
.ref(uploadUri)
.putFile(uri);
```
Once the notification is created the promise was not being resolved so in a situation where the user is waiting on the promise to complete, they would not get a callback and in case a user used async/await this would cause the app to get stuck at that point.
There are some cases when local notification action should be handled in
background eg. snoozing the reminder. In case of it launching app UI is
not necessary and would be confusing for the end user.
Therefore there should be a way to handle local notification action in
background.
For this reason new property 'runInBackground' was added to the
AndroidAction class and TypeScript type.
Also new broadcast receiver and service were implemented to handle
properly background actions.
In order to run particular action in background API consumer need to set its
'runInBackground' property to 'true', eg:
...
.android.addAction(new firebase.notifications.Android.Action("snooze",
"ic_snooze", "Snooze").setRunInBackground(true))
...
Then, there are two cases that API consumer needs to handle.
First when app is in the foreground, standard notification and
notification action code path will be executed. This mean, that:
* onNotification() listener will be called (which should call
displayNotification(), in order to show it to the user),
* onNotificationOpen() listener will be called after the action is
tapped by the user
Secondly, when application is in background or it is not running new
'RNFirebaseBackgroundNotificationAction' handler will be called. To
properly handle this case API consumer should create a background
asynchronous handler:
const handleAsyncTask = async (notificationOpen: NotifficationOpen) => {
if (notificationOpen && notificationOpen.notification) {
const action = notificationOpen.action;
const notificationId = notificationOpen.notification.notificationId;
if (action === "snooze") {
console.log("Reschedule notification for later time", notificationId);
} else {
console.log("unsupported action", action);
}
// hide the notification
firebase.notifications().removeDeliveredNotification(notificationId);
}
}
Next hander should be registered to headless handler:
AppRegistry.registerHeadlessTask('RNFirebaseBackgroundNotificationAction', () => handleAsyncTask);
Finally AndroidManifest.xml file must be modified, to include receiver
and service definition:
<receiver
android:name="io.invertase.firebase.notifications.RNFirebaseBackgroundNotificationActionReceiver"
android:exported="true">
<intent-filter>
<action android:name="io.invertase.firebase.notifications.BackgroundAction"/>
</intent-filter>
</receiver>
<service android:name="io.invertase.firebase.notifications.RNFirebaseBackgroundNotificationActionsService"/>
Now when ever 'Snooze' action is pressed it will launch
'handleAsyncTask' function in the background or onNotificationOpen()
when app is in foreground. And reschedule the notification
for the later time.