Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google Cloud Messaging: don't receive alerts when iOS App is in background

I have followed this tutorial https://developers.google.com/cloud-messaging/ios/client to implement GCM on my iOS Application. My app server is a google app engine written in Java and I use the gcm-server.jar https://github.com/google/gcm library. I think my certificates are fine and I can register, get a token and even receive the content of the messages sent by my app server. However, I don't receive any notification alerts when the app is in background, I always receive it only when I click on the app icon to restart it.

I thought that was because I was only implementing didReceiveRemoteNotification: and not didReceiveRemoteNotification:fetchCompletionHandler: so I implemented it instead of the first one but I don't receive notifications while in background either and worse, the app crashes saying something like"unrecognized selector sent to instance didReceiveRemoteNotification:" like something was wrong in the userInfo. I did allow background modes in xCode like required for it. Here is the code I use:

AppDelegate ()

@property (nonatomic, strong) NSDictionary *registrationOptions;
@property (nonatomic, strong) GGLInstanceIDTokenHandler registrationHandler;

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

//-- Set Notification
[[GCMService sharedInstance] startWithConfig:[GCMConfig defaultConfig]];
if ([application respondsToSelector:@selector(isRegisteredForRemoteNotifications)])
{
    NSLog(@"Case iOS8");
    // iOS 8 Notifications
    [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];

    [application registerForRemoteNotifications];
}
else
{
    NSLog(@"Case iOS7");
    // iOS < 8 Notifications
    [application registerForRemoteNotificationTypes:
     (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)];
}

self.registrationHandler = ^(NSString *registrationToken, NSError *error){
    if (registrationToken != nil) {

        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        [defaults setObject:registrationToken forKey:TOKENGCM];
        NSLog(@"Registration Token: %@", registrationToken);
        //some code
    } else {
        NSLog(@"Registration to GCM failed with error: %@", error.localizedDescription);
    }
};
return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application {
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
[[GCMService sharedInstance] disconnect];
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
// Connect to the GCM server to receive non-APNS notifications
[[GCMService sharedInstance] connectWithHandler:^(NSError *error) {
    if (error) {
        NSLog(@"Could not connect to GCM: %@", error.localizedDescription);
    } else {
        NSLog(@"Connected to GCM");
        // ...
    }
}];
}

- (void)applicationWillTerminate:(UIApplication *)application {
}


- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// Start the GGLInstanceID shared instance with the default config and request a registration
// token to enable reception of notifications
[[GGLInstanceID sharedInstance] startWithConfig:[GGLInstanceIDConfig defaultConfig]];
self.registrationOptions = @{kGGLInstanceIDRegisterAPNSOption:deviceToken,
                         kGGLInstanceIDAPNSServerTypeSandboxOption:@NO};
[[GGLInstanceID sharedInstance] tokenWithAuthorizedEntity:SENDER_ID
                                                    scope:kGGLInstanceIDScopeGCM
                                                  options:self.registrationOptions
                                                  handler:self.registrationHandler];
}

- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {
NSLog(@"Error in registration. Error: %@", err);
}

- (void)onTokenRefresh {
// A rotation of the registration tokens is happening, so the app needs to request a new token.
NSLog(@"The GCM registration token needs to be changed.");
[[GGLInstanceID sharedInstance] tokenWithAuthorizedEntity:SENDER_ID
                                                    scope:kGGLInstanceIDScopeGCM
                                                  options:self.registrationOptions
                                                  handler:self.registrationHandler];
}


- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo {
NSLog(@"Notification received: %@", userInfo);//This does print the content of my message in the console if the app is in foreground
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateActive) {
    NSString *cancelTitle = @"Close";
    NSString *showTitle = @"Show";
    NSString *message = [[userInfo valueForKey:@"aps"] valueForKey:@"alert"];
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Some title"
                                                        message:message
                                                       delegate:self
                                              cancelButtonTitle:cancelTitle
                                              otherButtonTitles:showTitle, nil];
    [alertView show];
}
else{
    NSLog(@"Notification received while inactive");
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber: 99];
    UIAlertView *BOOM = [[UIAlertView alloc] initWithTitle:@"BOOM"
                                                   message:@"app was INACTIVE"
                                                  delegate:self
                                         cancelButtonTitle:@"a-ha!"
                                         otherButtonTitles:nil];
    [BOOM show];
    NSLog(@"App was NOT ACTIVE");
   [[NSNotificationCenter defaultCenter] postNotificationName:@"Notification!"
                                                        object:nil
                                                      userInfo:userInfo];
}
// This works only if the app started the GCM service
[[GCMService sharedInstance] appDidReceiveMessage:userInfo];
}

//Implement that causes unrecognized selector crash 
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))handler {
NSLog(@"Notification received: %@", userInfo);
// This works only if the app started the GCM service
[[GCMService sharedInstance] appDidReceiveMessage:userInfo];
// Handle the received message
// Invoke the completion handler passing the appropriate UIBackgroundFetchResult value
// [START_EXCLUDE]
[[NSNotificationCenter defaultCenter] postNotificationName:@"notif"
                                                    object:nil
                                                  userInfo:userInfo];
handler(UIBackgroundFetchResultNoData);
// [END_EXCLUDE]
}

