Making GCM work for iOS device in the background
Asked Answered
M

5

25

I'm trying to use GCM for IOS and Android clients. It seems to work fine with IOS when app is in the foreground, however, when the app is in the background, the notification center doesn't receive the message and didReceiveRemoteNotification with completionHandler doesn't get called.

I identified a problem as a wrongly formatted message from GCM to APNS. Namely, that's how it looks:

[message: New message, collapse_key: do_not_collapse, from: **************]
While, the IOS push notifications should have aps key in the notification, am I right ? As well as content-available set to 1. For example:

{ "aps" : { "content-available" : 1 }, "data-id" : 345 }

By the way, in the foreground app receives the message anyway, the problem is only with the background. Any advice on how should I approach a problem, to make GCM work for both ios and android?

UPDATE: That is what I found on the net:

Regarding actual communication, as long as the application is in the background on an iOS device, GCM uses APNS to send messages, the application behaving similarly as using Apple’s notification system. But when the app is active, GCM communicates directly with the app

So the message I received in the foreground mode:

[message: New message, collapse_key: do_not_collapse, from: **************]

Was the direct message from GCM(APNS did not participate in this affair at all). So the question is: does APNS reformat what GCM sends to it to adhere to ios notifications format? If so how do I know that APNS actually does something and whether it sends me a notification in different format ? Is there any way to view logs of incoming data from APNS ?

UPDATE: Okay, I managed to change the structure of the message and now in the foreground mode I receive the following message:

Notification received: ["aps": {"alert":"Simple message","content-available":1}, collapse_key: do_not_collapse, from: **************]

Now it seems to be well formatted, but there is still no reaction when the app is in the background. didReceiveRemoteNotifification completionHandler doesn't get called! What should I look for and where can a problem be ? Can the square bracket be a problem for push notification ? To be even more precise, ios doesn't post any alerts/badges/banners from that incoming notification.

Monachism answered 29/6, 2015 at 6:56 Comment(1)
M
26

For every poor soul wondering in quest for an answer to GCM background mystery. I solved it and the problem was in the format. I'm posting the right format as well as Java code needed to send Http request to GCM with some message. So the Http request should have two field in the header, namely:

Authorization:key="here goes your GCM api key"
Content-Type:application/json for JSON data type

then the message body should be a json dictionary with keys "to" and "notification". For example:

{
  "to": "gcm_token_of_the_device",
  "notification": {
    "sound": "default",
    "badge": "2",
    "title": "default",
    "body": "Test Push!"
  }
}

Here is the simple java program (using only java libraries) that sends push to a specified device, using GCM:

public class SendMessage {

    //config
    static String apiKey = ""; // Put here your API key
    static String GCM_Token = ""; // put the GCM Token you want to send to here
    static String notification = "{\"sound\":\"default\",\"badge\":\"2\",\"title\":\"default\",\"body\":\"Test Push!\"}"; // put the message you want to send here
    static String messageToSend = "{\"to\":\"" + GCM_Token + "\",\"notification\":" + notification + "}"; // Construct the message.

