firebase / flutterfire

🔥 A collection of Firebase plugins for Flutter apps.
https://firebase.google.com/docs/flutter/setup
BSD 3-Clause "New" or "Revised" License
8.58k stars 3.94k forks source link

🐛 [firebase_messaging] iOS failing to notify app #12433

Closed MATTYGILO closed 5 months ago

MATTYGILO commented 5 months ago

Bug report

Describe the bug I am trying to send notifications to my flutter app, but none are being received whether, background, foreground or terminated. All help is much appreciated

Note: I changed much of the code in the past because I need phone auth to work without captcha.

Steps to reproduce

AppDelegate.swift

import UIKit
import Flutter
import Firebase
import FirebaseMessaging

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    FirebaseApp.configure()
    GeneratedPluginRegistrant.register(with: self)
    if #available(iOS 10.0, *) {
        UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
    }
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }

  func application(application: UIApplication,
                   didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
      Messaging.messaging().apnsToken = deviceToken
  }
}

Capabilities: 

Screenshot 2024-03-05 at 13 27 52

FirebaseAppDelegateProxyEnabled: 

Screenshot 2024-03-05 at 13 28 21

Firebase versions:

cloud_firestore: ^4.8.2
cloud_functions: ^4.3.3
firebase_auth: ^4.9.0
firebase_storage: ^11.2.4
firebase_core: ^2.14.0
firebase_messaging: ^14.6.7
firebase_app_check: ^0.2.1+8

GoogleService-info valid location and up to date: 

Screenshot 2024-03-05 at 13 31 06

Firebase messaging API:

Note: Cloud messaging legacy is disabled  

Screenshot 2024-03-05 at 13 33 11 Screenshot 2024-03-05 at 13 34 22

I have tried both notifications to all and specific devices: 

Screenshot 2024-03-05 at 13 31 42 Screenshot 2024-03-05 at 13 32 56

Runner entitlement:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>aps-environment</key>
    <string>development</string>
    <key>com.apple.developer.networking.wifi-info</key>
    <true/>
    <key>com.apple.developer.networking.HotspotConfiguration</key>
    <true/>
</dict>
</plist>

Podfile:

# Uncomment this line to define a global platform for your project
platform :ios, '14.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

project 'Runner', {
  'Debug' => :debug,
  'Profile' => :release,
  'Release' => :release,
}

def flutter_root
  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
  unless File.exist?(generated_xcode_build_settings_path)
    raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
  end

  File.foreach(generated_xcode_build_settings_path) do |line|
    matches = line.match(/FLUTTER_ROOT\=(.*)/)
    return matches[1].strip if matches
  end
  raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end

require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)

flutter_ios_podfile_setup

target 'Runner' do
  use_frameworks!
  use_modular_headers!

  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end

# Required for google_mlkit_text_recognition
$iOSVersion = '14.0'

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)

    target.build_configurations.each do |config|
      if config.build_settings['WRAPPER_EXTENSION'] == 'bundle'
        config.build_settings['DEVELOPMENT_TEAM'] = 'MY TEAM ID'
      end

      # This is to stop the: DT_TOOLCHAIN_DIR cannot be used to evaluate LIBRARY_SEARCH_PATHS, use TOOLCHAIN_DIR instead
      xcconfig_path = config.base_configuration_reference.real_path
      xcconfig = File.read(xcconfig_path)
      xcconfig_mod = xcconfig.gsub(/DT_TOOLCHAIN_DIR/, "TOOLCHAIN_DIR")
      File.open(xcconfig_path, "w") { |file| file << xcconfig_mod }

      # Stops M1 chips from being built for as not all flutter plugins are yet available
      config.build_settings['ONLY_ACTIVE_ARCH'] = 'YES'
      config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64 i386"
      config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET'

      config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
        '$(inherited)',

        ## dart: PermissionGroup.notification
        'PERMISSION_NOTIFICATIONS=1',

      ]

    end
  end
end

