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

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.

Related

Flutter: iOS notification background handler

How to handle iOS background notification click_action when it is null ?
{
"to" : "abcd1234",
"collapse_key" : "type_a",
"notification" : {
"title": "Testing",
"body" : "Testing 1 2 3 4 5 6 7"
},
"data" : {
"click_action": "FLUTTER_NOTIFICATION_CLICK",
"route": "/Home",
}
}
onBackgroundMessage: Platform.isAndroid ? BackgroundHandler.backgroundHandler : null
This post mentioned onBackgroundMessage not implemented in iOS just use for android. Is there a way to handle in iOS ? Thanks.
Yes.
If you did all the ios firebase configurations using XCode, you can use the native swift functions to listen to the background notifications on iOS.
Inside your AppDelegate class,
override func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
}
override func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
/// Here you will get the silent background notification .
/// May be you can call you flutter code from here if you need.
/// Make sure that you end your processing quickly, else you will not get the background notification regularly and timely from iOS.
/// After you completed, make sure you call the completionHandler so that iOS knows you are done with your background task and you completed it so quickly.
completionHandler(UIBackgroundFetchResult.newData)
}
FlutterFire team is working on this to bring to the flutter side and you can expect this very soon I think by looking at their GitHub PRs.

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.

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

watchOS2 app and iPhone app communication

In the watchOS1, we had a method “openParentApplication”. This method communicated with the phone application even when it wasn’t running in foreground or background and fetched a reply immediately. I need something similar for watchOS2. I want my watch application to communicate immediately with the phone app even if my iPhone application is not running. Methods like updateApplicationContext:error:, sendMessage:replyHandler:errorHandler: and transferUserInfo: are not helpful in this scenario.
Please can someone suggest me a better approach to achieve this?
Actually sendMessage:replyHandler:errorHandler: is doing exactly what you are asking for. As long as your watch is connected to your phone it immediately gets a response to the message. This is working when the app is in the foreground, in the background or not running at all.
Here is how you set it up:
In the WatchExtension:
Setup the session. Typically in your ExtensionDelegate:
func applicationDidFinishLaunching() {
if WCSession.isSupported() {
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
}
}
And then send the message when you need something from the app:
if WCSession.defaultSession().reachable {
let messageDict = ["message": "hello iPhone!"]
WCSession.defaultSession().sendMessage(messageDict, replyHandler: { (replyDict) -> Void in
print(replyDict)
}, errorHandler: { (error) -> Void in
print(error)
}
}
In the iPhone App:
Same session setup, but this time also set the delegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
...
if WCSession.isSupported() {
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
}
}
And then implement the delegate method to send the reply to the watch:
func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
replyHandler(["message": "Hello Watch!"])
}
This works whenever there is a connection between the Watch and the iPhone. If the app is not running, the system starts it in the background. So, basically it just works like openParentApplication(_:reply:)

Get notification when app is in background mode

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' } } }