SWIFT: I don't get notifications from my CloudKit app? - ios

Here is my code:
#IBAction func sendSweet(sender: AnyObject) {
//delegate method
let newSweet = CKRecord(recordType: "Extra1")
newSweet["content"] = textField.text
let publicData = CKContainer.defaultContainer().publicCloudDatabase
publicData.saveRecord(newSweet, completionHandler: { (record:CKRecord?, error:NSError?) -> Void in
if error == nil {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.beginUpdates()
self.sweets.insert(newSweet, atIndex: 0)
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Top)
self.tableView.endUpdates()
self.textField.text = ""
self.textField.resignFirstResponder()
})
}})
// Put the CloudKit private database in a constants
let privateDatabase = CKContainer.defaultContainer().publicCloudDatabase
// Create subscription and set three of its properties (an id, a predicate, and options)
let friendsSubscription = CKSubscription(recordType: "Extra1",
predicate: NSPredicate(format: "TRUEPREDICATE"),
subscriptionID: "Extra1",
options: .FiresOnRecordCreation)
// Create a notification and set two of its properties (alertBody and shouldBadge)
let notificationInfo = CKNotificationInfo()
notificationInfo.alertBody = "New message in Lms Chat"
notificationInfo.shouldBadge = false
// Attach the notification to the subscription
friendsSubscription.notificationInfo = notificationInfo
// Save the subscription in the private database
privateDatabase.saveSubscription(friendsSubscription) {recordReturned, error in
// On the main thread, display an error/success message in the textView
if error != nil {
NSOperationQueue.mainQueue().addOperationWithBlock {
self.textField.text = "Cloud error\n\(error!.localizedDescription)"
}
} else {
NSOperationQueue.mainQueue().addOperationWithBlock {
self.textField.text = ""
}
}
}
}
func textFieldShouldEndEditing(textField: UITextField!) -> Bool { //delegate method
return false
}
func textFieldShouldReturn(textField: UITextField!) -> Bool { //delegate method
textField.resignFirstResponder()
return true
}
It's a messaging app so people can message eachother but I also want them to recieve notifications. This is the code for notifications and I also have some code for notifications in App Delegate:
import UIKit
import CloudKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let notificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)
UIApplication.sharedApplication().registerForRemoteNotifications()
return true
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
let cloudKitNotification = CKNotification(fromRemoteNotificationDictionary: userInfo as! [String:NSObject])
if cloudKitNotification.notificationType == CKNotificationType.Query {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
NSNotificationCenter.defaultCenter().postNotificationName("performReload", object: nil)
})
}
}
func resetBadge () {
let badgeReset = CKModifyBadgeOperation(badgeValue: 0)
badgeReset.modifyBadgeCompletionBlock = { (error) -> Void in
if error == nil {
UIApplication.sharedApplication().applicationIconBadgeNumber = 0
}
}
CKContainer.defaultContainer().addOperation(badgeReset)
}
func applicationWillResignActive(application: UIApplication) {
// 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.
}
func applicationDidEnterBackground(application: UIApplication) {
resetBadge()
}
func applicationWillEnterForeground(application: UIApplication) {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
NSNotificationCenter.defaultCenter().postNotificationName("performReload", object: nil)
})
}
func applicationDidBecomeActive(application: UIApplication) {
resetBadge()
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
But notifications do not come in even though I have all of the code needed to make notifications every time someone sends a message. Am I missing something? Thanks!
Someone answered me before saying:
You are adding data to the public database but you are creating your subscription on the private database. One of those two needs to be changed to match the other.
So I changed the let privateDatabase = CKContainer.defaultContainer().privateCloudDatabase to let privateDatabase = CKContainer.defaultContainer().publicCloudDatabase

In your app Delegate, you are missing this method :
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
print(deviceToken) // This is the device token
var token: String = "\(deviceToken)" /// formatted token.
let rawtoken = token.stringByReplacingOccurrencesOfString(">", withString: "")
let cleantoken = rawtoken.stringByReplacingOccurrencesOfString("<", withString: "")
var finaltoken = cleantoken.stringByReplacingOccurrencesOfString(" ", withString: "")
// Send device token to server
}
}
}
WHat I mean is that to send notification, you register user for push Notification. For the case If the register succeeds, you have to implement the method above.
if the registeration fails, you have to implement this :
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
print(error)
}