    public static void main(String[] args) throws IOException {
        try {

            // URL
            URL url = new URL("https://android.googleapis.com/gcm/send");

            System.out.println(messageToSend);
            // Open connection
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            // Specify POST method
            conn.setRequestMethod("POST");

            //Set the headers
            conn.setRequestProperty("Content-Type", "application/json");
            conn.setRequestProperty("Authorization", "key=" + apiKey);
            conn.setDoOutput(true);

            //Get connection output stream
            DataOutputStream wr = new DataOutputStream(conn.getOutputStream());

            byte[] data = messageToSend.getBytes("UTF-8");
            wr.write(data);

            //Send the request and close
            wr.flush();
            wr.close();

            //Get the response
            int responseCode = conn.getResponseCode();
            System.out.println("\nSending 'POST' request to URL : " + url);
            System.out.println("Response Code : " + responseCode);

            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String inputLine;
            StringBuffer response = new StringBuffer();

            while ((inputLine = in.readLine()) != null) {
                response.append(inputLine);
            }
            in.close();

            //Print result
            System.out.println(response.toString()); //this is a good place to check for errors using the codes in http://androidcommunitydocs.com/reference/com/google/android/gcm/server/Constants.html

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Monachism answered 2/7, 2015 at 5:22 Comment(10)
Yes, the Java code for posting works and I'm able to see my push in the Notification drawer but none of my didReceiveRemoteNotification* methods are being called. That means I am receiving the push however, I cannot intercept it?! How did you solve this?Navigation
In other words, I guess my app is not being woken up because I'm not putting "content-available":"1" via my server. How and where did you put this? I tried adding this key in the notification key but it doesnt workNavigation
You should put it in the http request body. In this particular example, just add \"content_available\":\"1\" as a key to messageToSend string.Monachism
Yep thanks! I realised this when I saw this SO question: #30616802Navigation
THANK YOU. There is no indication in the Google docs that you need to use the "notification" key for it to work. We were trying to use "data" and getting nothing on the device, though GCM would tell us that a notification was successfully sent.Unijugate
Thanks. More info here developers.google.com/cloud-messaging/… .Electroencephalogram
does your method work when the app has been closed by the user, jiggle and swipe up?Prejudge
The data payload and notification payload are both applicable on iOS & Android. On iOS the difference is that notification payload is sent through APNS while data payload is sent through GCM's own connection which is only there when app is in foreground. SO,If I want to use GCM in both android and ios,the payload should contain only notification?or is both data and notification is fine?Inhale
worked very well... Can you let me know Where to pass my custom data like key value pair of "UserName":"Sam"... I am confused to pass that inside "notification" object or Outside?Cleghorn
This should be inside the notification object.Monachism
H
24

It is important to note that on iOS devices, if your app using GCM has been killed (swiped in the app switcher) then your device will only awake upon receiving a message if you send a notification with the "notification", "content_available" and "priority" (set to "high"). If you have one or the other, it may work when the app has been killed. But once the app has been killed, you MUST have all 3 of these keys in your notification payload.

Something like this:

{
    "to": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "notification": {
        "title": "test",
        "body": "my message"
    },
    "priority": "high",
    "content_available": true
}
Haematogenous answered 22/4, 2016 at 3:30 Comment(0)
S
7

Try setting priority key in Your payload according to the docs here: https://developers.google.com/cloud-messaging/concept-options#setting-the-priority-of-a-message

  • normal priority is equal to 5 in APNs terminology
  • high priority is equal to 10 in APNs terminology

Here You have more info about Apple APNs priorities and its behaviour here: https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/APNsProviderAPI.html

Example payload:

{
  "to": "gcm_device_token",
  "priority": "high",
  "content_available": false,
  "notification": {
    "sound": "default",
    "badge": "1",
    "title": "Push Title",
    "body": "Push Body"
  }
}
Siderosis answered 2/1, 2016 at 21:54 Comment(5)
The data payload and notification payload are both applicable on iOS & Android. On iOS the difference is that notification payload is sent through APNS while data payload is sent through GCM's own connection which is only there when app is in foreground. SO,If I want to use GCM in both android and ios,the payload should contain only notification?or is both data and notification is fine?Inhale
What do U mean data in last sentence?Siderosis
The payload can be either "data " or "notification" right?developers.google.com/cloud-messaging/concept-optionsInhale
I didn't try both but it seems from documentation that You attached that both notification and data is allowed in payload.Siderosis
what is gcm_device_token?? where can I get it??Boyla
P
0
  1. Did this for C#,think this could be help.I also had this problem after adding content_available to true , it worked. According to Apple documentation, that's the way OS understand that there is a notification when app is in background.

        JObject notification =new Object(
        new JProperty("to","put token which you get from running client application "),   
        new JProperty("content_available",true),
        new JProperty("priority","high"),
        new JProperty("notification",new JObject(
        new JProperty("title","message"),
        new JProperty("body","test message")
         ))
        );
    
Prescriptive answered 24/9, 2016 at 20:48 Comment(0)
L
0

Using nodeJS and the node-gcm npm library I've found that the following payload works for me for iOS, for Android I'm sending a slightly different payload because I want to intercept all push notifications before showing them in the system tray:

{ dryRun: false,
  data: 
   { customKey1: 'CustomValue1',
     customKey2: 'CustomValue2',
     content_available: '1',
     priority: 'high' },
  notification: 
   { title: 'My Title',
     icon: 'ic_launcher',
     body: 'My Body',
     sound: 'default',
     badge: '2' } }

Of course you'll need to ensure that your iOS app can handle the inbound notification but this should come through when your app is in the background.

Leptophyllous answered 25/11, 2016 at 10:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.