We are implementing server side remote notifications for device to device in IOS 11. For that first we created IOS app on Firebase, and configured cloud messaging details. Everything is done there and then. Now from there legacy server key is used at our own server, where we wrote scripts for sending remote notifications to different devices according to their Firebase token.
Notifications generation and disposal is successful in both the cases either trying is from Postman, or through programmatically from IOS device. We have tried our server both locally and on cloud.
When we send normal messages from Firebase console, it also works fine and we did receive remote notifications from there.
Every time we send notifications via server to Firebase and then to device with the help of APNs, we couldn't receive that remote notification.
Our App delegate looks like this.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
print("Application did finish launching")
FirebaseApp.configure()
// [END set_messaging_delegate]
// Register for remote notifications. This shows a permission dialog on first run, to
// show the dialog at a more appropriate time move this registration accordingly.
// [START register_for_notifications]
if #available(iOS 10.0, *) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
// [START set_messaging_delegate]
Messaging.messaging().delegate = self
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
// [END register_for_notifications]
return true
}
Further, we have things like this:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("APNs token retrieved: \(deviceToken)")
Messaging.messaging().apnsToken = deviceToken
print("Token is here \(String(describing: Messaging.messaging().fcmToken))")
print("Token is here \(String(describing: Messaging.messaging().apnsToken))")
if UserDefaults.standard.object(forKey: "FCM_Token") == nil
{
UserDefaults.standard.set(Messaging.messaging().fcmToken, forKey: "FCM_Token")
}
else
{
let fcmSavedToken = UserDefaults.standard.value(forKey: "FCM_Token") as! String
if fcmSavedToken == Messaging.messaging().fcmToken
{
}
else
{
UserDefaults.standard.set(Messaging.messaging().fcmToken, forKey: "FCM_Token")
}
}
// With swizzling disabled you must set the APNs token here.
// Messaging.messaging().apnsToken = deviceToken
}
Notifications and Firebase Messaging Delegate functions are here:
// [START ios_10_message_handling]
@available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {
// Receive displayed notifications for iOS 10 devices.
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo = notification.request.content.userInfo
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
// Print full message.
print(userInfo)
// Change this to your preferred presentation option
completionHandler([.alert, .badge, .sound])
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
// Print full message.
print(userInfo)
completionHandler()
}
}
// [END ios_10_message_handling]
extension AppDelegate : MessagingDelegate {
// [START refresh_token]
func messaging(_ messaging: Messaging, didRefreshRegistrationToken fcmToken: String) {
print("Firebase registration token: \(fcmToken)")
print("Received Remote Message: 1\nCheck Out:\n")
}
// [END refresh_token]
// [START ios_10_data_message]
// Receive data messages on iOS 10+ directly from FCM (bypassing APNs) when the app is in the foreground.
// To enable direct data messages, you can set Messaging.messaging().shouldEstablishDirectChannel to true.
func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
print("Received data message: \(remoteMessage.appData)")
print("Received Remote Message: 2\nCheck Out:\n")
}
// Receive data message on iOS 10 devices while app is in the foreground.
func application(received remoteMessage: MessagingRemoteMessage) {
print("Received Remote Message: 3\nCheck In:\n")
debugPrint(remoteMessage.appData)
print("Received Remote Message: 3\nCheck Out:\n")
}
// [END ios_10_data_message]
}
Finally we also have implemented remote notifications method:
// Remote Notification
// [START receive_message]
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
// Print full message.
print(userInfo)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
// Print full message.
print(userInfo)
print("Received Remote Message: 4\nCheck Out:\n")
print("Main Remote Message: 4\nCheck Out:\n")
completionHandler(UIBackgroundFetchResult.newData)
}
// [END receive_message]
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Unable to register for remote notifications: \(error.localizedDescription)")
}
FCM token is generated by Firebase and is also set to Messaging.messaging().apnsToken, in order to communicate with APNS.
Further this token is provided to our server for sending remote notification from the server with this token. When this token is provided to Firebase Console, it really sends remote notification and did receive by our app, but when the same token is used to send from our targeted server, message is sent successfully from the server, but couldn't received by our app.
If you're receiving notifications through FCM console then your client code is likely not the issue. What's the server payload you're trying to send?
@morganchen12 Thanks for replying me. I was really feeling problem in depth.
Here is my server side code:
In the Config.php my code is
<?php
define('DB_USERNAME','root');
define('DB_PASSWORD','');
define('DB_NAME','fcm');
define('DB_HOST','localhost');
//defined a new constant for firebase api key
define('Firebase Legacy Server Key');
In the file Firebase.php my code is
<?php
class Firebase {
public function send($registration_ids, $message) {
$fields = array(
'registration_ids' => $registration_ids,
'data' => $message,
);
return $this->sendPushNotification($fields);
}
/*
* This function will make the actuall curl request to firebase server
* and then the message is sent
*/
private function sendPushNotification($fields) {
//importing the constant files
require_once 'Config.php';
//firebase server url to send the curl request
$url = 'https://fcm.googleapis.com/fcm/send';
//building headers for the request
$headers = array(
'Authorization: key=' . FIREBASE_API_KEY,
'Content-Type: application/json'
);
//Initializing curl to open a connection
$ch = curl_init();
//Setting the curl url
curl_setopt($ch, CURLOPT_URL, $url);
//setting the method as post
curl_setopt($ch, CURLOPT_POST, true);
//adding headers
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//disabling ssl support
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
//adding the fields in json format
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields));
//finally executing the curl request
$result = curl_exec($ch);
if ($result === FALSE) {
die('Curl failed: ' . curl_error($ch));
}
//Now close the connection
curl_close($ch);
//and return the result
return $result;
}
}
In the file Push.php my code is
<?php
class Push {
//notification title
private $title;
//notification message
private $message;
//notification image url
private $image;
//initializing values in this constructor
function __construct($title, $message, $image) {
$this->title = $title;
$this->message = $message;
$this->image = $image;
}
//getting the push notification
public function getPush() {
$res = array();
$res['data']['title'] = $this->title;
$res['data']['message'] = $this->message;
$res['data']['image'] = $this->image;
return $res;
}
}
And finally I'm doing this to send push notification to a single user, in the file sendSinglePush.php
<?php
//importing required files
require_once 'DbOperation.php';
require_once 'Firebase.php';
require_once 'Push.php';
$db = new DbOperation();
$response = array();
if($_SERVER['REQUEST_METHOD']=='POST'){
//hecking the required params
if(isset($_POST['title']) and isset($_POST['message']) and isset($_POST['email'])){
//creating a new push
$push = null;
//first check if the push has an image with it
if(isset($_POST['image'])){
$push = new Push(
$_POST['title'],
$_POST['message'],
$_POST['image']
);
}else{
//if the push don't have an image give null in place of image
$push = new Push(
$_POST['title'],
$_POST['message'],
null
);
}
//getting the push from push object
$mPushNotification = $push->getPush();
//getting the token from database object
$devicetoken = $db->getTokenByEmail($_POST['email']);
//creating firebase class object
$firebase = new Firebase();
//sending push notification and displaying result
echo $firebase->send($devicetoken, $mPushNotification);
}else{
$response['error']=true;
$response['message']='Parameters missing';
}
}else{
$response['error']=true;
$response['message']='Invalid request';
}
echo json_encode($response);
My server response comes the following way:
{
"multicast_id": 6246214506600150616,
"success": 1,
"failure": 0,
"canonical_ids": 0,
"results": [
{
"message_id": "0:1508391551307562%8929e6c1f9fd7ecd"
}
]
}
In the legacy server key, when I give this from the console of an Android App, it works very fine, and the notifications appear on the device. But in case of putting legacy server key of an Firebase Console IOS app, it gives the response as above, which is indeed success, but couldn't show notification to my device.
What's the size of the images you're sending? APNs has a small limit on the size of messages that can be sent, so it's possible if you're sending somewhat large images your messages will not be delivered by APNs.
It's not the image, actually I am sending an image URL, that would be loaded on the client side. For Android, messages with and without URL, are successfully delivered.
In case of IOS I've only tried message with a title and message, but bot sending URL.
Like Message Title: "Asim's Message"
Message: "Check out my message."
What do you think of the Server Key: I'm putting Legacy server key.