Related

IOS Swift Pushkit: didUpdatePushCredentials not called

I am trying to develop a VoIP app using Twilio Client iOS SDK. My app receives incoming calls when its not in background. For background mode I am trying to use PushKit Framework as Apple suggests. But my app is not getting registered for PushKit. The method didUpdatePushCredentials is not getting called.
This is my app delegate and settings:
import UIKit
import PushKit;
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var voipRegistry: PKPushRegistry!;
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
if UIApplication.instancesRespondToSelector(#selector(UIApplication.registerUserNotificationSettings(_:))) {
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: [UIUserNotificationType.Alert, UIUserNotificationType.Badge, UIUserNotificationType.Sound], categories: nil))
}
let preferences = NSUserDefaults.standardUserDefaults()
let usrIdKey = "usrId"
let tokenKey = "token"
if preferences.objectForKey(usrIdKey) == nil || preferences.objectForKey(tokenKey) == nil{
// Doesn't exist
} else {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
var nav1 = UINavigationController()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewControllerObj = storyboard.instantiateViewControllerWithIdentifier("NumberScreenViewController") as? NumberScreenViewController
nav1.viewControllers = [viewControllerObj!]
self.window!.rootViewController = nav1
self.window?.makeKeyAndVisible()
}
phone.login{
device in
}
var state:String
switch application.applicationState {
case .Active:
state = "Active"
case .Background:
state = "Background"
case .Inactive:
state = "Active"
}
NSLog("App launched with state \(state)")
return true
}
func applicationWillResignActive(application: UIApplication) {
// 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.
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state informationO 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.
}
func applicationWillEnterForeground(application: UIApplication) {
// 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.
}
func applicationDidBecomeActive(application: UIApplication) {
// 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.
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
//output to see when we terminate the app
NSLog("app terminated")
}
func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
NSLog("Registering for VOIP notifications.")
//register for voip notifications
voipRegistry = PKPushRegistry(queue: dispatch_get_main_queue())
voipRegistry.desiredPushTypes = Set([PKPushTypeVoIP])
voipRegistry.delegate = self;
}
func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification)
{
if ( application.applicationState == UIApplicationState.Active)
{
print("Active")
// App is foreground and notification is recieved,
// Show a alert.
}
else if( application.applicationState == UIApplicationState.Background)
{
print("Background")
// App is in background and notification is received,
// You can fetch required data here don't do anything with UI.
self.redirectToPage(notification.userInfo)
}
else if( application.applicationState == UIApplicationState.Inactive)
{
print("Inactive")
// App came in foreground by used clicking on notification,
// Use userinfo for redirecting to specific view controller.
self.redirectToPage(notification.userInfo)
}
}
func redirectToPage(userInfo:[NSObject : AnyObject]!)
{
var viewControllerToBrRedirectedTo:DialScreenViewController = DialScreenViewController(nibName: "DialScreenViewController", bundle: nil)
if userInfo != nil
{
if let pageType = userInfo["TYPE"]
{
if pageType as! String == "Page1"
{
// viewControllerToBrRedirectedTo = UIViewController() // creater specific view controller
}
}
}
if self.window != nil && self.window?.rootViewController != nil
{
let rootVC = self.window?.rootViewController!
if rootVC is UINavigationController
{
(rootVC as! UINavigationController).pushViewController(viewControllerToBrRedirectedTo, animated: true)
}
else
{
rootVC?.presentViewController(viewControllerToBrRedirectedTo, animated: true, completion: { () -> Void in
})
}
}
}
}
extension AppDelegate: PKPushRegistryDelegate {
func pushRegistry(registry: PKPushRegistry!, didUpdatePushCredentials credentials: PKPushCredentials!, forType type: String!) {
//print out the VoIP token. We will use this to test the nofications.
NSLog("voip token: \(credentials.token)")
}
func pushRegistry(registry: PKPushRegistry!, didReceiveIncomingPushWithPayload payload: PKPushPayload!, forType type: String!) {
let payloadDict = payload.dictionaryPayload["aps"] as? Dictionary<String, String>
let message = payloadDict?["alert"]
//present a local notifcation to visually see when we are recieving a VoIP Notification
if UIApplication.sharedApplication().applicationState == UIApplicationState.Background {
let localNotification = UILocalNotification();
localNotification.alertBody = message
localNotification.applicationIconBadgeNumber = 1;
localNotification.soundName = UILocalNotificationDefaultSoundName;
UIApplication.sharedApplication().presentLocalNotificationNow(localNotification);
}
else {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let alert = UIAlertView(title: "VoIP Notification", message: message, delegate: nil, cancelButtonTitle: "Ok");
alert.show()
})
}
NSLog("incoming voip notfication: \(payload.dictionaryPayload)")
}
func pushRegistry(registry: PKPushRegistry!, didInvalidatePushTokenForType type: String!) {
NSLog("token invalidated")
}
}
I have same problem. However it was recovered after rebooting the phone.
Go through https://www.raywenderlich.com/123862/push-notifications-tutorial
Download
import UIKit
import PushKit
class AppDelegate: UIResponder, UIApplicationDelegate,PKPushRegistryDelegate{
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let types: UIRemoteNotificationType = [.Alert, .Badge, .Sound]
application.registerForRemoteNotificationTypes(types)
self. PushKitRegistration()
return true
}
//MARK: - PushKitRegistration
func PushKitRegistration()
{
let mainQueue = dispatch_get_main_queue()
// Create a push registry object
if #available(iOS 8.0, *) {
let voipRegistry: PKPushRegistry = PKPushRegistry(queue: mainQueue)
// Set the registry's delegate to self
voipRegistry.delegate = self
// Set the push type to VoIP
voipRegistry.desiredPushTypes = [PKPushTypeVoIP]
} else {
// Fallback on earlier versions
}
}
#available(iOS 8.0, *)
func pushRegistry(registry: PKPushRegistry!, didUpdatePushCredentials credentials: PKPushCredentials!, forType type: String!) {
// Register VoIP push token (a property of PKPushCredentials) with server
let hexString : String = UnsafeBufferPointer<UInt8>(start: UnsafePointer(credentials.token.bytes),
count: credentials.token.length).map { String(format: "%02x", $0) }.joinWithSeparator("")
print(hexString)
}
#available(iOS 8.0, *)
func pushRegistry(registry: PKPushRegistry!, didReceiveIncomingPushWithPayload payload: PKPushPayload!, forType type: String!) {
// Process the received push
}
}
Life cycle of app - when app is in terminated and push kit payload comes

