ionic-team / capacitor

Build cross-platform Native Progressive Web Apps for iOS, Android, and the Web ⚡️
https://capacitorjs.com
MIT License
11.44k stars 977 forks source link

iOS PushNotifications.register listener not firing #2654

Closed DevonBernard closed 4 years ago

DevonBernard commented 4 years ago

Bug Report

Capacitor Version

npx cap doctor output:

💊   Capacitor Doctor  💊 

Latest Dependencies:

  @capacitor/cli: 1.5.2

  @capacitor/core: 1.5.2

  @capacitor/android: 1.5.2

  @capacitor/ios: 1.5.2

Installed Dependencies:

  @capacitor/android not installed

  @capacitor/cli 1.5.2

  @capacitor/ios 1.5.2

  @capacitor/core 1.5.2

  Found 0 Capacitor plugins for ios:
[success] iOS looking great! 👌

Affected Platform(s)

Current Behavior

When I call PushNotifications.register() and listen for the function to resolve neither registration nor registrationError are called.

On my phone I see the permission popup and I hit "Allow" or "Don't Allow". In the XCode debugger I see the register is happening natively, but my callback is never triggered.

⚡️  To Native ->  PushNotifications register 130675206
⚡️  [log] - Attempt register
⚡️  To Native ->  PushNotifications addListener 130675207
⚡️  To Native ->  PushNotifications addListener 130675208
⚡️  To Native ->  PushNotifications addListener 130675209
⚡️  To Native ->  PushNotifications addListener 130675210

Expected Behavior

After hitting "Allow" I'd expect to see logs like this:

⚡️  To Native ->  PushNotifications register 130675206
⚡️  [log] - Attempt register
⚡️  To Native ->  PushNotifications addListener 130675207
⚡️  To Native ->  PushNotifications addListener 130675208
⚡️  To Native ->  PushNotifications addListener 130675209
⚡️  To Native ->  PushNotifications addListener 130675210
⚡️  [log] - Push registration success, token: XXX

and have my callback triggered.

Sample Code or Sample Application Repo

import React from 'react';
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonButton } from '@ionic/react';
import { Plugins, PushNotification, PushNotificationToken, PushNotificationActionPerformed } from '@capacitor/core';

import './Tab2.css';

const { PushNotifications } = Plugins;

class Tab2 extends React.Component {
  constructor(props) {
    super(props);
    this.state = { notifications: [] };
  }
  registerPushNotifications() {
    console.log('Attempt register');
    PushNotifications.register();

    // On success, we should be able to receive notifications
    PushNotifications.addListener('registration', 
      (token) => {
        console.log('Push registration success, token: ' + token.value);
      }
    );

    // Some issue with our setup and push will not work
    PushNotifications.addListener('registrationError', 
      (error) => {
        console.log('Error on registration: ' + JSON.stringify(error));
      }
    );

    // Show us the notification payload if the app is open on our device
    PushNotifications.addListener('pushNotificationReceived', 
      (notification) => {
        console.log('Push received: ' + JSON.stringify(notification));
      }
    );

    // Method called when tapping on a notification
    PushNotifications.addListener('pushNotificationActionPerformed', 
      (notification) => {
        console.log('Push action performed: ' + JSON.stringify(notification));
      }
    );
  }

  render() {
    return (
      <IonPage>
        <IonHeader>
          <IonToolbar>
            <IonTitle>Tab 2</IonTitle>
          </IonToolbar>
        </IonHeader>
        <IonContent>
          <IonHeader collapse="condense">
            <IonToolbar>
              <IonTitle size="large">Tab 2</IonTitle>
            </IonToolbar>
          </IonHeader>
          <div className="container">
            <IonButton color="primary" onClick={this.registerPushNotifications}>Register Push Notifications</IonButton>
          </div>
        </IonContent>
      </IonPage>
    );
  }
};

export default Tab2;

My package JSON:

{
  "name": "myproject",
  "version": "0.0.1",
  "private": true,
  "dependencies": {
    "@capacitor/core": "1.5.2",
    "@capacitor/ios": "^1.5.2",
    "@ionic/react": "^5.0.0",
    "@ionic/react-router": "^5.0.0",
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.4.0",
    "@testing-library/user-event": "^8.0.3",
    "@types/jest": "^24.0.25",
    "@types/node": "^12.12.24",
    "@types/react": "^16.9.17",
    "@types/react-dom": "^16.9.4",
    "@types/react-router": "^5.1.4",
    "@types/react-router-dom": "^5.1.3",
    "ionicons": "^5.0.0",
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    "react-router": "^5.1.2",
    "react-router-dom": "^5.1.2",
    "react-scripts": "3.4.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "@capacitor/cli": "1.5.2"
  },
  "description": "An Ionic project"
}

AppDelegate.swift