AppFrameworkInfo.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>CFBundleDevelopmentRegion</key>
  <string>en</string>
  <key>CFBundleExecutable</key>
  <string>App</string>
  <key>CFBundleIdentifier</key>
  <string>com.MYACTUAL.APPIDENTIFIER.flutter</string>
  <key>CFBundleInfoDictionaryVersion</key>
  <string>6.0</string>
  <key>CFBundleName</key>
  <string>App</string>
  <key>CFBundlePackageType</key>
  <string>FMWK</string>
  <key>CFBundleShortVersionString</key>
  <string>1.0</string>
  <key>CFBundleSignature</key>
  <string>????</string>
  <key>CFBundleVersion</key>
  <string>1.0</string>
  <key>MinimumOSVersion</key>
  <string>11.0</string>
</dict>
</plist>

Expected behavior

To receive notifications in at least one of foreground, background or terminated

Sample project

main.dart:

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  // Run the app
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();

    // On build
    WidgetsBinding.instance.addPostFrameCallback((_) async {
      FirebaseMessaging messaging = FirebaseMessaging.instance;

      Map<Permission, PermissionStatus> statuses = await [
        Permission.notification
      ].request();

      NotificationSettings settings = await messaging.requestPermission(
        alert: true,
        badge: true,
        provisional: false,
        sound: true,
      );

      if (settings.authorizationStatus == AuthorizationStatus.authorized) {
        print("User granted permission");

        // Output the FCM token
        String? token = await messaging.getToken();

        // await messaging.getAPNSToken();

        print("app_main token: $token");
      } else if (settings.authorizationStatus == AuthorizationStatus.provisional) {
        print("User granted provisional permission");
      } else {
        print("User declined or has not accepted permission");
      }

      FirebaseMessaging.onMessage.listen((RemoteMessage message) {
        print('Got a message whilst in the foreground!');
        print('Message data: ${message.data}');

        if (message.notification != null) {
          print('Message also contained a notification: ${message.notification}');
        }
      });
    });

  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Firebase Messaging Test'),
        ),
        body: Center(
          child: Text('Listening for messages...'),
        ),
      ),
    );
  }
}

Additional context

I have tried a lot and haven't logged all that I have tried. All help would be much appreciated.

Flutter doctor

Run flutter doctor and paste the output below:

Click To Expand ``` [✓] Flutter (Channel stable, 3.13.4, on macOS 13.5.2 22G91 darwin-arm64, locale en-GB) [!] Android toolchain - develop for Android devices (Android SDK version 32.1.0-rc1) ✗ cmdline-tools component is missing Run `path/to/sdkmanager --install "cmdline-tools;latest"` See https://developer.android.com/studio/command-line for more details. ✗ Android license status unknown. Run `flutter doctor --android-licenses` to accept the SDK licenses. See https://flutter.dev/docs/get-started/install/macos#android-setup for more details. [✓] Xcode - develop for iOS and macOS (Xcode 15.2) [✓] Chrome - develop for the web [✓] Android Studio (version 2023.1) [✓] IntelliJ IDEA Ultimate Edition (version 2023.3.2) [✓] Connected device (3 available) [✓] Network resources ```

Flutter dependencies

Run flutter pub deps -- --style=compact and paste the output below:

