17

Somewhat recently at the Google I/O event Google renovated Firebase & added a lot of new features, and touched up on the remaining ones. I have been trying to implement the iOS Push Notifications via Firebase into my app through the most basic level, So I created a very simple app that really does nothing besides receive remote push notifications.

Inside of Firebase, I have uploaded my certificate and within Xcode my provisioning profiles have been added to both the target and project, and in Firebase I have uploaded the correct certificate. Below is the code contained inside of my AppDelegate.swift file but because my ViewController.swift is "empty," I did not include it.

Although there are no crashes or runtime errors, when I load the app, I accept the notifications. Then, I exit the app and turn off my device. In Firebase, I send the notification to the correct app. After a couple of minutes, in Firebase it says the notification was "Completed".

However, I never received the notification on the device. So, in conclusion, I need a solution to send Firebase this deviceToken and then use 'Firebase Notifications' to send the push notification Message.

Any help for my code or in general would be greatly appreciated and I hope this helps future viewers. Thank you! My code in AppDelegate.swift :

import UIKit
import Firebase
import FirebaseMessaging

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

        FIRApp.configure()

        let notificationTypes : UIUserNotificationType = [UIUserNotificationType.Alert, UIUserNotificationType.Badge, UIUserNotificationType.Sound]

        let notificationSettings = UIUserNotificationSettings(forTypes: notificationTypes, categories: nil)

        application.registerForRemoteNotifications()
        application.registerUserNotificationSettings(notificationSettings)

        return true
    }

    func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {

        print("Device Token: \(deviceToken)")

    }

    func applicationWillResignActive(application: UIApplication) {

    }

    func applicationDidEnterBackground(application: UIApplication) {

    }

    func applicationWillEnterForeground(application: UIApplication) {

    }

    func applicationDidBecomeActive(application: UIApplication) {

    }

    func applicationWillTerminate(application: UIApplication) {

    }

    func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {

        print("MessageID : \(userInfo["gcm.messgae_id"]!)") // or gcm_etc...

        print(userInfo)

    }


}
Mayur Prajapati
  • 5,454
  • 7
  • 41
  • 70
EQOxx123
  • 244
  • 1
  • 2
  • 9
  • 1
    " turn off my device." Err, how can you receive the notification if the device is turned off? Do you mean the notification is not received after you turn the device back on? That's expected behavior. – Gruntcakes Jun 06 '16 at 22:30
  • So, on my iPhone, I open the application for the first time. I accept the enable notifications pop-up. Then I exit the app and turn it off. What I was expecting was that the notification would appear on your lock screen when the notification goes through then light up the lock screen, but nothing happens. – EQOxx123 Jun 06 '16 at 22:49
  • 1
    If you turn the device off there is no lock screen, how can there be if its turned off ? if you turn it off there's no power. So I don't understand what you are saying when you keep saying turn it off. Turn what off exactly? – Gruntcakes Jun 06 '16 at 22:54
  • Anyway, apart from that, lets assume the device is turned on and you're still not getting pushes. You code isn't getting the push token anywhere. How are you therefore supplying the push token to firebase? – Gruntcakes Jun 06 '16 at 23:02
  • That is what I am a little confused about. I thought the device token was not needed, and that firebase sends the notification to everyone who has the app. When I say "off," I do not mean powered all the way off but where you would be able to swipe to the left and put your password in. Thanks. – EQOxx123 Jun 06 '16 at 23:04
  • The token is essential. – Gruntcakes Jun 06 '16 at 23:06
  • Yes, you are right. How do you get Firebase that token. In my AppDelegate.swift file I am able to get that device token but how do i supply that to Firebase? – EQOxx123 Jun 06 '16 at 23:08
  • I don't know Firebase, it will be in the documentation or in previous SO questions. But push simply cannot work without the token being specified. – Gruntcakes Jun 06 '16 at 23:13
  • Ok, thank you for clarification. I edited my question. – EQOxx123 Jun 06 '16 at 23:15

4 Answers4

32

Updated: As of Firebase 4.0.4, you can follow https://github.com/onmyway133/blog/issues/64

HOW APNS DEVICE TOKEN IS HANDLED

I've been reading Send a Notification to a User Segment on iOS but there is no mention of APNS device token, which is crucial to push notifications.

So Firebase must be doing some swizzling under the hood. In fact it is. Reading backend documentation Downstream Messages gives us the idea

