Get notification when app is in background mode - swift

I want to call a function when I receive a notification, even when my application is in background
I found a lot of question / answer on stackoverflow
but in me it does not. So I will explain everything I 've done
i have background mode activate
here
my info.plist is good
here
in my didFinishLaunchingWithOption i have
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: .Badge | .Sound | .Alert, categories: nil))
application.registerForRemoteNotifications()
and i have
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
println("NOTIFICATION 4");
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
println("NOTIFICATION FETCH");
}
but i can't see my log when i send my notification
my notification is send like this
{ deviceToken: XXXXX,
expiration: 1442225011,
identifier: 0,
payload:
{ 'content-available': '1',
aps: { badge: 0, alert: 'Hello' } } }
I do not know what to do more ... I really can not receive this notification, nothing happens ( I receive the banner but no call to my function)
thank's a lot

As you can read in documents:
The aps dictionary can also contain the content-available property. The content-available property with a value of 1 lets the remote notification act as a “silent” notification.
so you should insert content-available in aps dictionary, like this:
{ deviceToken: XXXXX,
expiration: 1442225011,
identifier: 0,
payload:
{ aps: { 'content-available': '1', badge: 0, alert: 'Hello' } } }

Related

How do you receive APNS payload data from a Silent Notification?

I'm testing notifications in XCode 12.2. It is very easy to test alert style notifications. You just drag them onto your app in simulator and the payload shows up in:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
notificationsDispatcher.process(userInfo)
completionHandler(.newData)
}
Now, when testing silent notifications I can get the silent notification to fire after setting "Background fetch" in background modes. I then get into:
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
debugPrint("hello silence ")
}
This is fine if i want the app to do something unrelated to the data in the payload. How do I see the actual apns payload data?
I'm trying to access this payload example that i'm using:
{
"aps": {
"category": "tasks",
"content-available": 1,
"thread-id": "tasks"
},
"resource": {
"resourceType": null,
"additionalProperties": {
"taskIds": [
"xxx"
],
"classification": "xxx",
"status": "xxx"
}
}
}
Simulating silent push notifications currently does not work if the content-available flag is set since it calls the wrong delegate method.
If you try with an actual device and send a real push notification, it should call the application(:didReceiveRemoteNotification:fetchCompletionHandler:) delegate and you can access the payload.
There are also some forum posts about this, like https://developer.apple.com/forums/thread/652649.

Push notification with Engage Digital (formerly Dimelo) didReceiveRemoteNotification never called