SWIFT: I dont get notifications from my CloudKit app?

I asked this question and people have answered but nothing ever works. This is very important to me that I can get this to work. Here is some of my code:
#IBAction func sendSweet(sender: AnyObject) {
//delegate method
let newSweet = CKRecord(recordType: "Extra1")
newSweet["content"] = textField.text
let publicData = CKContainer.defaultContainer().publicCloudDatabase
publicData.saveRecord(newSweet, completionHandler: { (record:CKRecord?, error:NSError?) -> Void in
if error == nil {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.beginUpdates()
self.sweets.insert(newSweet, atIndex: 0)
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Top)
self.tableView.endUpdates()
self.textField.text = ""
self.textField.resignFirstResponder()
})
}})
// Put the CloudKit private database in a constants
let privateDatabase = CKContainer.defaultContainer().publicCloudDatabase
// Create subscription and set three of its properties (an id, a predicate, and options)
let friendsSubscription = CKSubscription(recordType: "Extra1",
predicate: NSPredicate(format: "TRUEPREDICATE"),
subscriptionID: "Extra1",
options: .FiresOnRecordCreation)
// Create a notification and set two of its properties (alertBody and shouldBadge)
let notificationInfo = CKNotificationInfo()
notificationInfo.alertBody = "New message in Lms Chat"
notificationInfo.shouldBadge = false
// Attach the notification to the subscription
friendsSubscription.notificationInfo = notificationInfo
// Save the subscription in the private database
privateDatabase.saveSubscription(friendsSubscription) {recordReturned, error in
// On the main thread, display an error/success message in the textView
if error != nil {
NSOperationQueue.mainQueue().addOperationWithBlock {
self.textField.text = "Cloud error\n\(error!.localizedDescription)"
}
} else {
NSOperationQueue.mainQueue().addOperationWithBlock {
self.textField.text = ""
}
}
}
}
func textFieldShouldEndEditing(textField: UITextField!) -> Bool { //delegate method
return false
}
func textFieldShouldReturn(textField: UITextField!) -> Bool { //delegate method
textField.resignFirstResponder()
return true
}
It's a messaging app so people can message eachother but I also want them to recieve notifications. This is the code for notifications and I also have some code for notifications in App Delegate:
import UIKit
import CloudKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let notificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)
UIApplication.sharedApplication().registerForRemoteNotifications()
return true
}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
print(deviceToken) // This is the device token
var token: String = "\(deviceToken)" /// formatted token.
// Following lines will convert token from NSData to String.
let rawtoken = token.stringByReplacingOccurrencesOfString(">", withString: "")
let cleantoken = rawtoken.stringByReplacingOccurrencesOfString("<", withString: "")
var finaltoken = cleantoken.stringByReplacingOccurrencesOfString(" ", withString: "")
// Send device token to server
}
}
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
print(error)
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
let cloudKitNotification = CKNotification(fromRemoteNotificationDictionary: userInfo as! [String:NSObject])
if cloudKitNotification.notificationType == CKNotificationType.Query {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
NSNotificationCenter.defaultCenter().postNotificationName("performReload", object: nil)
})
}
}
func resetBadge () {
let badgeReset = CKModifyBadgeOperation(badgeValue: 0)
badgeReset.modifyBadgeCompletionBlock = { (error) -> Void in
if error == nil {
UIApplication.sharedApplication().applicationIconBadgeNumber = 0
}
}
CKContainer.defaultContainer().addOperation(badgeReset)
}
func applicationWillResignActive(application: UIApplication) {
// 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.
}
func applicationDidEnterBackground(application: UIApplication) {
resetBadge()
}
func applicationWillEnterForeground(application: UIApplication) {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
NSNotificationCenter.defaultCenter().postNotificationName("performReload", object: nil)
})
}
func applicationDidBecomeActive(application: UIApplication) {
resetBadge()
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
But notifications do not come in even though I have all of the code needed to make notifications every time someone sends a message. Am I missing something? Thanks!
Someone answered me before saying:
You are adding data to the public database but you are creating your subscription on the private database. One of those two needs to be changed to match the other.
So I changed the let privateDatabase = CKContainer.defaultContainer().privateCloudDatabase to let privateDatabase = CKContainer.defaultContainer().publicCloudDatabase
Someone also told me I was missing a function:
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
print(deviceToken) // This is the device token
var token: String = "\(deviceToken)" /// formatted token.
// Following lines will convert token from NSData to String.
let rawtoken = token.stringByReplacingOccurrencesOfString(">", withString: "")
let cleantoken = rawtoken.stringByReplacingOccurrencesOfString("<", withString: "")
var finaltoken = cleantoken.stringByReplacingOccurrencesOfString(" ", withString: "")
// Send device token to server
}
}
}
They said: "What I mean is that to send notification, you register user for push Notification. For the case If the register succeeds, you have to implement the method above. if the registeration fails, you have to implement this :"
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
print(error)
}
I added this and it still doesn't work (Notifications still do not come up.) Any other solution? Thanks!

