Java – Google cloud messaging: when IOS app is in the background, it will not receive alerts
I have followed this tutorial https://developers.google.com/cloud-messaging/ios/client Implement GCM on my IOS application My application server is a Google application engine written in Java. I use GCM server jar https://github.com/google/gcm Library I think my certificate is very good. I can register, obtain tokens and even receive the contents of messages sent by my application server However, when the application is in the background, I will not receive any notification alerts. I will only receive notifications when I click the application icon to restart
I think this is because I only implemented didreceiveremotenotification: not didreceiveremotenotification: fetchcompletionhandler: so I implemented it instead of the first one, but I didn't receive a notification in the background. What's worse, The application crashed and said "unrecognized selector sent" instance didreceiveremotenotification: "it's like an error in userinfo. I do allow background mode in Xcode. This 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,kGGLInstanceIDAPNSServerTypeSand@R_184_2419@Option:@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 find out that I didn't receive a notice when I wasn't at the front desk?
Edit: Java code for sending GCM messages on the server side:
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: screenshot of 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 application server:
public static void sendGCMMessage(String tag,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(); }
Solution
Based on GCM documentation, you can_ Available is set to true
(on IOS, use this field to indicate the content available in APNs valid content. When sending a notification or message and setting it to true, it will wake up the inactive client application. On Android, data message wakes up the application by default. Chrome is not supported at present.)
content_ Available corresponds to Apple's content. You can find it in this Apple Push Notification Service documentation
In addition, you should use notification payload to send messages to IOS applications to display banners when the application is in the background
This is an example 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 an example. You can add other fields For example, in message In the Java class, you can add two private variables, one is private final Boolean contentavailable, and the other is private final < string, string > notification
You can try HTTP request notificaiton ": {" sound ":" default "," badge ":" 1 "," title ":" default "," body ":" test "," content_available ": true} ’https:// android . googleapis. COM / GCM / send, or try it in postman
Edit:
If your application has terminated and you want to display push notifications in the device, you can set high priority in the HTTP request body (note that setting messages to high priority will cause more battery consumption than normal priority messages)
HTTP request example:
{ "to" : "REGISTRATION_TOKEN","priority" : "normal",}