Click To Expand ``` - _flutterfire_internals 1.3.16 [collection firebase_core firebase_core_platform_interface flutter meta] - adaptive_number 1.0.0 [fixnum] - archive 3.3.7 [crypto path pointycastle] - args 2.4.0 - async 2.11.0 [collection meta] - audio_session 0.1.13 [flutter flutter_web_plugins rxdart meta] - boolean_selector 2.1.1 [source_span string_scanner] - camera_android 0.10.5 [camera_platform_interface flutter flutter_plugin_android_lifecycle stream_transform] - camera_avfoundation 0.9.13+1 [camera_platform_interface flutter stream_transform] - camera_platform_interface 2.5.0 [cross_file flutter plugin_platform_interface stream_transform] - camera_web 0.3.1+3 [camera_platform_interface flutter flutter_web_plugins stream_transform] - characters 1.3.0 - charcode 1.3.1 - clock 1.1.1 - cloud_firestore_platform_interface 6.1.0 [_flutterfire_internals collection firebase_core flutter meta plugin_platform_interface] - cloud_firestore_web 3.9.0 [_flutterfire_internals cloud_firestore_platform_interface collection firebase_core firebase_core_web flutter flutter_web_plugins js] - cloud_functions_platform_interface 5.5.11 [firebase_core flutter meta plugin_platform_interface] - cloud_functions_web 4.6.11 [cloud_functions_platform_interface firebase_core firebase_core_web flutter flutter_web_plugins js] - collection 1.17.2 - connectivity_plus_platform_interface 1.2.4 [flutter meta plugin_platform_interface] - convert 3.1.1 [typed_data] - cross_file 0.3.3+4 [js meta] - crypto 3.0.2 [typed_data] - csslib 0.17.2 [source_span] - dbus 0.7.4 [args ffi meta xml] - ed25519_edwards 0.3.1 [collection crypto convert adaptive_number] - fake_async 1.3.1 [clock collection] - ffi 1.2.1 - ffmpeg_kit_flutter_platform_interface 0.2.1 [flutter plugin_platform_interface] - file 6.1.4 [meta path] - firebase_app_check_platform_interface 0.1.0+10 [_flutterfire_internals firebase_core flutter meta plugin_platform_interface] - firebase_app_check_web 0.1.0+10 [_flutterfire_internals firebase_app_check_platform_interface firebase_core firebase_core_web flutter flutter_web_plugins js] - firebase_auth_platform_interface 7.0.9 [_flutterfire_internals collection firebase_core flutter meta plugin_platform_interface] - firebase_auth_web 5.8.13 [firebase_auth_platform_interface firebase_core firebase_core_web flutter flutter_web_plugins http_parser js meta] - firebase_core_platform_interface 5.0.0 [collection flutter flutter_test meta plugin_platform_interface] - firebase_core_web 2.10.0 [firebase_core_platform_interface flutter flutter_web_plugins js meta] - firebase_messaging_platform_interface 4.5.18 [_flutterfire_internals firebase_core flutter meta plugin_platform_interface] - firebase_messaging_web 3.5.18 [_flutterfire_internals firebase_core firebase_core_web firebase_messaging_platform_interface flutter flutter_web_plugins js meta] - firebase_remote_config_platform_interface 1.4.16 [_flutterfire_internals firebase_core flutter meta plugin_platform_interface] - firebase_remote_config_web 1.4.16 [firebase_core firebase_core_web firebase_remote_config_platform_interface flutter flutter_web_plugins js] - firebase_storage_platform_interface 5.1.3 [_flutterfire_internals collection firebase_core flutter meta plugin_platform_interface] - firebase_storage_web 3.6.17 [_flutterfire_internals async firebase_core firebase_core_web firebase_storage_platform_interface flutter flutter_web_plugins http js meta] - fixnum 1.1.0 - flutter_image_compress_common 1.0.1 [flutter flutter_image_compress_platform_interface] - flutter_image_compress_platform_interface 1.0.1 [flutter plugin_platform_interface cross_file] - flutter_image_compress_web 0.1.3 [flutter flutter_web_plugins flutter_image_compress_platform_interface js] - flutter_plugin_android_lifecycle 2.0.9 [flutter] - flutter_web_plugins 0.0.0 [flutter characters collection material_color_utilities meta vector_math web] - freezed_annotation 2.2.0 [collection json_annotation meta] - geolocator_android 4.1.7 [flutter geolocator_platform_interface] - geolocator_apple 2.2.5 [flutter geolocator_platform_interface] - geolocator_platform_interface 4.0.7 [flutter plugin_platform_interface vector_math meta] - geolocator_web 2.1.6 [flutter flutter_web_plugins geolocator_platform_interface] - geolocator_windows 0.1.1 [flutter geolocator_platform_interface] - google_identity_services_web 0.2.1 [js meta] - google_mlkit_commons 0.6.1 [flutter] - google_sign_in_android 6.1.9 [flutter google_sign_in_platform_interface] - google_sign_in_ios 5.6.1 [flutter google_sign_in_platform_interface] - google_sign_in_platform_interface 2.4.0 [flutter plugin_platform_interface quiver] - google_sign_in_web 0.12.0+2 [flutter flutter_web_plugins google_identity_services_web google_sign_in_platform_interface http js] - html 0.15.2 [csslib source_span] - http 0.13.5 [async http_parser meta path] - http_parser 4.0.2 [collection source_span string_scanner typed_data] - js 0.6.5 [meta] - json_annotation 4.8.0 [meta] - just_audio_platform_interface 4.2.0 [flutter plugin_platform_interface] - just_audio_web 0.4.7 [just_audio_platform_interface flutter flutter_web_plugins] - lints 2.0.1 - matcher 0.12.16 [async meta stack_trace term_glyph test_api] - material_color_utilities 0.5.0 [collection] - meta 1.9.1 - nested 1.0.0 [flutter] - network_info_plus_platform_interface 1.1.3 [flutter meta plugin_platform_interface] - nm 0.5.0 [dbus] - path 1.8.3 - path_provider_android 2.0.25 [flutter path_provider_platform_interface] - path_provider_foundation 2.2.2 [flutter path_provider_platform_interface] - path_provider_linux 2.1.10 [ffi flutter path path_provider_platform_interface xdg_directories] - path_provider_platform_interface 2.0.6 [flutter platform plugin_platform_interface] - path_provider_windows 2.0.7 [ffi flutter path path_provider_platform_interface win32] - permission_handler_android 10.2.0 [flutter permission_handler_platform_interface] - permission_handler_apple 9.0.8 [flutter permission_handler_platform_interface] - permission_handler_platform_interface 3.9.0 [flutter meta plugin_platform_interface] - permission_handler_windows 0.1.2 [flutter permission_handler_platform_interface] - petitparser 5.1.0 [meta] - platform 3.1.0 - plugin_platform_interface 2.1.4 [meta] - pointycastle 3.7.3 [collection convert js] - process 4.2.4 [file path platform] - quiver 3.2.1 [matcher] - record_linux 0.4.1 [flutter record_platform_interface path] - record_macos 0.2.2 [flutter record_platform_interface] - record_platform_interface 0.5.0 [flutter plugin_platform_interface] - record_web 0.5.0 [flutter flutter_web_plugins record_platform_interface] - record_windows 0.7.1 [flutter record_platform_interface path] - rxdart 0.27.7 - shared_preferences_android 2.1.2 [flutter shared_preferences_platform_interface] - shared_preferences_foundation 2.2.1 [flutter shared_preferences_platform_interface] - shared_preferences_linux 2.2.0 [file flutter path path_provider_linux path_provider_platform_interface shared_preferences_platform_interface] - shared_preferences_platform_interface 2.2.0 [flutter plugin_platform_interface] - shared_preferences_web 2.1.0 [flutter flutter_web_plugins shared_preferences_platform_interface] - shared_preferences_windows 2.2.0 [file flutter path path_provider_platform_interface path_provider_windows shared_preferences_platform_interface] - sign_in_with_apple_platform_interface 1.0.0 [flutter plugin_platform_interface meta] - sign_in_with_apple_web 1.0.1 [flutter flutter_web_plugins sign_in_with_apple_platform_interface js] - sky_engine 0.0.99 - source_span 1.10.0 [collection path term_glyph] - stack_trace 1.11.0 [path] - stream_channel 2.1.1 [async] - stream_transform 2.1.0 - string_scanner 1.2.0 [source_span] - term_glyph 1.2.1 - test_api 0.6.0 [async boolean_selector collection meta source_span stack_trace stream_channel string_scanner term_glyph] - tuple_dart 1.4.2 - typed_data 1.3.1 [collection] - universal_html 2.2.1 [async csslib charcode collection html meta source_span typed_data universal_io] - universal_io 2.2.0 [collection meta typed_data] - url_launcher_android 6.0.27 [flutter url_launcher_platform_interface] - url_launcher_ios 6.1.4 [flutter url_launcher_platform_interface] - url_launcher_linux 3.0.4 [flutter url_launcher_platform_interface] - url_launcher_macos 3.0.5 [flutter url_launcher_platform_interface] - url_launcher_platform_interface 2.1.2 [flutter plugin_platform_interface] - url_launcher_web 2.0.16 [flutter flutter_web_plugins url_launcher_platform_interface] - url_launcher_windows 3.0.5 [flutter url_launcher_platform_interface] - uuid 3.0.7 [crypto] - vector_math 2.1.4 - web 0.1.4-beta - webview_flutter_android 3.7.1 [flutter webview_flutter_platform_interface] - webview_flutter_platform_interface 2.3.1 [flutter meta plugin_platform_interface] - webview_flutter_wkwebview 3.4.4 [flutter path webview_flutter_platform_interface] - win32 2.6.1 [ffi] - xdg_directories 1.0.0 [meta path process] - xml 6.2.2 [collection meta petitparser] ```

