ionic-team / capacitor

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

bug: Plugins stop working after re-compilation #2849

Closed DevonBernard closed 3 years ago

DevonBernard commented 4 years ago

Bug Report

Capacitor Version

npx cap doctor output: 💊 Capacitor Doctor 💊

Latest Dependencies:

@capacitor/cli: 2.0.2

@capacitor/core: 2.0.2

@capacitor/android: 2.0.2

@capacitor/electron: 2.0.2

@capacitor/ios: 2.0.2

Installed Dependencies:

@capacitor/electron not installed

@capacitor/cli 2.0.0

@capacitor/android 2.0.0

@capacitor/ios 2.0.0

@capacitor/core 2.0.0

Affected Platform(s)

Current Behavior

Whenever I re-compile my app ionic capacitor copy there is a ~40% chance that all my plugins stop working. The Plugins I'm currently using are [Device, PushNotifications, Storage, SplashScreen, and AdMob].

When I say stop working, there are two specific cases in mind:

  1. For PushNotifications, no callbacks are ever fired, I call PushNotifications.register();, but PushNotifications.addListener('registration') nor any other listener fire.
  2. More on PushNotifications, if the register callback is triggered, sometimes both foreground and background notifications continue to work, other times only background notifications work.
  3. For Storage, I call Storage.set() and it never even saves the value to memory.

I specifically notice this starts getting buggy when I install new npm packages or clear the cache of XCode or Android Studio.

Expected Behavior

After successfully confirming Plugins like PushNotifications and Storage work in iOS/Android... rebuilding the project with ionic capacitor copy should not stop Plugins from working.

If I clear my cache in XCode or Android Studio, I'd expect running npx cap sync && cd ios/app && pod install && cd ../../ && ionic capacitor copy should get everything re-synced and working again.

I'd even optionally add: rm -rf ./node_modules && npm install before the commands above, but that still seems to be flaky.

Sample Code or Sample Application Repo

Reproduction Steps

I made a Github Repo where you can clone an MVP of testing device, storage, and push-notification plugins. The README has all steps to setup and test.

In this demo repo:

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

rnwolfe commented 4 years ago

I have a similar issue. I can't get to the point where the registration occurs, due to issues with the requesting of permissions on the device. I am attempting to implement push notifications on iOS.

After troubleshooting this for hours, there seems to be minute but significant errors in the documentation that I am unable to work through.

After finally identifying that I need to update all @capacitor packages to @next errors continued to occur. My TypeScript wouldn't transpile when using PushNotification.requestPermission() (as shown here) due to requestPermission not existing in PushNotification.

It suggested requestPermissions() which is in the declaration file; however, that function doesn't operate the same as in the above-linked doc. I try to run it anyway with a couple of tweaks that allow TypeScript to transpile. When running on a side-loaded device, I get the following:

[error] - ERROR Error: Uncaught (in promise): TypeError: u.requestPermissions is not a function. (In 'u.requestPermissions()', 'u.requestPermissions' is undefined)

So, apparently neither requestPermission() or requestPermissions() exist? I can't even try requestPermission() because TypeScript won't let me.

I've tried with the @next, 2.0.2, 2.0.0, 1.5.4, and even 1.4.0 version of the @capacitor modules core and ios with no change in behavior (except unrelated issues re: swizzling in FCM with 1.5.2 and 1.4.0 but that was expected).

I'm not sure how else to try and figure out/work around this?

rnwolfe commented 4 years ago

Naturally, 2 seconds after I post on the issue, I nuked my node_modules and Xcode Derived Data folder/build cache. I re-targeted 2.0.2 versions and npm install && ng build --prod && npx cap sync && npx cap open ios and it worked as expected (the requestPermission()) and also the registration event fired correctly.

@DevonBernard perhaps you should try going to 2.0.2 and cleaning/rebuilding everything? Looks like you're on 2.0.0 which I also had issues with.

DevonBernard commented 4 years ago

Yeah, when I was using 2.0.0 and 2.0.1, I occasionally got a fix similar to the way you did. Sometimes rm -rf node_modules wasn't sufficient and I had to remove package-lock.json as well. But doing that and full build ionic build && npx cap sync && npx cap copy tends to work.

That said, with 2.0.2 I think I've only had this issue once. I won't be surprised if sometime in the next few weeks I'll get the same error again with 2.0.2; I just hope clearing node will be a reliable fix and it's not just coincidence. Would be nice to have a reproducible guide with steps on effectively clearing XCode and Android Studio caches too.

Side note, when versions are aligned and installed correctly, the demo-repo above does work for storage, and push notifications (including APN->FCM conversion for Firebase).

ryantinker commented 4 years ago

I'm seeing something similar with a starter iOS project. PushNotification.requestPermission() is causing the prompt to display on my iPhone, but my PushNotifications.addListener('registration') callback isn't being called.

The only thing I can think that could be a problem is I have Cocoapods 1.7.5 -- would that cause this problem?

rnwolfe commented 4 years ago

@ryantinker can you post any code? The registration event is not fired when requestPermission() is called. Registration is actually fired when the device registers with the cloud messaging server.

ryantinker commented 4 years ago

