Problème:
iOS ne reçoit aucune notification à distance de GCM, mais ne trouve aucune information relative aux raisons pour lesquelles cela serait le cas.
Première mise en œuvre des notifications Push, ne sachant pas quelle est la cause du problème.
Situation:
Je travaille actuellement sur la version iOS d'une application qui utilise GCM pour les notifications Push. Les notifications sont bien reçues sur Android, cependant, il ne semble pas recevoir du tout sur iOS.
Lorsque je lance l'application, la console m'indique que tout va bien, a un jeton, est connecté à GCM et est abonné à des rubriques.
app [579: 45511] Jeton d'inscription: bk3RNwTe3H0: CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1pLTQ/8t-5QNiXbYwZYEWiSFD-frQKlV8lgI
app [579: 45511] Connecté à GCM
app [579: 45511] Déjà abonné à/topics/global
Cependant, il ne reçoit aucune notification et lorsque je descends dans le centre de notifications ou dans le centre de contrôle, le message suivant apparaît dans la console
app [579: 45511] Impossible de se connecter à GCM: l'opération n'a pas pu être terminée. (erreur com.google.gcm 2001.)
qui ne me dit pas grand chose d'autre que de parler de ...
/// KeyPair manquant.
kGGLInstanceIDOperationErrorCodeMissingKeyPair = 2001,
D'autre part, lorsque je l'envoie en arrière-plan avec la fonctionnalité multitâche et que je le ramène, je reçois à nouveau ceci:
app [579: 45511] connecté à GCM
app [579: 45511] Déjà abonné à/topics/global
Installer:
J'ai suivi les instructions de GCM pour la configuration dans iOS et j'ai même fait référence à GcmExample.xcodeproj pour la mise en œuvre (à tel point que le code est exactement identique).
Définissez info.plist pour 'Modes d'arrière-plan requis' -> 'L'application télécharge du contenu en réponse à des notifications Push'
Nous sommes tombés sur une autre question (impossible à trouver) concernant GCM et les adresses IP ne figurant pas sur la liste blanche, mais ils ont décidé que ce n'était pas le problème.
Code:
#import "AppDelegate.h"
@interface AppDelegate ()
@property(nonatomic, strong) void (^registrationHandler) (NSString *registrationToken, NSError *error);
@property(nonatomic, assign) BOOL connectedToGCM;
@property(nonatomic, strong) NSString* registrationToken;
@property(nonatomic, assign) BOOL subscribedToTopic;
@end
NSString *const SubscriptionTopic = @"/topics/global";
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// [START_EXCLUDE]
_registrationKey = @"onRegistrationCompleted";
_messageKey = @"onMessageReceived";
// Configure the Google context: parses the GoogleService-Info.plist, and initializes
// the services that have entries in the file
NSError* configureError;
[[GGLContext sharedInstance] configureWithError:&configureError];
if (configureError != nil) {
NSLog(@"Error configuring the Google context: %@", configureError);
}
_gcmSenderID = [[[GGLContext sharedInstance] configuration] gcmSenderID];
// [END_EXCLUDE]
// Register for remote notifications
UIUserNotificationType allNotificationTypes =
(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge);
UIUserNotificationSettings *settings =
[UIUserNotificationSettings settingsForTypes:allNotificationTypes categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
[[UIApplication sharedApplication] registerForRemoteNotifications];
// [END register_for_remote_notifications]
// [START start_gcm_service]
[[GCMService sharedInstance] startWithConfig:[GCMConfig defaultConfig]];
// [END start_gcm_service]
__weak typeof(self) weakSelf = self;
// Handler for registration token request
_registrationHandler = ^(NSString *registrationToken, NSError *error){
if (registrationToken != nil) {
weakSelf.registrationToken = registrationToken;
NSLog(@"Registration Token: %@", registrationToken);
[weakSelf subscribeToTopic];
NSDictionary *userInfo = @{@"registrationToken":registrationToken};
[[NSNotificationCenter defaultCenter] postNotificationName:weakSelf.registrationKey
object:nil
userInfo:userInfo];
} else {
NSLog(@"Registration to GCM failed with error: %@", error.localizedDescription);
NSDictionary *userInfo = @{@"error":error.localizedDescription};
[[NSNotificationCenter defaultCenter] postNotificationName:weakSelf.registrationKey
object:nil
userInfo:userInfo];
}
};
[[NSNotificationCenter defaultCenter] postNotificationName:_messageKey
object:nil
userInfo:nil];
return YES;
}
- (void)subscribeToTopic {
// If the app has a registration token and is connected to GCM, proceed to subscribe to the
// topic
if (_registrationToken && _connectedToGCM) {
[[GCMPubSub sharedInstance] subscribeWithToken:_registrationToken
topic:SubscriptionTopic
options:nil
handler:^(NSError *error) {
if (error) {
// Treat the "already subscribed" error more gently
if (error.code == 3001) {
NSLog(@"Already subscribed to %@",
SubscriptionTopic);
} else {
NSLog(@"Subscription failed: %@",
error.localizedDescription);
}
} else {
self.subscribedToTopic = true;
NSLog(@"Subscribed to %@", SubscriptionTopic);
}
}];
}
}
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
// [START disconnect_gcm_service]
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
[[GCMService sharedInstance] disconnect];
// [START_EXCLUDE]
_connectedToGCM = NO;
// [END_EXCLUDE]
}
// [END disconnect_gcm_service]
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
// 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 {
_connectedToGCM = true;
NSLog(@"Connected to GCM");
// [START_EXCLUDE]
[self subscribeToTopic];
// [END_EXCLUDE]
}
}];
}
// [END connect_gcm_service]
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
// [START receive_apns_token]
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// [END receive_apns_token]
// [START get_gcm_reg_token]
// Start the GGLInstanceID shared instance with the default config and request a registration
// token to enable reception of notifications
[[GGLInstanceID sharedInstance] startWithConfig:[GGLInstanceIDConfig defaultConfig]];
_registrationOptions = @{kGGLInstanceIDRegisterAPNSOption:deviceToken,
kGGLInstanceIDAPNSServerTypeSandboxOption:@YES};
[[GGLInstanceID sharedInstance] tokenWithAuthorizedEntity:_gcmSenderID
scope:kGGLInstanceIDScopeGCM
options:_registrationOptions
handler:_registrationHandler];
// [END get_gcm_reg_token]
}
// [START receive_apns_token_error]
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
NSLog(@"Registration for remote notification failed with error: %@", error.localizedDescription);
// [END receive_apns_token_error]
NSDictionary *userInfo = @{@"error" :error.localizedDescription};
[[NSNotificationCenter defaultCenter] postNotificationName:_registrationKey
object:nil
userInfo:userInfo];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
NSLog(@"Notification received: %@", userInfo);
// This works only if the app started the GCM service
[[GCMService sharedInstance] appDidReceiveMessage:userInfo];
// Handle the received message
// [START_EXCLUDE]
[[NSNotificationCenter defaultCenter] postNotificationName:_messageKey
object:nil
userInfo:userInfo];
// [END_EXCLUDE]
}
- (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:_messageKey
object:nil
userInfo:userInfo];
handler(UIBackgroundFetchResultNoData);
// [END_EXCLUDE]
}
// [END ack_message_reception]
// [START on_token_refresh]
- (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:_gcmSenderID
scope:kGGLInstanceIDScopeGCM
options:_registrationOptions
handler:_registrationHandler];
}
// [END on_token_refresh]
@end
METTRE À JOUR
code php backend pour envoyer un message GCM
//------------------------------
// Payload data you want to send
// to Android device (will be
// accessible via intent extras)
//------------------------------
$msg = addslashes($_POST["msg"]);
//------------------------------
// The recipient registration IDs
// that will receive the Push
// (Should be stored in your DB)
//
// Read about it here:
// http://developer.Android.com/google/gcm/
//------------------------------
//------------------------------
// Call our custom GCM function
//------------------------------
sendGoogleCloudMessage( $msg );
echo "send";
//------------------------------
// Define custom GCM function
//------------------------------
function sendGoogleCloudMessage( $msg )
{
//------------------------------
// Replace with real GCM API
// key from Google APIs Console
//
// https://code.google.com/apis/console/
//------------------------------
$apiKey = 'abc';
//------------------------------
// Define URL to GCM endpoint
//------------------------------
$url = 'https://Android.googleapis.com/gcm/send';
//------------------------------
// Set CURL request headers
// (Authentication and type)
//------------------------------
$headers = array(
'Authorization: key=' . $apiKey,
'Content-Type: application/json'
);
//------------------------------
// Initialize curl handle
//------------------------------
$ch = curl_init();
//------------------------------
// Set URL to GCM endpoint
//------------------------------
curl_setopt( $ch, CURLOPT_URL, $url );
//------------------------------
// Set request method to POST
//------------------------------
curl_setopt( $ch, CURLOPT_POST, true );
//------------------------------
// Set our custom headers
//------------------------------
curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
//------------------------------
// Get the response back as
// string instead of printing it
//------------------------------
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
//------------------------------
// Set post data as JSON
//------------------------------
$post_json_encode = '{"data":{"message":"' . $msg . '"},"to":"/topics/global"}';
curl_setopt( $ch, CURLOPT_POSTFIELDS, $post_json_encode );
//------------------------------
// Actually send the Push!
//------------------------------
$result = curl_exec( $ch );
//------------------------------
// Error? Display it!
//------------------------------
if ( curl_errno( $ch ) )
{
echo 'GCM error: ' . curl_error( $ch );
}
//------------------------------
// Close curl handle
//------------------------------
curl_close( $ch );
//------------------------------
// Debug GCM response
//------------------------------
$arr_result = json_decode($result, true);
foreach ($arr_result as $name => $value) {
echo "<p>".$name .": ". $value ."</p>";
}
}
L'erreur 2001 que vous obtenez n'est PAS kGGLInstanceIDOperationErrorCodeMissingKeyPair
mais plutôt kGCMServiceErrorCodeAlreadyConnected
. Ce dernier signifie que vous êtes déjà connecté à GCM. Pour mieux résoudre ce problème, j’essayerais d’envoyer une notification d’affichage au jeton de périphérique, c’est-à-dire d’envoyer cette
$ post_json_encode = '{"notification": {"body": "'. $ msg. '"}, "to": "/ topics/global"}';
Vous devriez théoriquement vous connecter à GCM lorsque votre application est au premier plan et vous déconnecter lorsque vous passez à l'arrière-plan. Vous pouvez ensuite vous reconnecter lorsque vous arrivez au premier plan.
data payload
et notification payload
sont tous deux applicables sur iOS et Android. Sur iOS, la différence est que la charge de notification est envoyée via APNS, tandis que la charge de données est envoyée via la propre connexion de GCM, qui n'est disponible que lorsque l'application est au premier plan. La charge de notification Android est le nouveau service de notification d'affichage ajouté récemment.
Je faisais face au même problème sur iOS. Ensuite, j'ai trouvé la solution sur le site Web de PushBots. Cela fonctionne bien pour moi maintenant.
Dans XCode, allez à Cibles> Paramètres de construction> Identité de signature de code Et assurez-vous que ce n'est pas automatique et que le certificat de correspondance de profil lié à l'ID de l'application, par exemple