MATTYGILO commented 5 months ago

So I have started trying to send a notification from a firebase function:

exports.sendNotification = functions.region("europe-west2").https.onRequest(async (req:any, res: any) => {

    const token = req.query.token; // The FCM token of the device you want to send the notification to
    const message = req.query.message; // The message you want to send

    if (!token || !message) {
        return res.status(400).send("Missing token or message parameter.");
    }

    const payload = {
        notification: {
            title: "New Notification",
            body: message,
            icon: "default",
            click_action: "FLUTTER_NOTIFICATION_CLICK", // Custom click action for Flutter apps
        },
    };

    try {
        const response = await admin.messaging().sendToDevice(token, payload);
        console.log("Notification sent successfully:", response);
        res.send("Notification sent successfully");
    } catch (error) {
        console.log("Error sending notification:", error);
        res.status(500).send("Error sending notification: " + error);
    }
});

Which resulted in this error:

Failed to send notification. Status code: 500, Message: Error sending notification: Error: An error occurred when trying to authenticate to the FCM servers. Make sure the credential used to authenticate this SDK has the proper permissions. See https://firebase.google.com/docs/admin/setup for setup instructions. Raw server response: "<HTML>
<HEAD>
<TITLE>PROJECT_NOT_PERMITTED</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>PROJECT_NOT_PERMITTED</H1>
<H2>Error 401</H2>
</BODY>
</HTML>
". Status code: 401.
MATTYGILO commented 5 months ago