I send notification I receive it on the phone no problem;
Now I want to customise image titre ...
the problem is this function on delegate was never called didReceiveRemoteNotification
On appDelegate didFinishLaunchingWithOptions:
// Dimelo: Push Notif and Badge
dimelo?.updateAppBadgeNumber = true
dimelo?.developmentAPNS = true
dimelo?.initialize(withApiSecret: BuildConfig.GetInstance().getDimeloApiSecret(), domainName: BuildConfig.GetInstance().getDimeloDomainName(), delegate: self)
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
// Register the device token.
Dimelo.sharedInstance().deviceToken = deviceToken
}
func dimeloDidBeginNetworkActivity(_ dimelo: Dimelo?) {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
func dimeloDidEndNetworkActivity(_ dimelo: Dimelo?) {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
dimelo?.consumeReceivedRemoteNotification(userInfo)
}
I already activate remote notification on baclground mode
but the function didReceiveRemoteNotification was never called :(
I'm integrating this RingCentral Engage Digital / Dimelo library:
https://github.com/ringcentral/engage-digital-messaging-ios/issues
I have implemented this demo app using the Dimelo iOS SDK: https://github.com/tylerlong/GrandTravel-iOS/blob/master/GrandTravel/AppDelegate.swift
Could you please check my code and figure out the differences?
I suggest you to print logs for both handleActionWithIdentifier and didReceiveRemoteNotification.
If it still doesn't work, please send email to devsupport#ringcentral.com and we will investigate.

FCM: Message sent but not received

tried to solve this myself. spent like an hour or so still no result.
Got me old code of a previous project regarding FCM. however the code was and still working on it's app. though i managed to transfer the code to my new project. but it won't work here.
Now i know APN's are weird and complicated. but it's more of a memorized situation for me.
Things i have done:
- Uploaded my personal .p12 to my firebase project
- Enabled "Push Notifications" in app capabilities
- Imported and used UserNotifications framework on appdelegate.swift
Here's how my AppDelegate look like:
import UIKit
import Firebase
import FirebaseFirestore
import StoreKit
import UserNotifications
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, MessagingDelegate {
var window: UIWindow?
override init() {
super.init()
FirebaseApp.configure()
// not really needed unless you really need it FIRDatabase.database().persistenceEnabled = true
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
Auth.auth().signInAnonymously() { (authResult, error) in
// ...
if #available(iOS 10.0, *) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
}
Messaging.messaging().delegate = self
UIApplication.shared.statusBarStyle = .default
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = mainStoryboard.instantiateViewController(withIdentifier: "gateway") as! gatewayViewController
window!.rootViewController = viewController
return true
}
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
print("Firebase registration token: \(fcmToken)")
let dataDict:[String: String] = ["token": fcmToken]
NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
// TODO: If necessary send token to application server.
// Note: This callback is fired at each app startup and whenever a new token is generated.
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
// if let messageID = userInfo[gcmMessageIDKey] {
// print("Message ID: \(messageID)")
// }
// Print full message.
print(userInfo)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
//if let messageID = userInfo[gcmMessageIDKey] {
// print("Message ID: \(messageID)")
//}//
// Print full message.
print(userInfo)
completionHandler(UIBackgroundFetchResult.newData)
}
Okay so with this code you get the devices Firebase registration token, i copied my code and used it on Cloud Functions to send a test message, here's how my CF looks like:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.helloWorld = functions.https.onRequest((request, response) => {
var registrationToken = 'f8fWx_sANVM:APA91bEd46drxiBvHLZd5YKVClQr91oubzJKOyXE1LNgxOsi3ihUw31yEJL6prHKm-A83B1N1sr2GOff3P9tUsRNhCpG7_VMRlDUDfthIcwkDUgzKPV5NZtlo6pcpxsvD9ZgYlPqibNp';
var payload = {
notification: {
title: "just published new Word",
body: "Hii",
}
};
// registration token.
admin.messaging().sendToDevice(registrationToken, payload)
.then(function(response) {
// See the MessagingDevicesResponse reference documentation for
// the contents of response.
return console.log("Successfully sent message:", response);
})
.catch(function(error) {
console.log("Error sending message:", error);
});
});
Okay so far after hitting the helloWorld url, my console gets this:
Successfully sent message: { results: [ { messageId: '0:1537714204565821%b3b8835bb3b8835b' } ],
canonicalRegistrationTokenCount: 0,
failureCount: 0,
successCount: 1,
multicastId: 8154206809408282000 }
Function execution took 60002 ms, finished with status: 'timeout'
Last time on my previous project it took 20ms at it's very best. I still can't figure this out. Your help is greatly appreciated
You're using a HTTP(S) triggered Cloud Function, which means your code must send a response. Since your code doesn't do that, the function runs for 60s and then gets terminated by the Cloud Functions environment. This means you pay for more time than you actually need, so you'll want to fix it.
For example:
// registration token.
admin.messaging().sendToDevice(registrationToken, payload)
.then(function(response) {
// See the MessagingDevicesResponse reference documentation for
// the contents of response.
//return console.log("Successfully sent message:", response);
res.status(200).send(response);
})
.catch(function(error) {
console.log("Error sending message:", error);
});
This will get rid of the message in the Cloud Functions logs, and ensures you only pay for the time you actually need.

How to receive PUSH Notifications properly?