SWIFT: Why do I not get notifications from my CloudKit app?

Here is my code:
#IBAction func sendSweet(sender: AnyObject) {
//delegate method
let newSweet = CKRecord(recordType: "Extra1")
newSweet["content"] = textField.text
let publicData = CKContainer.defaultContainer().publicCloudDatabase
publicData.saveRecord(newSweet, completionHandler: { (record:CKRecord?, error:NSError?) -> Void in
if error == nil {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.beginUpdates()
self.sweets.insert(newSweet, atIndex: 0)
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Top)
self.tableView.endUpdates()
self.textField.text = ""
self.textField.resignFirstResponder()
})
}})
// Put the CloudKit private database in a constants
let privateDatabase = CKContainer.defaultContainer().privateCloudDatabase
// Create subscription and set three of its properties (an id, a predicate, and options)
let friendsSubscription = CKSubscription(recordType: "Extra1",
predicate: NSPredicate(format: "TRUEPREDICATE"),
subscriptionID: "Extra1",
options: .FiresOnRecordCreation)
// Create a notification and set two of its properties (alertBody and shouldBadge)
let notificationInfo = CKNotificationInfo()
notificationInfo.alertBody = "New message in Lms Chat"
notificationInfo.shouldBadge = false
// Attach the notification to the subscription
friendsSubscription.notificationInfo = notificationInfo
// Save the subscription in the private database
privateDatabase.saveSubscription(friendsSubscription) {recordReturned, error in
// On the main thread, display an error/success message in the textView
if error != nil {
NSOperationQueue.mainQueue().addOperationWithBlock {
self.textField.text = "Cloud error\n\(error!.localizedDescription)"
}
} else {
NSOperationQueue.mainQueue().addOperationWithBlock {
self.textField.text = ""
}
}
}
}
func textFieldShouldEndEditing(textField: UITextField!) -> Bool { //delegate method
return false
}
func textFieldShouldReturn(textField: UITextField!) -> Bool { //delegate method
textField.resignFirstResponder()
return true
}
It's a messaging app so people can message eachother but I also want them to recieve notifications. This is the code for notifications and I also have some code for notifications in App Delegate:
import UIKit
import CloudKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let notificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)
UIApplication.sharedApplication().registerForRemoteNotifications()
return true
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
let cloudKitNotification = CKNotification(fromRemoteNotificationDictionary: userInfo as! [String:NSObject])
if cloudKitNotification.notificationType == CKNotificationType.Query {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
NSNotificationCenter.defaultCenter().postNotificationName("performReload", object: nil)
})
}
}
func resetBadge () {
let badgeReset = CKModifyBadgeOperation(badgeValue: 0)
badgeReset.modifyBadgeCompletionBlock = { (error) -> Void in
if error == nil {
UIApplication.sharedApplication().applicationIconBadgeNumber = 0
}
}
CKContainer.defaultContainer().addOperation(badgeReset)
}
func applicationWillResignActive(application: UIApplication) {
// 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.
}
func applicationDidEnterBackground(application: UIApplication) {
resetBadge()
}
func applicationWillEnterForeground(application: UIApplication) {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
NSNotificationCenter.defaultCenter().postNotificationName("performReload", object: nil)
})
}
func applicationDidBecomeActive(application: UIApplication) {
resetBadge()
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
But notifications do not come in even though I have all of the code needed to make notifications every time someone sends a message. Am I missing something? Thanks!
You are adding data to the public database but you are creating your subscription on the private database. One of those two needs to be changed to match the other.

GCM group notifications not received on iOS when app is in background

I'm using google cloud messaging to send notifications to my iOS application.
I've configured the GCM services in my App and the notifications are working correctly. I mean that my device can request a gcmRegistrationToken and I can send notification using postman to my app. These notifications are working both when app is active and inactive.
Now I want to use the Device Groups Notifications feature. So I created a group for all my user's device:
https://android.googleapis.com/gcm/notification
Content-Type:application/json
Authorization:key=API_KEY
project_id:SENDER_ID
{
"operation": "create",
"notification_key_name": "appUser-userA",
"registration_ids": ["regToken1", "regToken2"]
}
I receive my device group key:
{
"notification_key": "deviceGroupKey"
}
But no when I want to send notification to the device group using postman, I only receive the notification when the app is ACTIVE.
Here is my POST request:
https://android.googleapis.com/gcm/send
Content-Type:application/json
Authorization:key=API_KEY
{
"to" : "deviceGroupToken",
"content_available" : true,
"priority": "high",
"sound": "default",
"notification" : {
"body_loc_key" : "NOTIF_FRIEND_REQUEST",
"body_loc_args" : ["UserB"],
"click_action": "NEW_FRIEND_REQUEST"
},
"data" : {
"friendRequestId": "12345678901234567890"
}
}
Here is my AppDelegate.swift:
//
// AppDelegate.swift
//
import UIKit
import CoreData
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GGLInstanceIDDelegate, GCMReceiverDelegate {
var window: UIWindow?
var connectedToGCM = false
var gcmSenderID: String?
var gcmRegistrationToken: String?
var gcmRegistrationOptions = [String: AnyObject]()
let gcmRegistrationKey = "onRegistrationCompleted"
var subscribedToTopic = false
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
print("bundleId=\(NSBundle.mainBundle().bundleIdentifier)")
// Configure Google Analytics
// Configure tracker from GoogleService-Info.plist.
var configureError:NSError?
GGLContext.sharedInstance().configureWithError(&configureError)
assert(configureError == nil, "Error configuring Google services: \(configureError)")
// Optional: configure GAI options.
let gai = GAI.sharedInstance()
gai.trackUncaughtExceptions = true // report uncaught exceptions
gai.logger.logLevel = GAILogLevel.Verbose // remove before app release
// Override point for customization after application launch.
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)) // types are UIUserNotificationType members
// Register for remotes notifications
UIApplication.sharedApplication().registerForRemoteNotifications()
// Get the gcm sender id
gcmSenderID = GGLContext.sharedInstance().configuration.gcmSenderID
var gcmConfig = GCMConfig.defaultConfig()
gcmConfig.receiverDelegate = self
GCMService.sharedInstance().startWithConfig(gcmConfig)
return true
}
func applicationWillResignActive(application: UIApplication) {
// 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.
}
func applicationDidEnterBackground(application: UIApplication) {
// 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()
connectedToGCM = false
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
// 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.
// -->The app go through this point
// Connect to the GCM server to receive non-APNS notifications
GCMService.sharedInstance().connectWithHandler(gcmConnectionHandler)
// -->The app go through this point
}
func gcmConnectionHandler(error: NSError?) {
// -->The app never enter in this function
if let error = error {
print("Could not connect to GCM: \(error.localizedDescription)")
} else {
self.connectedToGCM = true
print("Connected to GCM")
// ...
}
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
self.saveContext()
}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
// 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)
gcmRegistrationOptions = [kGGLInstanceIDRegisterAPNSOption:deviceToken,
kGGLInstanceIDAPNSServerTypeSandboxOption:true]
GGLInstanceID.sharedInstance().tokenWithAuthorizedEntity(gcmSenderID,
scope: kGGLInstanceIDScopeGCM, options: gcmRegistrationOptions, handler: gcmRegistrationHandler)
}
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
print("-- Failed to get deviceToken: \(error.localizedDescription)")
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
application.applicationIconBadgeNumber += 1
print(userInfo)
let apsInfo = userInfo["aps"] as! NSDictionary
var alertMessage = ""
print("********************** Received Notif")
if let alert = apsInfo["alert"] as? String{
alertMessage = alert
print(alertMessage)
}
else if let alert = apsInfo["alert"] as? NSDictionary {
if let body = alert["body"] as? String {
alertMessage = body
print(alertMessage)
}
else if let locKey = alert["loc-key"] as? String {
var alertBody = ""
if let locArgs = alert["loc-args"] as? [String] {
var safeArgs = ["", "", "", "", "", "", "", "", "", ""]
for var i = 0; i < locArgs.count && i < safeArgs.count; i++ {
safeArgs[i] = locArgs[i]
}
alertBody = String(format: NSLocalizedString(locKey, comment: ""),
safeArgs[0], safeArgs[1],
safeArgs[2], safeArgs[3],
safeArgs[4], safeArgs[5],
safeArgs[6], safeArgs[7],
safeArgs[8], safeArgs[9])
}
else {
alertBody = NSLocalizedString(locKey, comment: "")
}
alertMessage = alertBody
}
}
// If the application is currently on screen "Active" then we trigger a custom banner View for that notification to be shown
// Else the system will handle that and put it in the notification center
if application.applicationState == UIApplicationState.Active {
AGPushNoteView.showWithNotificationMessage(alertMessage, autoClose: true, completion: { () -> Void in
// Do nothing
})
}
else {
// if the app is inactive I try to trigger a local notification to check if the notification is received but not displayed
// or just not received because consol print doesn't work when app is inactive
var localNotification: UILocalNotification = UILocalNotification()
localNotification.alertBody = alertMessage
localNotification.fireDate = NSDate(timeIntervalSinceNow: 1)
UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
}
}
func gcmRegistrationHandler(registrationToken: String!, error: NSError!) {
if (registrationToken != nil) {
self.gcmRegistrationToken = registrationToken
print("GCM Registration Token: \(registrationToken)")
let userInfo = ["registrationToken": registrationToken]
NSNotificationCenter.defaultCenter().postNotificationName(
self.gcmRegistrationKey, object: nil, userInfo: userInfo)
} else {
print("Registration to GCM failed with error: \(error.localizedDescription)")
let userInfo = ["error": error.localizedDescription]
NSNotificationCenter.defaultCenter().postNotificationName(self.gcmRegistrationKey, object: nil, userInfo: userInfo)
}
}
// MARK: - GGLInstanceIDDelegate
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: gcmRegistrationOptions, handler: gcmRegistrationHandler)
}
// MARK: - GCMReceiverDelegate
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.
}
func subscribeToTopic() {
// If the app has a registration token and is connected to GCM, proceed to subscribe to the
// topic
let subscriptionTopic = "/topics/test-global"
if(gcmRegistrationToken != nil && connectedToGCM) {
GCMPubSub.sharedInstance().subscribeWithToken(gcmRegistrationToken, topic: subscriptionTopic,
options: nil, handler: {(NSError error) -> Void in
if (error != nil) {
// Treat the "already subscribed" error more gently
if error.code == 3001 {
print("Already subscribed to \(subscriptionTopic)")
} else {
print("Subscription failed: \(error.localizedDescription)");
}
} else {
subscribedToTopic = true;
NSLog("Subscribed to \(subscriptionTopic)");
}
})
}
}
}
Do I have forgotten something in my code? or maybe it is not possible?
Any help would be appreciated.
PS: I tryed to send notification to topics "/topics/general" and it workss fine.