I have verified that my Bundle ID, Team ID and App Store ID all match

MATTYGILO commented 5 months ago

I have revoked my APNs key and re uploaded it many times I have download my service-account.json and replaced it Xcode many times

MATTYGILO commented 5 months ago

As you can see my service account has all firebase admin cloud messaging permissions

Screenshot 2024-03-06 at 13 36 18
MATTYGILO commented 5 months ago

I also tried this code using the new API but it didn't work either:

const payload = {
            message: {
                token: token, // Set the device token
                notification: {
                    title: "Notification Title", // Set a title for the notification
                    body: message, // Use the message passed in the query for the body
                },
                apns: {
                    headers: {
                        "apns-priority": "10",
                    },
                    payload: {
                        aps: {
                            alert: {
                                title: "Notification Title",
                                body: message,
                            },
                        },
                    },
                },
                webpush: {
                    headers: {
                        Urgency: "high",
                    },
                    notification: {
                        body: message,
                        requireInteraction: "true",
                    },
                },
            },
        };

        const response = await admin.messaging().send(payload.message);

It returned this error:

Screenshot 2024-03-06 at 13 48 15
MATTYGILO commented 5 months ago

I tried the old API and it gave me this error:

A message targeted to an iOS device could not be sent because the required APNs SSL certificate was not uploaded or has expired. Check the validity of your development and production certificates.

However as I have revoked and re uploaded the Auth key to the server and I still get this error.

MATTYGILO commented 5 months ago

The dumbest mistake to make (Don't use TeamID from Xcode):

Screenshot 2024-03-06 at 14 21 19

Go to App Store Connect, scroll to Membership details and use the value there (Use TeamID from AppStore connect)

Screenshot 2024-03-06 at 14 22 17