Title: Unable to Retrieve APNs Token in Flutter macOS App
Question:
I’m developing a Flutter macOS application and am trying to retrieve the APNs token using the Firebase Messaging plugin. Despite implementing the necessary code, I always receive null
when trying to fetch the APNs token. Here’s a detailed description of my setup and issue:
Native Code (Swift)
import Cocoa
import FlutterMacOS
import UserNotifications
@main
class AppDelegate: FlutterAppDelegate, UNUserNotificationCenterDelegate {
override func applicationDidFinishLaunching(_ notification: Notification) {
super.applicationDidFinishLaunching(notification)
// Register for push notifications
registerForPushNotifications()
// Setup Flutter messaging
if let flutterViewController = mainFlutterWindow?.contentViewController as? FlutterViewController {
let firebaseMessagingChannel = FlutterMethodChannel(name: "plugins.flutter.io/firebase_messaging",
binaryMessenger: flutterViewController.engine.binaryMessenger)
// Ensure method call handler is set
firebaseMessagingChannel.setMethodCallHandler { [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) in
if call.method == "getAPNSToken" {
self?.getAPNSToken(result: result)
} else {
result(FlutterMethodNotImplemented)
}
}
}
}
private func registerForPushNotifications() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
if granted {
DispatchQueue.main.async {
NSApplication.shared.registerForRemoteNotifications()
}
}
}
UNUserNotificationCenter.current().delegate = self
}
private func getAPNSToken(result: @escaping FlutterResult) {
if let deviceToken = UserDefaults.standard.string(forKey: "APNSDeviceToken") {
result(deviceToken)
} else {
result(FlutterError(code: "UNAVAILABLE", message: "APNS token is not available", details: nil))
}
}
override func application(_ application: NSApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
// Convert device token to String
let tokenParts = deviceToken.map { String(format: "%02.2hhx", $0) }
let token = tokenParts.joined()
print("APNS Token: (token)")
// Save token for later retrieval
UserDefaults.standard.set(token, forKey: "APNSDeviceToken")
// Pass token to Flutter if needed
if let flutterViewController = mainFlutterWindow?.contentViewController as? FlutterViewController {
let firebaseMessagingChannel = FlutterMethodChannel(name: "plugins.flutter.io/firebase_messaging",
binaryMessenger: flutterViewController.engine.binaryMessenger)
firebaseMessagingChannel.invokeMethod("FirebaseMessaging#onTokenRefresh", arguments: token)
}
}
override func application(_ application: NSApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to register for remote notifications: (error.localizedDescription)")
}
}
Flutter Code (Dart)
import 'dart:io';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/services.dart';
import 'package:logger/logger.dart';
abstract class FcmServices {
static FirebaseMessaging messaging = FirebaseMessaging.instance;
static const MethodChannel _channel = MethodChannel('plugins.flutter.io/firebase_messaging');
static Future<void> _handleMethodCall(MethodCall call) async {
switch (call.method) {
case 'FirebaseMessaging#onTokenRefresh':
String token = call.arguments;
print('Received APNS Token: $token');
// Handle the APNS token here, e.g., save it, send it to a server, etc.
break;
default:
print('Unknown method ${call.method}');
}
}
static Future<void> initialize() async {
_channel.setMethodCallHandler(_handleMethodCall);
NotificationSettings settings = await messaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
Logger().i('User granted permission');
} else {
Logger().w('User declined or has not accepted permission');
return;
}
}
static String personID = 'productivity';
static Future<String?> getDeviceToken() async {
try {
String? token;
if (Platform.isMacOS) {
token = await messaging.getAPNSToken();
if (token == null) {
Logger().i('APNS token is null, retrying...');
await Future<void>.delayed(
const Duration(seconds: 3),
);
token = await messaging.getAPNSToken();
}
if (token != null) {
Logger().i('Successfully retrieved APNS token');
await messaging.subscribeToTopic(personID);
} else {
Logger().w('Failed to retrieve APNS token after retry.');
}
} else {
token = await messaging.getToken();
if (token != null) {
Logger().i('Successfully retrieved FCM token');
await messaging.subscribeToTopic(personID);
} else {
Logger().w('Failed to retrieve FCM token.');
}
}
return token;
} catch (e, s) {
Logger().e('Error retrieving token', error: e, stackTrace: s);
return null;
}
}
}
Issue
When I try to get the APNs token using FirebaseMessaging.instance.getAPNSToken()
on macOS, it always returns null
.
Troubleshooting Steps Taken
- Verified that the method channel is correctly set up in Swift.
- Added logging to check if the token retrieval is being reached.
- Ensured the token is saved and retrieved from
UserDefaults
correctly.
Questions
- Is there something missing in my setup?
- Are there additional configurations required for macOS to retrieve the APNs token?
- How can I verify that push notifications are correctly set up and that the device token is being registered?
Any help or guidance would be greatly appreciated!