Swizzling disabled: mapping your APNs token and registration token

If you have disabled method swizzling, you'll need to explicitly map your APNs token to the FCM registration token. Override the

methods didRegisterForRemoteNotificationsWithDeviceToken to retrieve the APNs token, and then call setAPNSToken.

func application(application: UIApplication,
                   didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
  FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: FIRInstanceIDAPNSTokenTypeSandbox)
}

I particularly try to avoid swizzling as much as possible. Reading Migrate a GCM Client App for iOS to Firebase Cloud Messaging gives us how to do disable it

Enabling/disabling method swizzling

Method swizzling available with FCM simplifies your client code. However, for developers who prefer not to use it, FCM allows you to disable method swizzling by adding the FIRMessagingAutoRegisterEnabledflag in the app’s Info.plist file and setting its value to NO (boolean value).

FCM swizzling affects how you handle the default registration token, and how you handle downstream message callbacks. Where

applicable, this guide provides migration examples both with and without method swizzling enabled.

SHOW ME THE CODE

Have this in your Podfile

pod 'Firebase'
pod 'FirebaseMessaging'

Here is the completed code

import Firebase
import FirebaseMessaging

override func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
  FIRApp.configure()

  NSNotificationCenter.defaultCenter().addObserver(self,
                                                   selector: #selector(tokenRefreshNotification(_:)),
                                                   name: kFIRInstanceIDTokenRefreshNotification,
                                                   object: nil)
}

// NOTE: Need to use this when swizzling is disabled
public func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {

  FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: FIRInstanceIDAPNSTokenType.Sandbox)
}

func tokenRefreshNotification(notification: NSNotification) {
  // NOTE: It can be nil here
  let refreshedToken = FIRInstanceID.instanceID().token()
  print("InstanceID token: \(refreshedToken)")

  connectToFcm()
}

func connectToFcm() {
  FIRMessaging.messaging().connectWithCompletion { (error) in
    if (error != nil) {
      print("Unable to connect with FCM. \(error)")
    } else {
      print("Connected to FCM.")
    }
  }
}

public func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
  print(userInfo)
}
onmyway133
  • 45,645
  • 31
  • 257
  • 263
13

Integrate without CocoaPods

First Read Firebase Doc. => Firebase Doc

  1. Register project on Firebase here => Register Project here

  2. Get GoogleService-Info.plist file from here=> project=> settings => General

  3. GoogleService-Info.plist file drop in your project.

  4. set Notification .p12 Certificates (Production and Development) in Firebase => project=> settings => Cloud Messaging

  5. Download Firebase SDK here => Firebase SDK Download

  6. Create SDK folder in your project and Drop all SDK Folder in it.

  7. Now Add this Framework in your Xcode => libicucore.tbd

  8. Set Background Modes ON in Xcode => Projects => Capabilities => Background Mode ON => RemoteNotification

  9. Add in your Info.Plist file FirebaseAppDelegateProxyEnabled Set BOOL NO.

enter image description here

In Objective-c your Appdelegate.m file

#import "AppDelegate.h"
#import "Firebase.h"
#import "AFNHelper.h"

@interface AppDelegate (){
    
    NSString *InstanceID;
}
@property (nonatomic, strong) NSString *strUUID;
@property (nonatomic, strong) NSString *strDeviceToken;
@end
@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    UIUserNotificationType allNotificationTypes =
    (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge);
    UIUserNotificationSettings *settings =
    [UIUserNotificationSettings settingsForTypes:allNotificationTypes categories:nil];
    [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
    [[UIApplication sharedApplication] registerForRemoteNotifications];

    [FIRApp configure];
   
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(tokenRefreshNotification:) name:kFIRInstanceIDTokenRefreshNotification object:nil];
    
    return YES;
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    
    NSLog(@"Message ID: %@", userInfo[@"gcm.message_id"]);
    [[FIRMessaging messaging] appDidReceiveMessage:userInfo];
    
    NSLog(@"userInfo=>%@", userInfo);
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    
    [[FIRInstanceID instanceID] setAPNSToken:deviceToken type:FIRInstanceIDAPNSTokenTypeProd];
    NSLog(@"deviceToken1 = %@",deviceToken);
    
}
- (void)tokenRefreshNotification:(NSNotification *)notification {
   NSLog(@"instanceId_notification=>%@",[notification object]);
    InstanceID = [NSString stringWithFormat:@"%@",[notification object]];
   
 [self connectToFcm];  
}