...
  func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    NotificationCenter.default.post(name: Notification.Name(CAPNotifications.DidRegisterForRemoteNotificationsWithDeviceToken.name()), object: deviceToken)
    /*Messaging.messaging().apnsToken = deviceToken
    InstanceID.instanceID().instanceID { (result, error) in
        if let error = error {
            NotificationCenter.default.post(name: Notification.Name(CAPNotifications.DidFailToRegisterForRemoteNotificationsWithError.name()), object: error)
        } else if let result = result {
            NotificationCenter.default.post(name: Notification.Name(CAPNotifications.DidRegisterForRemoteNotificationsWithDeviceToken.name()), object: result.token)
        }
    }*/
  }
...

Reproduction Steps

Follow this setup guide. Try running iOS app from XCode.

Also note, I've tried with and without removing didRegisterForRemoteNotificationsWithDeviceToken code from AppDelegate.swift as suggested by Issue 2614.

Other Technical Details

npm --version output: 6.11.3

node --version output: v12.11.1

pod --version output (iOS issues only): 1.9.1

Other Information

I've seen similar github issues (only some resolved), but following them hasn't resolved my issue. Issue 2268, Issue 2247, Issue 2614

NiklasWilson commented 4 years ago

@DevonBernard What did you do to resolve this?

DevonBernard commented 4 years ago

@NiklasWilson I have no clue. I just kept running npx cap sync ios, cleaning XCode build, re-building project, swapping target devices, then suddenly it started working. Unfortunately, after a few releases, I was just debugging and after swapping my provisioning profile and swapping it back, it's totally broken again. If I find a fix, I'll post it here. 😞

DevonBernard commented 4 years ago

Fixed it again. Previously I was trying version 2.0.0 of capacitor/core, capacitor/ios and capacitor/android. It didn't work with either PushNotifications.requestPermission() or PushNotifications.register(). Previously after downgrading to version 1.5.2, npx cap sync, and cd ios/app pod install then ionic capacitor copy it worked.

This time I was running just 1.5.2 and PushNotifications.register() didn't work. But after upgrading back to version 2.0.0 and using PushNotifications.requestPermission() it worked. Not sure why the build originally broke; but seems like changing capacitor packages and syncing fixes it. I suspect whatever ended up breaking; changing the npm packages cleared the cache in a way that clearing in XCode didn't.

I still don't fully understand what exactly happened. But changing version, syncing, pod install, then copy seemed to fix the issue.

NiklasWilson commented 4 years ago

Actually I think I have the answer for any future readers. I was using v2.0.0 for capacitor iOS but my capacitor core was still pre-v2 (maybe 1.5).

Now that everything related to capacitor is running v2 everything seems to be working. I also did a lot of what you said, so maybe it was a combo but Im fairly certain that the core being on v2 as well was key to the solution.

Thanks for your response though. Im sure someone else will run into your post eventually.

jsamol commented 4 years ago

Version switching and/or combination of syncing, copying and installing Pods was only a temporary and unstable solution in my case (the listener would not be notified after the app was relaunched, for example).

I did some debugging and research and it seems that the problem lies in thedidRegisteredForRemoteNotificationsWithDeviceToken method not being called after device has been successfully registered with Firebase. From what I've found the reason may be method swizzling performed by the FCM SDK. It can be disabled, though, by setting FirebaseAppDelegateProxyEnabled flag to NO in Info.plist. And it was what eventually resolved the problem for me. At least for now 🤞.

It would be good to confirm if that's actually the source of the issue. If yes, then perhaps it should be included it in the setup guide.

NiklasWilson commented 4 years ago

Ah interesting. I only use Firebase for Android so maybe that's why I have not noticed that yet.

DoctorsInTech commented 4 years ago

I am also having this issue, registration event fires as expected on android, but on iOS the event does not fire anymore, although it was firing reliably before. This is prohibiting me from registering the app with the backend (notifications hub). I have tried all the options above but no luck. I am currenlty running capacitor 2.1.2 on all packages. Did anybody find a more concrete solution, I am not using firebase so that's not the issue in my case.

santekotturi commented 4 years ago

[UPDATE] I tried again this morning and it's working. Maybe Apple just needed 12 hours to make my certificate available on their end. Still not sure why it would work on one phone and not others but now it's working across all device.


I have 3 iPhones, an 11 and 2x6s. The 11 gets a token, the other's don't. Running the exact same code. Is apple just hating on me for using old hardware?! All phones running iOS 13.5.1.