I am writing VoIP application using Xcode 9.2, Swift 4.0, minimum iOS version is 10.3. I want to receive incoming call even iPhone is in sleeping mode (just like WhatsApp or Viber). I already know that I must use CallKit for it. But I do not know how to receive PUSH Notifications correctly. I mean, I have two ways how to receive them. First is in AppDelegate.swift, please, look (this method is working):
import UIKit
import UserNotifications
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
//Getting permissions for Notifications
//222
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
}
application.registerForRemoteNotifications()
//222
return true
}
//For PUSH Notifications settings
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("NOTIFICATION ERROR: \(error)")
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let pushToken = deviceToken.reduce("", {$0 + String(format: "%02X", $1)}).lowercased()
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
// Receiving call?
}
}
But, no matter, what I am doing, it does not trigger incoming call if iPhone is sleeping. Lately I saw that there is another method to receive PUSH Notifications. Please, look:
import UIKit
import CallKit
import PushKit
class ViewController: UIViewController, CXProviderDelegate, PKPushRegistryDelegate {
override func viewDidLoad() {
let registry = PKPushRegistry(queue: nil)
registry.delegate = self
registry.desiredPushTypes = [PKPushType.voIP]
}
func providerDidReset(_ provider: CXProvider) {
}
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
action.fulfill()
}
func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
action.fulfill()
}
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
print(pushCredentials.token.map { String(format: "%02.2hhx", $0) }.joined())
}
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: #escaping () -> Void) {
// Receive call?
}
}
The problem is that in first method I have this PUSH token: 9bcf73ac3d6a68be07c26d8b6a972f5b716cd5308f1a19048b62d19b9b8d4bd1
Second method returns me this PUSH token:
1e40fafb2963161b1bcf1bdde00d625a879c6934e19e2fb32f62b1c9272e956f
And they are not equal! How can this be? So, main question is: how to receive PUSH Notifications and what is difference between AppDelegate and PushKit methods and which token is correct?
Thank you for any help!
There are two types of Notifications (in fact more, but, in this case, just two): PUSH Notifications and VoIP Notifications. They have different tokens and different system of receiving. VoIP services exists only for calls (Apple CallKit), opposite PUSH service exists for messages and other notifications. So, in fact, both of this methods are necessary and one does not replaces another. And both of this methods requiring different Certificates in Apple Developer Account: Apple Push Notification service SSL for PUSH Notifications and VoIP Services Certificate for VoIP.
Thank you everyone for help! Be free to edit this if you see a error.
First of all remove this line application.registerForRemoteNotifications()
Now comes to puskit below method will have right token
func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, forType type: PKPushType) {
print("\(#function) voip token: \(credentials.token)")
let deviceToken = credentials.token.reduce("", {$0 + String(format: "%02X", $1) })
print("\(#function) token is: \(deviceToken)")}
and Push-kit framework does not have UI like remote or local notification you need to trigger local notification every time when below method called
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: #escaping () -> Void) {
// Receive call?
}

CloudKit subscriptions in tvOS

I am trying to sync data between a few Apple TVs using CloudKit and CKSubcription. The problem is application:didReceiveRemoteNotification: is never called when I add, delete, or update records. I believe I am configuring the subscriptions correctly and I have confirmed they are added to the CloudKit dashboard. I have repeatedly tried resetting the development environment in the dashboard, but that is not helping. I really don't want to create a timer to fetch every so often. Thanks for any help!
Also, I am using the private database in CloudKit and not the public database if that matters.
Here's my code:
AppDelegate.swift
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
CloudKitManager.subscribeToItemUpdates()
application.registerForRemoteNotifications()
...
return true
}
CloudKitManager.swift
class func subscribeToItemUpdates() {
if let uuid = UIDevice.currentDevice().identifierForVendor?.UUIDString {
saveSubscriptionWithIdentifier(uuid + "create", options: CKSubscriptionOptions.FiresOnRecordCreation)
saveSubscriptionWithIdentifier(uuid + "update", options: CKSubscriptionOptions.FiresOnRecordUpdate)
saveSubscriptionWithIdentifier(uuid + "delete", options: CKSubscriptionOptions.FiresOnRecordDeletion)
}
}
class func saveSubscriptionWithIdentifier(identifier: String, options: CKSubscriptionOptions) {
let sub = CKSubscription(recordType: "Message", predicate: NSPredicate(value: true), subscriptionID: identifier, options: options)
sub.notificationInfo = CKNotificationInfo()
let publicDatabase = CKContainer.defaultContainer().publicCloudDatabase
publicDatabase.saveSubscription(sub) { (savedSubscription, error) -> Void in
if error != nil {
print("Error saving CloudKit subscription \(error)")
}
else {
print("Saved subscription to CloudKit", savedSubscription)
}
}
}
There is a slight difference between tvOS and iOS. In my demo app i handle it like this:
#if os(tvOS)
//This will only be called when your app is active. So this is what you should use on tvOS
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
EVLog("Push received")
EVCloudData.publicDB.didReceiveRemoteNotification(userInfo, executeIfNonQuery: {
EVLog("Not a CloudKit Query notification.")
}, completed: {
EVLog("All notifications are processed")
})
}
#else
// Process al notifications even if we are in the background. tvOS will not have this event
// Make sure you enable background notifications in the app settings. (entitlements: pushnotifications and backgrounds modes - notifications plus background fetch)
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
EVLog("Push received")
EVCloudData.publicDB.didReceiveRemoteNotification(userInfo, executeIfNonQuery: {
EVLog("Not a CloudKit Query notification.")
}, completed: {
EVLog("All notifications are processed")
completionHandler(.NewData)
})
}
#endif
For more information see EVCloudKitDao