I'm not putting Web API Key.

@puf I/ve seen your recommendations and featuring activities on stack overflow.
Please help me out here.
@iamasimkhan try sending directly through APNs. If it succeeds, then we can be sure that there's an issue in your FCM setup.
You should migrate to the new server key if you can, though the old one should continue to work.
closing due to inactivity, will reopen if there are more responses.
I am having the same exact problem, has anyone found any solution for this problem?
Have you tried sending directly through APNS?
Yes I tried directly form firebase console, and it works, but not form the server(BackEnd).
Here is a question I posted in Stack but din't get answer
https://stackoverflow.com/questions/50315330/firebase-notification-in-ios
What's the payload you're sending through Firebase Console?
@Prashanna7
Yes the same situation happened at time when I triggered this GitHub query. Then literally I got the solved by the time.
This happens
function sendPushNotification($fields)
{
// //importing the constant files
// require_once '../config/database.php';
// $db = new Database();
//firebase server url to send the curl request
$url = 'https://fcm.googleapis.com/fcm/send';
//building headers for the request
$headers = array('Authorization: key=YOUR_KEY' , 'Content-Type: application/json');
//Initializing curl to open a connection
$ch = curl_init();
//Setting the curl url
curl_setopt($ch, CURLOPT_URL, $url);
//setting the method as post
curl_setopt($ch, CURLOPT_POST, true);
//adding headers
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//disabling ssl support
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
//adding the fields in json format
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields));
//finally executing the curl request
$result = curl_exec($ch);
if ($result === FALSE)
{
require_once 'firebase-response-model/model.php';
// Response From FCM
$returnStatus = new Response();
$returnStatus->multicast_id = "";
$returnStatus->success = 0;
$returnStatus->failure = 1;
$returnStatus->canonical_ids = "";
$results = new ResponseResult();
$results->message_id = "There is no Successfull Message Sent.";
$results->error = "Curl failed: ". curl_error($ch);
$returnStatus->results = $results;
return json_encode($returnStatus);
}
//Now close the connection
curl_close($ch);
//and return the result
return $result;
}
Now you should provide the $fields value in the same manner as explained by Google Firebase in the official Documentation here.
https://firebase.google.com/docs/cloud-messaging/http-server-ref#notification-payload-support
Let us define a dummy structure for$fields.
$fields = array(
'to' => YOUR_DEVICE_TOKEN,
'priority' => 'high',
'notification' => $notification,
'content_available'=> true,
'data' => $message
);
Where $message and $notification are as below.
$notification = array('title' =>'Title' , 'text' =>'Hello', 'sound' => 'default', 'badge' => '1');
$message = array();
// Completing Details :: Finally Making Array for sending
$message['id'] = '1';
$message['name'] = 'Ali';
$message['phone'] = '03344823832';
$message['address'] = 'Lahore, Pakistan.';
Most helpful comment
If you're receiving notifications through FCM console then your client code is likely not the issue. What's the server payload you're trying to send?