@rnwolfe Oh interesting. Yes I can totally post code. This is just a starter project and I'm trying to exercise all these cool features. Just a minute.

ryantinker commented 4 years ago

So, one thing to know about all this: The PushNotifications.addListener('registration') callback is working on Android. I'm getting the token back (and alerting it).

This is only a problem on iOS.

In my Angular app:

home.page.ts:

ngOnInit() {

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

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

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

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

  }

  async requestPushPermission() {

    // Request permission to use push notifications
    // iOS will prompt user and return if they granted permission or not
    // Android will just grant without prompting
    PushNotifications.requestPermission().then( result => {
      if (result.granted) {
        console.log('Request granted!', result);
        // Register with Apple / Google to receive push via APNS/FCM
        PushNotifications.register();
      } else {
        // Show some error
        console.log('Request failed!');
      }
    });

  }

In my iOS app:

I've got my GoogleService-Info.plist with the correct BUNDLE_ID.

Over in my AppDelegate.swift:

import Firebase

...

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    FirebaseApp.configure()
    return true
  }

...

#if USE_PUSH

  func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {  
      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)
          }
      }
  }
rnwolfe commented 4 years ago

@ryantinker are you getting any console log from the line console.log('Request granted!', result);? I'm wondering if the if(result.granted) is executing.

ryantinker commented 4 years ago

Oh yeah, great question. I meant to actually highlight that line and say: "Yes I'm getting the console.log('Request granted!', result); line to execute.

Here's a screenshot of my app showing the prompt: small

And here's the lines from the xcode log (you can see 'Request granted!'):

⚡️  To Native ->  App addListener 39641457
APP INACTIVE
APP ACTIVE
2020-05-15 10:58:11.041203-0700 App[1318:242354] [general] Connection to daemon was invalidated
⚡️  To Native ->  PushNotifications requestPermission 39641458
APP INACTIVE
⚡️  TO JS {"granted":true}
⚡️  [log] - Request granted! {"granted":true}
⚡️  To Native ->  PushNotifications register 39641459
⚡️  TO JS {}
ryantinker commented 4 years ago

Some notes:

  1. I just changed the home.page.ts function above from ionViewDidLoad() to ngOnInit() (I was playing with those).

That didn't seem to fix anything.

  1. Here's a screenshot of the token coming back from Android: Screen Shot 2020-05-15 at 11 06 06 AM
ryantinker commented 4 years ago

One other note: I can see all the listeners being added in Xcode, but there's a weird "Connection to daemon was invalidated" at the end.

⚡️  To Native ->  PushNotifications addListener 97548415
⚡️  To Native ->  PushNotifications addListener 97548416
⚡️  To Native ->  PushNotifications addListener 97548417
⚡️  To Native ->  PushNotifications addListener 97548418
⚡️  To Native ->  App addListener 97548419
2020-05-15 11:07:36.172246-0700 App[1359:247447] [general] Connection to daemon was invalidated
ryantinker commented 4 years ago

The only thing I can think of here is that I have cocoapods 1.7.5 -- from a few years ago. I think I might try installing a Cocoapod manager to bring the latest cocoapods into this project.

ryantinker commented 4 years ago

I'm also wondering if there's something with my APNS certificate. I have a developer and production one. I bet this app is currently running with my developer certificate, but Firebase only has my production.

rnwolfe commented 4 years ago

Yea, that could be part of it. I actually used Firebase Cloud Messaging entirely for iOS (and haven't messed with Android, yet). I just used whatever XCode did for CocoaPods.

The PushNotifications.register(); should be what results in firing the registration event. So, it could be that something is going wrong there. Perhaps you can see if there is some sort of catch/console logging you can do on that to see if more info is available.

ryantinker commented 4 years ago

Okay I think I found the problem. It was something with my APNs certificates/keys.

I believe the certificate being used in my iOS app was not the same certificate that Firebase was using (Firebase uses keys).

I re-created some keys and certificates, reuploaded them all, and it finally works.

IT-MikeS commented 4 years ago

Are you still having an issue @ryantinker ?

ryaa commented 3 years ago

I also had the problem registering push notification/getting the token, I tried many options discussed on the internet and none seemed to work.

Finally, based on the below text from Apple site (https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/HandlingRemoteNotifications.html) I inserted a valid SIM card with active data plan (there was either none used or not active SIM before because this is my development/test iPhone device) and got the registration token successfully.

If a cellular or Wi-Fi connection is not available, neither the application:didRegisterForRemoteNotificationsWithDeviceToken: method nor the application:didFailToRegisterForRemoteNotificationsWithError: method is called. For Wi-Fi connections, this sometimes occurs when the device cannot connect with APNs over the configured port. If this happens, the user can move to another Wi-Fi network that isn’t blocking the required port. On devices with a cellular radio, the user can also wait until the cellular data service becomes available.

Please also see No Delegate Callbacks section at https://developer.apple.com/library/archive/technotes/tn2265/_index.html

After that I removed SIM card and everything still works even though I'm connected to WiFi only. I'm getting registration token and push notification messages.

Ionitron commented 3 years ago

It looks like this issue didn't get the information it needed, so I'll close it for now. If I made a mistake, sorry! I am just a bot.

Have a great day! Ionitron 💙

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.