- (void)connectToFcm {

[[FIRMessaging messaging] connectWithCompletion:^(NSError * _Nullable error) {
    if (error != nil) {
        NSLog(@"Unable to connect to FCM. %@", error);
    } else {
        
        NSLog(@"InstanceID_connectToFcm = %@", InstanceID);
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            
            dispatch_async(dispatch_get_main_queue(), ^{
                [self sendDeviceInfo];
                NSLog(@"instanceId_tokenRefreshNotification22=>%@",[[FIRInstanceID instanceID] token]);
                
            });
        });
        
        
    }
}];
}
Community
  • 1
  • 1
Sanandiya Vipul
  • 764
  • 4
  • 16
  • I did this and checked it. I thought I received the notifications once but can't receive again. Check the question I posted here in which I put my whole code: http://stackoverflow.com/questions/37965615/cocos2d-fcm-push-notification-not-working – Paresh Thakor Jun 23 '16 at 17:44
  • 3
    This works fine when iOS app in background OR foreground mode. But it is not working when iOS app will terminate. I could not receive push notification when app will kill. – Parthpatel1105 Jul 15 '16 at 05:51
  • u have somthing missing bro my app is live on app store its work fine for me – Sanandiya Vipul Jul 15 '16 at 09:18
  • 2
    Don't use this in production! No one will accept push notifications permission on first app start. – Iulian Onofrei Nov 21 '16 at 16:12
  • @lulian Onofrei I am using this in my app and my app live and its work fine. – Sanandiya Vipul Nov 22 '16 at 12:41
  • @SanandiyaVipul how can i test static notification? – Krutarth Patel Dec 30 '16 at 10:38
  • @KrutarthPatel U can use APNS Tester software for Testing. – Sanandiya Vipul Jan 02 '17 at 04:41
  • in my case [[FIRInstanceID instanceID] token] return nil in first launch app.. :( mb someone know how to fix it or use InstanceID just? – Bimawa Jun 02 '17 at 15:32
  • i have followed all the steps but fails to understand one procedure . when i am sending message from firebase manually , i get the notification and message as well, but when i call firebase to send notification through web-service , i am not getting any notifications nor any messages . same scenario with android is working fine . so for ios , what and which methods should i use more ? am i missing some other features to follow ? please help me with this . – Moxarth Jul 04 '17 at 05:52
  • [[FIRInstanceID instanceID] setAPNSToken:deviceToken type:FIRInstanceIDAPNSTokenTypeProd]; // this is deprecated – Andrew Smith May 15 '18 at 21:40
5

Docs are pretty poor for the FCM for iOS now.

Follow the sample app they have on github

Important part added here :

import Firebase
import FirebaseInstanceID
import FirebaseMessaging

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    // Register for remote notifications
    if #available(iOS 8.0, *) {
      let settings: UIUserNotificationSettings =
      UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
      application.registerUserNotificationSettings(settings)
      application.registerForRemoteNotifications()
    } else {
      // Fallback
      let types: UIRemoteNotificationType = [.Alert, .Badge, .Sound]
      application.registerForRemoteNotificationTypes(types)
    }

    FIRApp.configure()

    // Add observer for InstanceID token refresh callback.
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.tokenRefreshNotificaiton),
        name: kFIRInstanceIDTokenRefreshNotification, object: nil)

    return true
  }

 func tokenRefreshNotificaiton(notification: NSNotification) {
    let refreshedToken = FIRInstanceID.instanceID().token()!
    print("InstanceID token: \(refreshedToken)")

    // Connect to FCM since connection may have failed when attempted before having a token.
    connectToFcm()
  }
  // [END refresh_token]

  // [START connect_to_fcm]
  func connectToFcm() {
    FIRMessaging.messaging().connectWithCompletion { (error) in
      if (error != nil) {
        print("Unable to connect with FCM. \(error)")
      } else {
        print("Connected to FCM.")
      }
    }
  }

Now your token has been sent to the FCM server

Shubhank
  • 21,721
  • 8
  • 65
  • 83
0

The official iOS FCM client is a fat monster! I've created an alternative FCM client that registers iOS app in FCM service to receive push notifications.

onegray
  • 5,105
  • 1
  • 23
  • 29