ionic-team / capacitor-plugins

Official plugins for Capacitor ⚡️
485 stars 562 forks source link

@Capacitor/push-notifications hangs on registration in Xcode Emulator #1776

Closed morsagmon closed 9 months ago

morsagmon commented 10 months ago

Bug Report

Plugin(s)

Capacitor Version

Latest Dependencies:

  @capacitor/cli: 5.3.0
  @capacitor/core: 5.3.0
  @capacitor/android: 5.3.0
  @capacitor/ios: 5.3.0

Installed Dependencies:

  @capacitor/cli: 4.8.1
  @capacitor/core: 4.8.1
  @capacitor/ios: 4.8.1
  @capacitor/android: 4.8.1

Platform(s)

iOS on Xcode Emulator (iPhone 14, for example). XCode 14.2 (the latest supported on my Mac).

Current Behavior

Launching the app in an Emulator on XCode - the app hangs with the splash screen and an endless spinner. Very little info is given in the terminal, which looks like this:

23-09-03 11:50:45.177708+0300 App[3539:14999] [SceneConfiguration] Info.plist contained no UIScene configuration dictionary (looking for configuration named "(no name)")
2023-09-03 11:50:45.177991+0300 App[3539:14999] [SceneConfiguration] Info.plist contained no UIScene configuration dictionary (looking for configuration named "(no name)")
2023-09-03 11:50:45.299885+0300 App[3539:15813] 10.14.0 - [FirebaseMessaging][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
to ensure proper integration.
2023-09-03 11:50:45.310719+0300 App[3539:14999] DiskCookieStorage changing policy from 2 to 0, cookie file: file:///Users/morsagmon/Library/Developer/CoreSimulator/Devices/3A5F5867-3446-4598-9E77-2879D7C25BC0/data/Containers/Data/Application/634C985D-61B3-4CD3-A46E-9878BD3DE05D/Library/Cookies/com.simplify.learning.mvp.tst.binarycookies
2023-09-03 11:50:45.732829+0300 App[3539:14999] KeyboardPlugin: resize mode - native
Loading network plugin
⚡️ Warning: isWebDebuggable only functions as intended on iOS 16.4 and above.
⚡️  Loading app at capacitor://localhost...
Reachable via WiFi
⚡️  [log] - onscript loading complete
⚡️  WebView loaded
⚡️  [log] - Received firebase token:  
⚡️  To Native ->  Network addListener 101653962
⚡️  To Native ->  Network getStatus 101653963
⚡️  TO JS {"connectionType":"wifi","connected":true}
⚡️  To Native ->  PushNotifications requestPermissions 101653964
⚡️  To Native ->  PushNotifications addListener 101653965
⚡️  To Native ->  PushNotifications addListener 101653966
⚡️  To Native ->  PushNotifications addListener 101653967
⚡️  To Native ->  PushNotifications addListener 101653968
⚡️  TO JS {"receive":"granted"}
⚡️  To Native ->  PushNotifications register 101653969
⚡️  [log] - FireBase Permission granted
⚡️  TO JS undefined
⚡️  [log] - Ionic Native: deviceready event fired after 197 ms
⚡️  To Native ->  App addListener 101653970
⚡️  To Native ->  Keyboard getResizeMode 101653971
⚡️  TO JS {"mode":"native"}

The log shows that the app hangs when attempting the Firebase registration request. This is throwing to the console: console.log('FireBase Permission granted'); But this is not throwing to the console: console.log('In registration. My token: ' + JSON.stringify(token));

(What you see in the above terminal: ⚡️ [log] - Received firebase token: is just a subscriber in the app.component awaiting the token to be emitted, which obviously not yet emitting).

Updating that the problem persists as I tried the following:

Another update: I thought I use the angular/fire library (AngularFireMessaging) to register and obtain the token from Firebase, bypassing the capacitor registration service. It behaves exactly as with the capacitor library, with the exact same console log output. Leads me to assume this is not capacitor-specific, but rather something in the way XCode emulator approaches Firebase for registration.

For completeness, here is the full service code:

import { Injectable, NgZone } from "@angular/core";
import {
  ActionPerformed,
  PushNotifications,
  PushNotificationSchema,
  Token,
} from "@capacitor/push-notifications";
import { Capacitor } from "@capacitor/core";
import { getLocal, setLocal } from "./storage.service";
import { BehaviorSubject } from "rxjs";
import { App } from "@capacitor/app";
import { Router } from "@angular/router";
import { ToastAlertService } from "./toast-alert.service";

@Injectable({
  providedIn: "root",
})
export class FbNotificationsService {
  onFCMTokenChanged: BehaviorSubject<string> = new BehaviorSubject(""); //Listened to in app.component
  onAppResume: BehaviorSubject<string> = new BehaviorSubject(""); //Listened to in chat.page
  tokenProcessed: boolean = false;

  constructor(private taService: ToastAlertService, private zone: NgZone, private route: Router) { }

  initPush() {
    if (Capacitor.getPlatform() !== "web") {
      this.registerPush();
    } else {
      this.onFCMTokenChanged.next("web-platform");
    }
  }

  private registerPush() {
    PushNotifications.requestPermissions().then((permission) => {
      if (permission.receive === "granted") {

        console.log('FireBase Permission granted');

        PushNotifications.register(); //Registers app to receive notifications - FAILES AFTER UPGRADE to CAPACITOR 4 !!!!!!!!!!!!!!!!!!!!!!!!!!!!
      } else {
        console.log('FireBase NO permissions granted');
      }
    });

    PushNotifications.addListener("registration", (token: Token) => {

      console.log('In registration. My token: ' + JSON.stringify(token));
      if (token && !this.tokenProcessed) {
        this.tokenProcessed = true;
        console.log('My token: ' + JSON.stringify(token));
        this.onFCMTokenChanged.next(token.value);

        setLocal("device-token", token.value); //.then( () => {

      }
    });

    PushNotifications.addListener("registrationError", (error: any) => {
      console.log('Registration Error: ' + JSON.stringify(error));
      this.taService.dialogClose(
        "Network Error",
        "The network signal is not sufficient at this time. Please improve connection and try again"
      );
      App.exitApp();
    });

    PushNotifications.addListener(
      "pushNotificationReceived",
      async (notification: PushNotificationSchema) => {
        const data = notification.data.data; //data.learnerId , data.notificationCode, ...
        console.log('Push alert notification received: ' + JSON.stringify(notification));
      }
    );

    PushNotifications.addListener(
      "pushNotificationActionPerformed",
      async (notification: ActionPerformed) => {
        const data = notification.notification.data.data;
        console.log('Push alert notification Action performed: ' + notification.notification);
        console.log('data payload: ', data);
        this.processNotificationReceived(JSON.parse(data));
      }
    );
  }

  processNotificationReceived(data, originTap: boolean = true) {
    console.log('Processing alert notification received and tapped: ', data, data.notificationCode);
    getLocal("in-chat").then((inchat) => {
      switch (data.notificationCode) {
        case "1":
          ...
    });
  }
}

Expected Behavior

Normally, the token is received and the app home page comes up. Important: This app is working perfectly fine, including push notification, in the following:

It only fails in the XCode emulator, on my local machine as well as on Apple's testing emulators during a review process of the app, causing a rejection of the app.

Code Reproduction

Naturally, there is no way for me to share a reproduction of this environment as usual, such as on Stackblitz, given the unique nature of the Xcode-Emulator environment needed.

Other Technical Details

XCode 14.2

Additional Context

This app was upgraded to the latest Capacitor family version ^5.0.0 along with respective dependencies, but was downgraded back to ^4.0.0 after major issues with Android Studio not resolved over many days, as can be seen here: https://stackoverflow.com/questions/76845287/android-studio-build-failed-tiramisu-and-packageinfoflags-not-found

Podfile:

require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers'

platform :ios, '13.0'
use_frameworks!

# workaround to avoid Xcode caching of Pods that requires
# Product -> Clean Build Folder after new Cordova plugins installed
# Requires CocoaPods 1.6 or newer
install! 'cocoapods', :disable_input_output_paths => true

def capacitor_pods
  pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
  pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
  pod 'CapacitorCommunityCameraPreview', :path => '../../node_modules/@capacitor-community/camera-preview'
  pod 'CapacitorCommunityContacts', :path => '../../node_modules/@capacitor-community/contacts'
  pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app'
  pod 'CapacitorCamera', :path => '../../node_modules/@capacitor/camera'
  pod 'CapacitorDevice', :path => '../../node_modules/@capacitor/device'
  pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics'
  pod 'CapacitorKeyboard', :path => '../../node_modules/@capacitor/keyboard'
  pod 'CapacitorNetwork', :path => '../../node_modules/@capacitor/network'
  pod 'CapacitorPreferences', :path => '../../node_modules/@capacitor/preferences'
  pod 'CapacitorPushNotifications', :path => '../../node_modules/@capacitor/push-notifications'
  pod 'CapacitorSplashScreen', :path => '../../node_modules/@capacitor/splash-screen'
  pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
  pod 'CapacitorVoiceRecorder', :path => '../../node_modules/capacitor-voice-recorder'
  pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins'
end

target 'App' do
  capacitor_pods
  # Add your Pods here
  pod 'Firebase/Messaging'
end

post_install do |installer|
  assertDeploymentTarget(installer)
end

package.json:

{
  "browser": {
    "fs": false,
    "path": false,
    "os": false
  },
  "name": "xxx",
  "version": "0.0.1",
  "author": "xxx",
  "homepage": "",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "resources-android": "capacitor-resources android && node src/resources-android.js",
    "resources-ios": "capacitor-resources ios && node src/resources-ios.js"
  },
  "private": true,
  "dependencies": {
    "@angular/common": "^15.2.9",
    "@angular/core": "^15.2.9",
    "@angular/fire": "^7.6.1",
    "@angular/forms": "^15.2.9",
    "@angular/platform-browser": "^15.2.9",
    "@angular/platform-browser-dynamic": "^15.2.9",
    "@angular/router": "^15.2.9",
    "@awesome-cordova-plugins/android-permissions": "^6.4.0",
    "@awesome-cordova-plugins/core": "^6.4.0",
    "@awesome-cordova-plugins/native-audio": "^6.4.0",
    "@aws-sdk/client-lambda": "^3.382.0",
    "@aws-sdk/client-s3": "^3.382.0",
    "@capacitor-community/camera-preview": "^4.0.0",
    "@capacitor-community/contacts": "^4.0.4",
    "@capacitor/android": "^4.8.1",
    "@capacitor/app": "^4.1.1",
    "@capacitor/camera": "^4.1.5",
    "@capacitor/cli": "^4.8.1",
    "@capacitor/core": "^4.8.1",
    "@capacitor/device": "^4.1.0",
    "@capacitor/haptics": "^4.1.0",
    "@capacitor/ios": "^4.8.1",
    "@capacitor/keyboard": "^4.1.1",
    "@capacitor/network": "^4.1.0",
    "@capacitor/preferences": "^4.0.2",
    "@capacitor/push-notifications": "^4.1.2",
    "@capacitor/splash-screen": "^4.2.0",
    "@capacitor/status-bar": "^4.1.1",
    "@ionic/angular": "^7.2.1",
    "bcryptjs": "^2.4.3",
    "buffer": "^6.0.3",
    "capacitor-voice-recorder": "^4.0.2",
    "cordova-plugin-android-permissions": "1.1.5",
    "cordova-plugin-nativeaudio": "^3.0.9",
    "crypto-browserify": "^3.12.0",
    "firebase": "^10.1.0",
    "fs": "^0.0.1-security",
    "google-libphonenumber": "3.2.32",
    "mini-css-extract-plugin": "^2.7.6",
    "ng2-search-filter": "^0.5.1",
    "os-browserify": "^0.3.0",
    "path": "^0.12.7",
    "raw-loader": "^4.0.2",
    "rxjs": "^7.8.1",
    "stream-browserify": "^3.0.0",
    "stream-http": "^3.2.0",
    "tslib": "^2.6.1",
    "url": "^0.11.1",
    "util": "^0.12.5",
    "uuid": "^9.0.0",
    "zone.js": "^0.13.1"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^15.2.9",
    "@angular/cli": "^15.2.9",
    "@angular/compiler": "^15.2.9",
    "@angular/compiler-cli": "^15.2.9",
    "@angular/language-service": "^15.2.9",
    "@ionic/angular-toolkit": "^10.0.0",
    "@types/jasmine": "^4.3.5",
    "@types/jasminewd2": "^2.0.10",
    "@types/node": "^20.4.5",
    "jasmine-core": "3.99.1",
    "jasmine-spec-reporter": "~5.0.0",
    "karma": "6.4.1",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage": "2.2.0",
    "karma-coverage-istanbul-reporter": "~3.0.2",
    "karma-jasmine": "~4.0.0",
    "karma-jasmine-html-reporter": "^1.5.0",
    "ts-node": "^10.9.1",
    "typescript": "^4.9.5",
    "webpack": "^5.88.2"
  },
  "description": "An Ionic project"
}
Ionitron commented 10 months ago

This issue needs more information before it can be addressed. In particular, the reporter needs to provide a minimal sample app that demonstrates the issue. If no sample app is provided within 15 days, the issue will be closed.

Please see the Contributing Guide for how to create a Sample App.

Thanks! Ionitron 💙

morsagmon commented 10 months ago

Hello Ionitron. I'd be happy to reproduce for you to check. Please refer me to a service that allows a reproduction of such a situation: Angular with Capacitor and Firebase notifications on an XCode MAC with emulators. I'm not familiar with such a service. I really thought I provided a very thorough description and information. If you are missing something please let me know and I'll do my best to complete. Thanks! Mor

morsagmon commented 10 months ago

Updating that the problem persists as I tried the following:

morsagmon commented 10 months ago

Another update: I thought I use the angular/fire library (AngularFireMessaging) to register and obtain the token from Firebase, bypassing the capacitor registration service. It behaves exactly as with the capacitor library, with the exact same console log output. Leads me to assume this is not capacitor-specific, but rather something in the way XCode emulator approaches Firebase for registration.

jcesarmobile commented 9 months ago

Push notifications only work in the iOS simulator if you are in macOS 13.x, Xcode 14.x and Apple silicon computer. Since you say latest Xcode you can install is 14.2, I would assume you are in macOS 12.x as Xcode 14.3 requires macOS 13.x.

So it's not a bug, it's a limitation of the iOS simulator.

ionitron-bot[bot] commented 9 months 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 the plugin, please create a new issue and ensure the template is fully filled out.