Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(messaging): Throw exception if APNS token is not yet available #11400

Merged
merged 10 commits into from Aug 23, 2023
14 changes: 13 additions & 1 deletion docs/cloud-messaging/client.md
Expand Up @@ -144,8 +144,20 @@ To retrieve the current registration token for an app instance, call
ask the user for notification permissions. Otherwise, it returns a token or
rejects the future due to an error.

Warning: From iOS SDK 10.4.0 and higher, it is a requirement that the APNS token
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey @kevinthecheung - documentation update that could do with a review, please 😄

russellwheatley marked this conversation as resolved.
Show resolved Hide resolved
is available before making API requests. The APNS token is not guaranteed to have been received
russellwheatley marked this conversation as resolved.
Show resolved Hide resolved
before making FCM plugin API requests.

```dart
final fcmToken = await FirebaseMessaging.instance.getToken();
// You may set the permission requests to "provisional" which allows the user to choose what type
// of notifications they would like to receive once the user receives a notification.
final notificationSettings = await FirebaseMessaging.instance.requestPermission(provisional: true);

// For apple platforms, ensure the APNS token is available before making any FCM plugin API calls
final apnsToken = await FirebaseMessaging.instance.getAPNSToken();
if (apnsToken != null) {
// APNS token is available, make FCM plugin API requests...
}
```

On web platforms, pass your VAPID public key to `getToken()`:
Expand Down
Expand Up @@ -4,6 +4,7 @@
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';
import 'dart:io' show Platform;
import 'dart:ui';

import 'package:firebase_core/firebase_core.dart';
Expand Down Expand Up @@ -126,6 +127,24 @@ class MethodChannelFirebaseMessaging extends FirebaseMessagingPlatform {
final StreamController<String> _tokenStreamController =
StreamController<String>.broadcast();

// Created this to check APNS token is available before certain Apple Firebase
// Messaging requests. See this issue:
// https://github.com/firebase/flutterfire/issues/10625
Future<void> _APNSTokenCheck() async {
if (Platform.isMacOS || Platform.isIOS) {
String? token = await getAPNSToken();

if (token == null) {
throw FirebaseException(
plugin: 'firebase_messaging',
code: 'apns-token-not-set',
message:
'APNS token has not been set yet. Please ensure the APNS token is available by calling `getAPNSToken()`.',
);
}
}
}

@override
FirebaseMessagingPlatform delegateFor({required FirebaseApp app}) {
return MethodChannelFirebaseMessaging(app: app);
Expand Down Expand Up @@ -188,6 +207,8 @@ class MethodChannelFirebaseMessaging extends FirebaseMessagingPlatform {

@override
Future<void> deleteToken() async {
await _APNSTokenCheck();

try {
await channel
.invokeMapMethod('Messaging#deleteToken', {'appName': app.name});
Expand Down Expand Up @@ -219,6 +240,8 @@ class MethodChannelFirebaseMessaging extends FirebaseMessagingPlatform {
Future<String?> getToken({
String? vapidKey, // not used yet; web only property
}) async {
await _APNSTokenCheck();

try {
Map<String, String?>? data =
await channel.invokeMapMethod<String, String>('Messaging#getToken', {
Expand Down Expand Up @@ -363,6 +386,8 @@ class MethodChannelFirebaseMessaging extends FirebaseMessagingPlatform {

@override
Future<void> subscribeToTopic(String topic) async {
await _APNSTokenCheck();

try {
await channel.invokeMapMethod('Messaging#subscribeToTopic', {
'appName': app.name,
Expand All @@ -375,6 +400,8 @@ class MethodChannelFirebaseMessaging extends FirebaseMessagingPlatform {

@override
Future<void> unsubscribeFromTopic(String topic) async {
await _APNSTokenCheck();

try {
await channel.invokeMapMethod('Messaging#unsubscribeFromTopic', {
'appName': app.name,
Expand Down