我们需要为 GCM iOS 设置代表吗?
Do we need to set delegates for GCM iOS?
在GCM client guide for iOS, it talks about implementing an onTokenRefresh
method, but it doesn't say what class to implement that on, or how the Google code knows what object to send this message to. I see that an onTokenRefresh
method is specified in the GGLInstanceIDDelegate
protocol, and there is a property of type id<GGLInstanceIDDelegate>
called delegate
in the GGLInstanceIDConfig
class中,但是示例代码中没有设置这个属性,指南也没有讲设置它。示例代码使用 GGLInstanceIDConfig
的唯一地方是将默认配置直接传递给 GGLInstanceID
的 startWithConfig:
而无需设置任何内容:
[[GGLInstanceID sharedInstance] startWithConfig:[GGLInstanceIDConfig defaultConfig]];
需要单独设置吗?
同样,指南中关于发送上游消息的部分讨论了实现 willSendDataMessageWithID
和 didSendDataMessageWithID
方法,但没有说明 class 实现这些方法的内容或方式GCM 知道将它们发送到什么对象。它们在 GCMReceiverDelegate
protocol, and there is a property of type id<GCMReceiverDelegate>
called receiverDelegate
in the GCMConfig
class 中,然而,再一次,属性 没有在示例代码中设置,指南也没有讨论设置它。
需要单独设置吗?
是的,您必须将自定义委托设置为接收回调。这就是我为 GCMReceiverDelegate
所做的
初始配置GCM
,
- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
self.apnsToken = deviceToken;
WEAKIFY(self);
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
STRONGIFY(self);
self.notificationCenter = [[GCMNotificationCenter alloc] init]
// InstanceID
GGLInstanceIDConfig *instanceIDConfig = [GGLInstanceIDConfig defaultConfig];
instanceIDConfig.logLevel = kGGLInstanceIDLevelDebug;
instanceIDConfig.delegate = self.notificationCenter;
[[GGLInstanceID sharedInstance] startWithConfig:instanceIDConfig];
// GCM
GCMConfig *config = [GCMConfig defaultConfig];
config.logLevel = kGCMLogLevelDebug;
config.receiverDelegate = [[GCMReceiver alloc] init];
[[GCMService sharedInstance] startWithConfig:config];
}
}
}
GCMReceiver.m
会有。
#pragma mark - GCM delegate
- (void)willSendDataMessageWithID:(NSString *)messageID error:(NSError *)error {
if (error) {
// Failed to send the message.
} else {
// Will send message
}
}
- (void)didSendDataMessageWithID:(NSString *)messageID {
// Did send message successfully
}
- (void)didDeleteMessagesOnServer {
// Some messages were deleted on the server. Let the server
// know about this to retrieve them.
}
#pragma mark - InstanceID delegate
- (void)onTokenRefresh {
// Fetch all the tokens for all auth-entities and send them
// to the server.
}
请注意,在第一个回调 willSendDataMessageWithID:error:
中,当没有 error
对象时,您不会收到回调,这不是很好,将在下一个版本中由 Google 更正.
您也必须为 GGLInstanceIDDelegate
做同样的事情。
我来晚了一点,但我想说的是,通过浏览 Google 文档并查看它们在 GIT:
https://github.com/googlesamples/google-services/blob/master/ios/gcm/GcmExampleSwift/AppDelegate.swift
碰巧,Google 在 AppDelegate 中实现了大部分功能。
//
// Copyright (c) 2015 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GGLInstanceIDDelegate, GCMReceiverDelegate {
var window: UIWindow?
var connectedToGCM = false
var subscribedToTopic = false
var gcmSenderID: String?
var registrationToken: String?
var registrationOptions = [String: AnyObject]()
let registrationKey = "onRegistrationCompleted"
let messageKey = "onMessageReceived"
let subscriptionTopic = "/topics/global"
// [START register_for_remote_notifications]
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions:
[NSObject: AnyObject]?) -> Bool {
// [START_EXCLUDE]
// Configure the Google context: parses the GoogleService-Info.plist, and initializes
// the services that have entries in the file
var configureError:NSError?
GGLContext.sharedInstance().configureWithError(&configureError)
assert(configureError == nil, "Error configuring Google services: \(configureError)")
gcmSenderID = GGLContext.sharedInstance().configuration.gcmSenderID
// [END_EXCLUDE]
// 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)
}
// [END register_for_remote_notifications]
// [START start_gcm_service]
let gcmConfig = GCMConfig.defaultConfig()
gcmConfig.receiverDelegate = self
GCMService.sharedInstance().startWithConfig(gcmConfig)
// [END start_gcm_service]
return true
}
func subscribeToTopic() {
// If the app has a registration token and is connected to GCM, proceed to subscribe to the
// topic
if(registrationToken != nil && connectedToGCM) {
GCMPubSub.sharedInstance().subscribeWithToken(self.registrationToken, topic: subscriptionTopic,
options: nil, handler: {(error:NSError?) -> Void in
if let error = error {
// Treat the "already subscribed" error more gently
if error.code == 3001 {
print("Already subscribed to \(self.subscriptionTopic)")
} else {
print("Subscription failed: \(error.localizedDescription)");
}
} else {
self.subscribedToTopic = true;
NSLog("Subscribed to \(self.subscriptionTopic)");
}
})
}
}
// [START connect_gcm_service]
func applicationDidBecomeActive( application: UIApplication) {
// Connect to the GCM server to receive non-APNS notifications
GCMService.sharedInstance().connectWithHandler({(error:NSError?) -> Void in
if let error = error {
print("Could not connect to GCM: \(error.localizedDescription)")
} else {
self.connectedToGCM = true
print("Connected to GCM")
// [START_EXCLUDE]
self.subscribeToTopic()
// [END_EXCLUDE]
}
})
}
// [END connect_gcm_service]
// [START disconnect_gcm_service]
func applicationDidEnterBackground(application: UIApplication) {
GCMService.sharedInstance().disconnect()
// [START_EXCLUDE]
self.connectedToGCM = false
// [END_EXCLUDE]
}
// [END disconnect_gcm_service]
// [START receive_apns_token]
func application( application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken
deviceToken: NSData ) {
// [END receive_apns_token]
// [START get_gcm_reg_token]
// Create a config and set a delegate that implements the GGLInstaceIDDelegate protocol.
let instanceIDConfig = GGLInstanceIDConfig.defaultConfig()
instanceIDConfig.delegate = self
// Start the GGLInstanceID shared instance with that config and request a registration
// token to enable reception of notifications
GGLInstanceID.sharedInstance().startWithConfig(instanceIDConfig)
registrationOptions = [kGGLInstanceIDRegisterAPNSOption:deviceToken,
kGGLInstanceIDAPNSServerTypeSandboxOption:true]
GGLInstanceID.sharedInstance().tokenWithAuthorizedEntity(gcmSenderID,
scope: kGGLInstanceIDScopeGCM, options: registrationOptions, handler: registrationHandler)
// [END get_gcm_reg_token]
}
// [START receive_apns_token_error]
func application( application: UIApplication, didFailToRegisterForRemoteNotificationsWithError
error: NSError ) {
print("Registration for remote notification failed with error: \(error.localizedDescription)")
// [END receive_apns_token_error]
let userInfo = ["error": error.localizedDescription]
NSNotificationCenter.defaultCenter().postNotificationName(
registrationKey, object: nil, userInfo: userInfo)
}
// [START ack_message_reception]
func application( application: UIApplication,
didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
print("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]
}
func application( application: UIApplication,
didReceiveRemoteNotification userInfo: [NSObject : AnyObject],
fetchCompletionHandler handler: (UIBackgroundFetchResult) -> Void) {
print("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(UIBackgroundFetchResult.NoData);
// [END_EXCLUDE]
}
// [END ack_message_reception]
func registrationHandler(registrationToken: String!, error: NSError!) {
if (registrationToken != nil) {
self.registrationToken = registrationToken
print("Registration Token: \(registrationToken)")
self.subscribeToTopic()
let userInfo = ["registrationToken": registrationToken]
NSNotificationCenter.defaultCenter().postNotificationName(
self.registrationKey, object: nil, userInfo: userInfo)
} else {
print("Registration to GCM failed with error: \(error.localizedDescription)")
let userInfo = ["error": error.localizedDescription]
NSNotificationCenter.defaultCenter().postNotificationName(
self.registrationKey, object: nil, userInfo: userInfo)
}
}
// [START on_token_refresh]
func onTokenRefresh() {
// A rotation of the registration tokens is happening, so the app needs to request a new token.
print("The GCM registration token needs to be changed.")
GGLInstanceID.sharedInstance().tokenWithAuthorizedEntity(gcmSenderID,
scope: kGGLInstanceIDScopeGCM, options: registrationOptions, handler: registrationHandler)
}
// [END on_token_refresh]
// [START upstream_callbacks]
func willSendDataMessageWithID(messageID: String!, error: NSError!) {
if (error != nil) {
// Failed to send the message.
} else {
// Will send message, you can save the messageID to track the message
}
}
func didSendDataMessageWithID(messageID: String!) {
// Did successfully send message identified by messageID
}
// [END upstream_callbacks]
func didDeleteMessagesOnServer() {
// Some messages sent to this device were deleted on the GCM server before reception, likely
// because the TTL expired. The client should notify the app server of this, so that the app
// server can resend those messages.
}
}
在GCM client guide for iOS, it talks about implementing an onTokenRefresh
method, but it doesn't say what class to implement that on, or how the Google code knows what object to send this message to. I see that an onTokenRefresh
method is specified in the GGLInstanceIDDelegate
protocol, and there is a property of type id<GGLInstanceIDDelegate>
called delegate
in the GGLInstanceIDConfig
class中,但是示例代码中没有设置这个属性,指南也没有讲设置它。示例代码使用 GGLInstanceIDConfig
的唯一地方是将默认配置直接传递给 GGLInstanceID
的 startWithConfig:
而无需设置任何内容:
[[GGLInstanceID sharedInstance] startWithConfig:[GGLInstanceIDConfig defaultConfig]];
需要单独设置吗?
同样,指南中关于发送上游消息的部分讨论了实现 willSendDataMessageWithID
和 didSendDataMessageWithID
方法,但没有说明 class 实现这些方法的内容或方式GCM 知道将它们发送到什么对象。它们在 GCMReceiverDelegate
protocol, and there is a property of type id<GCMReceiverDelegate>
called receiverDelegate
in the GCMConfig
class 中,然而,再一次,属性 没有在示例代码中设置,指南也没有讨论设置它。
需要单独设置吗?
是的,您必须将自定义委托设置为接收回调。这就是我为 GCMReceiverDelegate
初始配置GCM
,
- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
self.apnsToken = deviceToken;
WEAKIFY(self);
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
STRONGIFY(self);
self.notificationCenter = [[GCMNotificationCenter alloc] init]
// InstanceID
GGLInstanceIDConfig *instanceIDConfig = [GGLInstanceIDConfig defaultConfig];
instanceIDConfig.logLevel = kGGLInstanceIDLevelDebug;
instanceIDConfig.delegate = self.notificationCenter;
[[GGLInstanceID sharedInstance] startWithConfig:instanceIDConfig];
// GCM
GCMConfig *config = [GCMConfig defaultConfig];
config.logLevel = kGCMLogLevelDebug;
config.receiverDelegate = [[GCMReceiver alloc] init];
[[GCMService sharedInstance] startWithConfig:config];
}
}
}
GCMReceiver.m
会有。
#pragma mark - GCM delegate
- (void)willSendDataMessageWithID:(NSString *)messageID error:(NSError *)error {
if (error) {
// Failed to send the message.
} else {
// Will send message
}
}
- (void)didSendDataMessageWithID:(NSString *)messageID {
// Did send message successfully
}
- (void)didDeleteMessagesOnServer {
// Some messages were deleted on the server. Let the server
// know about this to retrieve them.
}
#pragma mark - InstanceID delegate
- (void)onTokenRefresh {
// Fetch all the tokens for all auth-entities and send them
// to the server.
}
请注意,在第一个回调 willSendDataMessageWithID:error:
中,当没有 error
对象时,您不会收到回调,这不是很好,将在下一个版本中由 Google 更正.
您也必须为 GGLInstanceIDDelegate
做同样的事情。
我来晚了一点,但我想说的是,通过浏览 Google 文档并查看它们在 GIT: https://github.com/googlesamples/google-services/blob/master/ios/gcm/GcmExampleSwift/AppDelegate.swift
碰巧,Google 在 AppDelegate 中实现了大部分功能。
//
// Copyright (c) 2015 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GGLInstanceIDDelegate, GCMReceiverDelegate {
var window: UIWindow?
var connectedToGCM = false
var subscribedToTopic = false
var gcmSenderID: String?
var registrationToken: String?
var registrationOptions = [String: AnyObject]()
let registrationKey = "onRegistrationCompleted"
let messageKey = "onMessageReceived"
let subscriptionTopic = "/topics/global"
// [START register_for_remote_notifications]
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions:
[NSObject: AnyObject]?) -> Bool {
// [START_EXCLUDE]
// Configure the Google context: parses the GoogleService-Info.plist, and initializes
// the services that have entries in the file
var configureError:NSError?
GGLContext.sharedInstance().configureWithError(&configureError)
assert(configureError == nil, "Error configuring Google services: \(configureError)")
gcmSenderID = GGLContext.sharedInstance().configuration.gcmSenderID
// [END_EXCLUDE]
// 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)
}
// [END register_for_remote_notifications]
// [START start_gcm_service]
let gcmConfig = GCMConfig.defaultConfig()
gcmConfig.receiverDelegate = self
GCMService.sharedInstance().startWithConfig(gcmConfig)
// [END start_gcm_service]
return true
}
func subscribeToTopic() {
// If the app has a registration token and is connected to GCM, proceed to subscribe to the
// topic
if(registrationToken != nil && connectedToGCM) {
GCMPubSub.sharedInstance().subscribeWithToken(self.registrationToken, topic: subscriptionTopic,
options: nil, handler: {(error:NSError?) -> Void in
if let error = error {
// Treat the "already subscribed" error more gently
if error.code == 3001 {
print("Already subscribed to \(self.subscriptionTopic)")
} else {
print("Subscription failed: \(error.localizedDescription)");
}
} else {
self.subscribedToTopic = true;
NSLog("Subscribed to \(self.subscriptionTopic)");
}
})
}
}
// [START connect_gcm_service]
func applicationDidBecomeActive( application: UIApplication) {
// Connect to the GCM server to receive non-APNS notifications
GCMService.sharedInstance().connectWithHandler({(error:NSError?) -> Void in
if let error = error {
print("Could not connect to GCM: \(error.localizedDescription)")
} else {
self.connectedToGCM = true
print("Connected to GCM")
// [START_EXCLUDE]
self.subscribeToTopic()
// [END_EXCLUDE]
}
})
}
// [END connect_gcm_service]
// [START disconnect_gcm_service]
func applicationDidEnterBackground(application: UIApplication) {
GCMService.sharedInstance().disconnect()
// [START_EXCLUDE]
self.connectedToGCM = false
// [END_EXCLUDE]
}
// [END disconnect_gcm_service]
// [START receive_apns_token]
func application( application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken
deviceToken: NSData ) {
// [END receive_apns_token]
// [START get_gcm_reg_token]
// Create a config and set a delegate that implements the GGLInstaceIDDelegate protocol.
let instanceIDConfig = GGLInstanceIDConfig.defaultConfig()
instanceIDConfig.delegate = self
// Start the GGLInstanceID shared instance with that config and request a registration
// token to enable reception of notifications
GGLInstanceID.sharedInstance().startWithConfig(instanceIDConfig)
registrationOptions = [kGGLInstanceIDRegisterAPNSOption:deviceToken,
kGGLInstanceIDAPNSServerTypeSandboxOption:true]
GGLInstanceID.sharedInstance().tokenWithAuthorizedEntity(gcmSenderID,
scope: kGGLInstanceIDScopeGCM, options: registrationOptions, handler: registrationHandler)
// [END get_gcm_reg_token]
}
// [START receive_apns_token_error]
func application( application: UIApplication, didFailToRegisterForRemoteNotificationsWithError
error: NSError ) {
print("Registration for remote notification failed with error: \(error.localizedDescription)")
// [END receive_apns_token_error]
let userInfo = ["error": error.localizedDescription]
NSNotificationCenter.defaultCenter().postNotificationName(
registrationKey, object: nil, userInfo: userInfo)
}
// [START ack_message_reception]
func application( application: UIApplication,
didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
print("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]
}
func application( application: UIApplication,
didReceiveRemoteNotification userInfo: [NSObject : AnyObject],
fetchCompletionHandler handler: (UIBackgroundFetchResult) -> Void) {
print("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(UIBackgroundFetchResult.NoData);
// [END_EXCLUDE]
}
// [END ack_message_reception]
func registrationHandler(registrationToken: String!, error: NSError!) {
if (registrationToken != nil) {
self.registrationToken = registrationToken
print("Registration Token: \(registrationToken)")
self.subscribeToTopic()
let userInfo = ["registrationToken": registrationToken]
NSNotificationCenter.defaultCenter().postNotificationName(
self.registrationKey, object: nil, userInfo: userInfo)
} else {
print("Registration to GCM failed with error: \(error.localizedDescription)")
let userInfo = ["error": error.localizedDescription]
NSNotificationCenter.defaultCenter().postNotificationName(
self.registrationKey, object: nil, userInfo: userInfo)
}
}
// [START on_token_refresh]
func onTokenRefresh() {
// A rotation of the registration tokens is happening, so the app needs to request a new token.
print("The GCM registration token needs to be changed.")
GGLInstanceID.sharedInstance().tokenWithAuthorizedEntity(gcmSenderID,
scope: kGGLInstanceIDScopeGCM, options: registrationOptions, handler: registrationHandler)
}
// [END on_token_refresh]
// [START upstream_callbacks]
func willSendDataMessageWithID(messageID: String!, error: NSError!) {
if (error != nil) {
// Failed to send the message.
} else {
// Will send message, you can save the messageID to track the message
}
}
func didSendDataMessageWithID(messageID: String!) {
// Did successfully send message identified by messageID
}
// [END upstream_callbacks]
func didDeleteMessagesOnServer() {
// Some messages sent to this device were deleted on the GCM server before reception, likely
// because the TTL expired. The client should notify the app server of this, so that the app
// server can resend those messages.
}
}