@end

Can someone figure out what I don't receive notifications when not in foreground?

EDIT: the Java code used on the server side to send the GCM message:

public static MulticastResult sendViaGCM(String tag, String message, List<String> deviceIdsList) throws IOException {
    Sender sender = new Sender(Constantes.API_KEY);
    // This message object is a Google Cloud Messaging object
    Message msg = new Message.Builder().addData("tag",tag).addData("message", message).build();
    MulticastResult result = sender.send(msg, deviceIdsList, 5);
    return result;
}

EDIT2: screenshots of the POST Request http://image.noelshack.com/fichiers/2015/34/1440193492-gcm1.png http://image.noelshack.com/fichiers/2015/34/1440193502-gcm2.png

EDIT3: the request I now send from my app server:

public static void sendGCMMessage(String tag, String message, List<String> deviceIdsList) {
    String request = "https://gcm-http.googleapis.com/gcm/send";
    try{
        URL url = new URL(request);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setDoOutput(true);
        //conn.setInstanceFollowRedirects(false);
        conn.setRequestMethod("POST");
        //Les deux headers obligatoires:
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setRequestProperty("Authorization", "key=" + API_KEY);
        //Construction du JSON:
        JSONObject fullJSON = new JSONObject();
        JSONObject data=new JSONObject();
        JSONObject notification=new JSONObject();
        data.put("tag", tag);
        data.put("message", message);
        notification.put("sound", "default");
        notification.put("badge", "1");
        notification.put("title", "default");
        notification.put("body", message);
        fullJSON.put("registration_ids", deviceIdsList);

        fullJSON.put("notification", notification);
        fullJSON.put("content_available", "true");
        fullJSON.put("data", data);

        //Phase finale:
        OutputStreamWriter wr= new OutputStreamWriter(conn.getOutputStream());
        wr.write(fullJSON.toString());
        wr.flush();
        wr.close();//pas obligatoire
        //conn.setUseCaches(false);
    }
    catch(Exception e){
        e.printStackTrace();
    }
like image 202
Gannicus Avatar asked Aug 21 '15 07:08

Gannicus


People also ask

Does firebase cloud messaging work on iOS?

React Native Firebase provides native integration of Firebase Cloud Messaging (FCM) for both Android & iOS. FCM is a cost free service, allowing for server-device and device-device communication.

How do I handle the firebase notification when an app is in foreground?

Firebase notifications behave differently depending on the foreground/background state of the receiving app. If you want foregrounded apps to receive notification messages or data messages, you'll need to write code to handle the onMessageReceived callback.

What is the difference between firebase cloud messaging and in app messaging?

Firebase Cloud Messaging is for sending the usual push notifications for an app appearing on the phone's status bar. Firebase In-App Messaging is to display messages "in the app" to users who are currently using the app.

Will iOS awake my app when I receive silent push notification?

Silent notifications are not meant as a way to keep your app awake in the background beyond quick refresh operations, nor are they meant for high priority updates.


2 Answers

Based on the GCM documentation, you can set the content_available to true.

(On iOS, use this field to represent content-available in the APNS payload. When a notification or message is sent and this is set to true, an inactive client app is awoken. On Android, data messages wake the app by default. On Chrome, currently not supported.)

The content_available is correspond to Apple's content-available, which you can find in this Apple Push Notification Service documentation.

Also, you should use Notification playload, for sending message to your iOS application, so that it can show a banner when your app is in background.

Here's a sample HTTP request:

https://gcm-http.googleapis.com/gcm/send
Content-Type:application/json
Authorization:key=API_KEY
{
   "to" : "REGISTRATION_TOKEN",
   "notification" : {
     "sound" : "default",
     "badge" : "1",
     "title" : "default",
     "body"  : "Test",
   },
   "content_available" : true,
}

The Java library is just a sample, you can add other fields to it. For example, in the Message.java class, you can add two private variables, one is private final Boolean contentAvailable, another one is private final Map<String, String> notification.

You can try a HTTP request in your terminal by doing curl -i -H "Content-Type:application/json" -H "Authorization:key=API_KEY" -X POST -d '{"to":"REGISTRATION_TOKEN", "notificaiton":{"sound":"default", "badge":"1", "title": "default", "body":"test",},"content_available":true}' https://android.googleapis.com/gcm/send, or try it in Postman.

Edited:

If your application was terminated, and you want to the push notifications to be shown in your device, you can set a high priority in your HTTP request body (beware that setting your messages to high priority contributes more to battery drain compared to normal priority messages).

Sample HTTP request:

{
   "to" : "REGISTRATION_TOKEN",
    "notification" : {
     "sound" : "default",
     "badge" : "1",
     "title" : "default",
     "body"  : "Test",
   },
   "content_available" : true,
   "priority" : "normal",
 }
like image 193
ztan Avatar answered Oct 18 '22 22:10

ztan


I had the same problem, could not receive topic notification when app was killed, this POST is working now, I had to add priority hight.

   {
   "to" : "/topics/offers",
   "notification" : {
     "sound" : "default",
     "badge" : "1",
     "body" : "Text",
   },
     "priority" : "high",   
}
like image 28
abaraga Avatar answered Oct 18 '22 22:10

abaraga