GCM for iOS, gcm connection handler is never called

I'm implementing Google Cloud Messaging in my application. I've followed the tutorial given in google documentations. I can send notifications to my device with HTTP POST request, but the problem is that in the applicationDidBecomeActive, as google showed, I try to connect with gcmService but the connectionHandler block is never called.
applicationDidBecomeActive function in my AppDelegate
func applicationDidBecomeActive(application: UIApplication) {
GCMService.sharedInstance().connectWithHandler({
(NSError error) -> Void in
if error != nil {
print("Could not connect to GCM: \(error.localizedDescription)")
} else {
self.connectedToGCM = true
print("Connected to GCM")
self.subscribeToTopic()
}
})
}
Does any one have solved this issue?
EDIT - This is the correct way
Here is my complete AppDelegate.swift file
//
// AppDelegate.swift
//
import UIKit
import CoreData
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GGLInstanceIDDelegate, GCMReceiverDelegate {
var window: UIWindow?
var connectedToGCM = false
var gcmSenderID: String?
var gcmRegistrationToken: String?
var gcmRegistrationOptions = [String: AnyObject]()
let gcmRegistrationKey = "onRegistrationCompleted"
var subscribedToTopic = false
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
print("bundleId=\(NSBundle.mainBundle().bundleIdentifier)")
// Configure Google Analytics
// Configure tracker from GoogleService-Info.plist.
var configureError:NSError?
GGLContext.sharedInstance().configureWithError(&configureError)
assert(configureError == nil, "Error configuring Google services: \(configureError)")
// Optional: configure GAI options.
let gai = GAI.sharedInstance()
gai.trackUncaughtExceptions = true // report uncaught exceptions
gai.logger.logLevel = GAILogLevel.Verbose // remove before app release
// Override point for customization after application launch.
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)) // types are UIUserNotificationType members
// Register for remotes notifications
UIApplication.sharedApplication().registerForRemoteNotifications()
// Get the gcm sender id
gcmSenderID = GGLContext.sharedInstance().configuration.gcmSenderID
var gcmConfig = GCMConfig.defaultConfig()
gcmConfig.receiverDelegate = self
GCMService.sharedInstance().startWithConfig(gcmConfig)
return true
}
func applicationWillResignActive(application: UIApplication) {
// 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.
}
func applicationDidEnterBackground(application: UIApplication) {
// 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()
connectedToGCM = false
}
func applicationWillEnterForeground(application: UIApplication) {
// 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.
}
func applicationDidBecomeActive(application: UIApplication) {
// 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.
// -->The app go through this point
// Connect to the GCM server to receive non-APNS notifications
GCMService.sharedInstance().connectWithHandler(gcmConnectionHandler)
// -->The app go through this point
}
func gcmConnectionHandler(error: NSError?) {
// -->The app never enter in this function
if let error = error {
print("Could not connect to GCM: \(error.localizedDescription)")
} else {
self.connectedToGCM = true
print("Connected to GCM")
// ...
}
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
self.saveContext()
}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
// 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)
gcmRegistrationOptions = [kGGLInstanceIDRegisterAPNSOption:deviceToken,
kGGLInstanceIDAPNSServerTypeSandboxOption:true]
GGLInstanceID.sharedInstance().tokenWithAuthorizedEntity(gcmSenderID,
scope: kGGLInstanceIDScopeGCM, options: gcmRegistrationOptions, handler: gcmRegistrationHandler)
}
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
print("-- Failed to get deviceToken: \(error.localizedDescription)")
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
application.applicationIconBadgeNumber += 1
print(userInfo)
let apsInfo = userInfo["aps"] as! NSDictionary
var alertMessage = ""
print("********************** Received Notif")
if let alert = apsInfo["alert"] as? String{
alertMessage = alert
print(alertMessage)
}
else if let alert = apsInfo["alert"] as? NSDictionary, let body = alert["body"] as? String {
alertMessage = body
print(alertMessage)
}
// If the application is currently on screen "Active" then we trigger a custom banner View for that notification to be shown
// Else the system will handle that and put it in the notification center
if application.applicationState == UIApplicationState.Active {
AGPushNoteView.showWithNotificationMessage(alertMessage, autoClose: true, completion: { () -> Void in
// Do nothing
})
}
}
func gcmRegistrationHandler(registrationToken: String!, error: NSError!) {
if (registrationToken != nil) {
self.gcmRegistrationToken = registrationToken
print("GCM Registration Token: \(registrationToken)")
let userInfo = ["registrationToken": registrationToken]
NSNotificationCenter.defaultCenter().postNotificationName(
self.gcmRegistrationKey, object: nil, userInfo: userInfo)
} else {
print("Registration to GCM failed with error: \(error.localizedDescription)")
let userInfo = ["error": error.localizedDescription]
NSNotificationCenter.defaultCenter().postNotificationName(self.gcmRegistrationKey, object: nil, userInfo: userInfo)
}
}
// MARK: - GGLInstanceIDDelegate
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: gcmRegistrationOptions, handler: gcmRegistrationHandler)
}
// MARK: - GCMReceiverDelegate
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.
}
func subscribeToTopic() {
// If the app has a registration token and is connected to GCM, proceed to subscribe to the
// topic
let subscriptionTopic = "/topics/test-global"
if(gcmRegistrationToken != nil && connectedToGCM) {
GCMPubSub.sharedInstance().subscribeWithToken(gcmRegistrationToken, topic: subscriptionTopic,
options: nil, handler: {(NSError error) -> Void in
if (error != nil) {
// Treat the "already subscribed" error more gently
if error.code == 3001 {
print("Already subscribed to \(subscriptionTopic)")
} else {
print("Subscription failed: \(error.localizedDescription)");
}
} else {
subscribedToTopic = true;
NSLog("Subscribed to \(subscriptionTopic)");
}
})
}
}
}
There are couple problems in your code.
First, you need to call GCMService.sharedInstance().startWithConfig(GCMConfig.defaultConfig()) in the didFinishLaunchingWithOptions method, but you only called it in didRegisterForRemoteNotificationsWithDeviceToken method.
Second, you should call application.registerForRemoteNotifications() after application.registerUserNotificationSettings() in your didFinishLaunchingWithOptions method.
There is a sample GCM project for iOS available, you can follow the sample implementation for AppDelegate.swift file, so that your app will work correctly.
You can also get the GCM iOS sample project via Cocoapods by doing pod try Google, you can visit this documentation for more details.
Edited:
You should replace the lines in your didFinishLaunchingWithOptions with the following (notice that you should use let gcmConfig = GCMConfig.defaultConfig() and gcmConfig.receiverDelegate = self):
// [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
let settings: UIUserNotificationSettings =
UIUserNotificationSettings( forTypes: [.Alert, .Badge, .Sound], categories: nil )
application.registerUserNotificationSettings( settings )
application.registerForRemoteNotifications()
// [END register_for_remote_notifications]
// [START start_gcm_service]
var gcmConfig = GCMConfig.defaultConfig()
gcmConfig.receiverDelegate = self
GCMService.sharedInstance().startWithConfig(gcmConfig)
// [END start_gcm_service]
return true
Have you checked that you have defined NSAppTransportSecurity key is in your plist.

Resources