PushNotifications.requestPermission().then(
        (result) => {
          console.log('[NOTIFICATIONS] permission: ', result);
          if (result.granted) {
            // Register with Apple / Google to receive push via APNS/FCM
            PushNotifications.register().then((response) => {
              console.log('[NOTIFICATIONS] register complete: ', response);
            }, (err) => {
              console.error('[NOTIFICATIONS] register error: ', err);
            });
          } else {
            // Show some error
            alert('Push notifications not allowed');
          }
        },
        (err) =>
          console.error('[NOTIFICATIONS] ERROR requesting permission: ', err)
      );

      // On success, we should be able to receive notifications
      PushNotifications.addListener(
        'registration',
        (token: PushNotificationToken) => {
          // token.value contains the FCM token needed to register with firebase.
          console.log(
            '[NOTIFICATIONS] Push registration success, token: ' + token.value
          );
          localStorage.setItem('FCM_TOKEN', JSON.stringify(token.value));
          this.token.next(token.value);
          this.userSvc.user$.subscribe((user) => {
            if (user) {
              console.log(
                '[NOTIFICATIONS] adding FCM token to user: ',
                user.UID
              );
              user.FCM_TOKEN = token.value;
              this.userSvc.updateUser(user);
            }
          });
        }
      );

iPhone 6 result:

⚡️  [log] - [NOTIFICATIONS] permission:  {"granted":true}
⚡️  [log] - [NOTIFICATIONS] register complete:  {}

iPhone 11 result:

⚡️  [log] - [NOTIFICATIONS] permission:  {"granted":true}
⚡️  [log] - [NOTIFICATIONS] register complete:  {}
⚡️  [log] - [NOTIFICATIONS] Push registration success, token: d2euglUPVl0...long string

Running ionic 5.0.7. capacitor 2.2.0 cli/core/ios.

[UPDATE] : tried on an iPad Pro. No token despite permission granted = true.

beaunus commented 4 years ago

I still don't fully understand what exactly happened. But changing version, syncing, pod install, then copy seemed to fix the issue.

For me, pod install fixed it.

Horst1102 commented 4 years ago

For me nothing helped. On my iPhone 11 it worked fine, but somehow it stopped working. On my iPad (2018) it is working. Same code base.

Anybody have an idea?

NethajiV commented 3 years ago

For me also nothing worked. Trying for days, finally I got this error on iPhone XS

[Firebase/Messaging][I-FCM012002] Error in application:didFailToRegisterForRemoteNotificationsWithError: no valid \M-b\M^@\M^\aps-environment\M-b\M^@\M^] entitlement string found for application

But in iPhone 6, I got this log [Firebase/Messaging][I-FCM001000] FIRMessaging Remote Notifications proxy enabled, will swizzle remote notification receiver handlers. If you'd prefer to manually integrate Firebase Messaging, add "FirebaseAppDelegateProxyEnabled" to your Info.plist, and set it to NO. Follow the instructions at: https://firebase.google.com/docs/cloud-messaging/ios/client#method_swizzling_in_firebase_messaging

I tried with both .p8 and .p12 certificates.

Any help?

freite commented 3 years ago

