From ccd78b9cf5d6961f5252e582ede785932599d25d Mon Sep 17 00:00:00 2001 From: Mike Hardy Date: Fri, 17 May 2024 15:30:32 -0500 Subject: [PATCH] fix(messaging, ios): register for notifications on permission grant Related #7272 - the only difference between react-native-permissions and our requestPermission implementation was an immediate registration for notifications after a successful grant Assuming you would never request permission if you did not want to receive notifications, we now copy this behavior This allows messaging registration and APNS token tests to pass again on capable simulators (after altering tests to use the new permission behavior) --- packages/messaging/e2e/messaging.e2e.js | 58 +++++++++---------- .../ios/RNFBMessaging/RNFBMessagingModule.m | 9 +++ 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/packages/messaging/e2e/messaging.e2e.js b/packages/messaging/e2e/messaging.e2e.js index 1d02fc12cb..53cb47343f 100644 --- a/packages/messaging/e2e/messaging.e2e.js +++ b/packages/messaging/e2e/messaging.e2e.js @@ -37,6 +37,16 @@ async function isAPNSCapableSimulator() { } describe('messaging()', function () { + before(async function () { + // our device registration tests require permissions. Set them up + await firebase.messaging().requestPermission({ + alert: true, + badge: true, + sound: true, + provisional: true, + }); + }); + describe('firebase v8 compatibility', function () { describe('namespace', function () { it('accessible from firebase.app()', function () { @@ -98,8 +108,6 @@ describe('messaging()', function () { if (device.getPlatform() === 'ios') { await firebase.messaging().unregisterDeviceForRemoteMessages(); should.equal(firebase.messaging().isDeviceRegisteredForRemoteMessages, false); - // did this happen in logs? - // 2024-02-02 18:35:26.277 Df testing[26266:18d3f] (Detox) 10.20.0 - [FirebaseMessaging][I-FCM002022] Declining request for FCM Token since no APNS Token specified tryToRegister = await isAPNSCapableSimulator(); if (tryToRegister) { await firebase.messaging().registerDeviceForRemoteMessages(); @@ -112,27 +120,21 @@ describe('messaging()', function () { }); describe('hasPermission', function () { - it('returns true android (default)', async function () { - if (device.getPlatform() === 'android') { - should.equal(await firebase.messaging().hasPermission(), true); - } else { - this.skip(); - } - }); - - it('returns -1 on ios (default)', async function () { - if (device.getPlatform() === 'ios') { - should.equal(await firebase.messaging().hasPermission(), -1); - } + // we request permission in a before block, so both should be truthy + it('returns truthy', async function () { + should.equal(!!(await firebase.messaging().hasPermission()), true); }); }); describe('requestPermission', function () { - it('resolves 1 on android', async function () { + // we request permission in a before block + it('resolves correctly for default request', async function () { if (device.getPlatform() === 'android') { - should.equal(await firebase.messaging().requestPermission(), 1); + // our default resolve on android is "authorized" + should.equal(await firebase.messaging().requestPermission({ provisional: true }), 1); } else { - this.skip(); + // our default request on iOS results in "provisional" + should.equal(await firebase.messaging().requestPermission({ provisional: true }), 2); } }); }); @@ -585,30 +587,22 @@ describe('messaging()', function () { }); describe('hasPermission', function () { - it('returns true android (default)', async function () { + it('returns true', async function () { + // our before block requests permission, so both should be truthy const { getMessaging, hasPermission } = messagingModular; - if (device.getPlatform() === 'android') { - should.equal(await hasPermission(getMessaging()), true); - } else { - this.skip(); - } - }); - - it('returns -1 on ios (default)', async function () { - const { getMessaging, hasPermission } = messagingModular; - if (device.getPlatform() === 'ios') { - should.equal(await hasPermission(getMessaging()), -1); - } + should.equal(!!(await hasPermission(getMessaging())), true); }); }); describe('requestPermission', function () { - it('resolves 1 on android', async function () { + it('resolves correctly for default request', async function () { const { getMessaging, requestPermission } = messagingModular; + // our before block requests, android will always be 1 if (device.getPlatform() === 'android') { should.equal(await requestPermission(getMessaging()), 1); } else { - this.skip(); + // ... and iOS should always be 2 (provisional) + should.equal(await requestPermission(getMessaging(), { provisional: true }), 2); } }); }); diff --git a/packages/messaging/ios/RNFBMessaging/RNFBMessagingModule.m b/packages/messaging/ios/RNFBMessaging/RNFBMessagingModule.m index 128353fb49..d5e845815a 100644 --- a/packages/messaging/ios/RNFBMessaging/RNFBMessagingModule.m +++ b/packages/messaging/ios/RNFBMessaging/RNFBMessagingModule.m @@ -284,6 +284,15 @@ - (NSDictionary *)constantsToExport { if (error) { [RNFBSharedUtils rejectPromiseWithNSError:reject error:error]; } else { + // if we do not attempt to register immediately, registration fails + // later unknown reason why, but this was the only difference between + // using a react-native-permissions vs built-in permissions request in + // a sequence of "request permissions" --> "register for messages" you + // only want to request permission if you want to register for + // messages, so we register directly now - see #7272 + dispatch_async(dispatch_get_main_queue(), ^{ + [[UIApplication sharedApplication] registerForRemoteNotifications]; + }); [self hasPermission:resolve:reject]; } }];