I had the same issue on iOS. Calling PushNotifications.addListener('registration', ... BEFORE PushNotifications.register() fixed the issue. This is the opposite of the official example code.

amilamen commented 3 years ago

I had the same issue on iOS. Calling PushNotifications.addListener('registration', ... BEFORE PushNotifications.register() fixed the issue. This is the opposite of the official example code.

You suggest to call PushNotifications.addListener('registration', ...) before PushNotifications.register() ?

MASP3000 commented 3 years ago

I had the same issue on iOS. Calling PushNotifications.addListener('registration', ... BEFORE PushNotifications.register() fixed the issue. This is the opposite of the official example code.

You suggest to call PushNotifications.addListener('registration', ...) before PushNotifications.register() ?

Do you have an code example?

Stefano1990 commented 3 years ago

I have the same problem. I don't think this issue should be closed?

szymek commented 3 years ago

Same problem here. Any idea how to solve it?

pedramp20 commented 3 years ago

Any updates? I am having the same issue

dolosplus commented 3 years ago

anyone resolve this issue? I'm having the same problem.

kotunde commented 3 years ago

After spending at least 3 days with trying to find a solution, finally digged into the native code and overwrote the function that corresponds to the "pushNotificationActionPerformed" listener. By the way, if you only need the "registration" listener, an alternative is the @capacitor-community/fcm package, and use fcm.getToken() to get registration token. For me the PushNotifications.requestPermission().then( . . . version did not work at all, the code blocked from running further at this point, because no permission is requested

jussikinnula commented 3 years ago

I have the same issue with Capacitor 3.

I'll probably check out @capacitor-community/fcm package next.

jussikinnula commented 3 years ago

Apparently I was missing the parts needed on my AppDelegate.swift, which fixed the problem: https://capacitorjs.com/docs/apis/push-notifications#ios

heloufir commented 2 years ago

Apparently I was missing the parts needed on my AppDelegate.swift, which fixed the problem: https://capacitorjs.com/docs/apis/push-notifications#ios

That was the problem for me too, after adding the 2 functions as mentionned here (https://capacitorjs.com/docs/apis/push-notifications#ios) the problem was fixed.

manuelpgs commented 2 years ago

Apparently I was missing the parts needed on my AppDelegate.swift, which fixed the problem: https://capacitorjs.com/docs/apis/push-notifications#ios

This was my issue too, to get the token using:

PushNotifications.addListener('registration', (token: Token) => { // in this way, the token doesn't work for iOS!!!! console.log('Getting token with addListener to registration: ' + JSON.stringify(token));

...but, that token that this method return doesn't work to send a direct message to a device from Firebase Console or SDK, this token look like (without those dots):

444199BA8F75593793B ....... 333654C19DD58FF53A0F8C2D133EE1EE14B

So, I was forced to used @capacitor-community/fcm package in this way:

if (Capacitor.getPlatform() === 'ios') { // in this way, the token return doesn't work for Android!!! FCM.getToken() .then((r) => this.sendDeviceTokenToAPI(r.token, 'FCM') ) .catch((err) => console.log('Error getting token with FCM: ', err)); }

And that token looks like (without those dot):

hSiYKNNXlEcKuocSI6x3Wo:APA91bGR......gzSz8Qfw4bBPeW5Lx3MdRZBkRdA7-xEXMNHlezUyWwc6pIxqyMJ_d18JrsF6wzCebkxfysdCt9UxRDKsubr8ZfXktPFETdVaA

sebas9673 commented 2 years ago

I got it working by installing FCM, even though I'm not using it.

    PushNotifications.addListener('registration',
    (token: Token) => {
            console.log('Push registration success, token: ' + token.value);
    }
  );
SaschaLiebmann commented 2 years ago

I solved it by deleting the

if USE_PUSH

...

endif

Inside my AppDelegate.swift file around the functions for

    didRegisterForRemoteNotificationsWithDeviceToken
    didFailToRegisterForRemoteNotificationsWithError

Seems for some Reason these excluded me from registering my callbacks correctly

cyrilfr commented 1 year ago

Did you find a definitive solution?

I added the code from here in my AppDelegate.swift file:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
  NotificationCenter.default.post(name: .capacitorDidRegisterForRemoteNotifications, object: deviceToken)
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
  NotificationCenter.default.post(name: .capacitorDidFailToRegisterForRemoteNotifications, object: error)
}

But here the procedure and the native code is totally different!

Which one is the good one?

daniel-0318 commented 1 year ago

Did you find a definitive solution?

I added the code from here in my AppDelegate.swift file:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
  NotificationCenter.default.post(name: .capacitorDidRegisterForRemoteNotifications, object: deviceToken)
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
  NotificationCenter.default.post(name: .capacitorDidFailToRegisterForRemoteNotifications, object: error)
}

But here the procedure and the native code is totally different!

Which one is the good one?

this work for me, thanks you

NicoHof commented 1 year ago

Hi! 👋 Unfortunately I still have the issue described above. the registration event is not fired after calling PushNotifications.register();

  1. On my android build the javascript part for registration is working fine when I start the app (so I assume the issue is not related to my vue.js integration in ionic)
  2. I added the capabilities for push notifications as described here https://capacitorjs.com/docs/v3/ios/configuration#setting-capabilities
  3. I added the two functions in AppDelegate.swift as described by @jussikinnula
  4. IPhone asks me for permissions for push (which I agree), after that my app calls PushNotifications.register();

This is what I get in my safari debug console after registering the listeners and calling the register function

Bildschirmfoto 2022-09-17 um 13 41 05

For me it looks like everything is working so far? But after that the registration listener callback never fires and logs my token...

I am running out of ideas what I am missing. Two thoughts left:

  1. I missed to somehow rebuild/clear the app cache correct after adding those 2 functions? (I did pod install, npx cap sync, rebuild in xcode, restarted xcode, ...)
  2. This last step is just not working in the simulator? (Somewhere I read about push notifications will only work on real Devices? - But I thought this is just about really receiving notifications later and not about registration/logging the token?)

Setup:

I am thankful for every hint.

WHeesters commented 1 year ago

@NicoHof I'm running into the exact same issue, did you ever figure out what's wrong?

NicoHof commented 1 year ago

@WHeesters Yes, I solved this issue by using a real device for testing. The simulator asks for permissions (which was kinda misleading for me) but never fires the event.

So just use a real hardware device and you should be fine if everything else is prepared properly.

WHeesters commented 1 year ago

@NicoHof Thanks for the rapid response, that was one part of my issue. The other thing was that I added the listeners before calling register(). Now I call register before adding the listeners and it seems to be working.

ionitron-bot[bot] commented 1 year ago

Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of Capacitor, please create a new issue and ensure the template is fully filled out.