I am using Parse as my backend for my iOS app to send push notifications. My problem is that the app icon never shows a badge after receiving push notifications (Besides for the badge, everything works fine).
I've checked the "badge" field in the Installation DB on Parse and it is increasing with every push, so I feel it might be a client-side issue
Here is my cloud code:
Parse.Push.send({
where: pushQuery,
data: {
aps: {
alert: "Your friend " + request.user.get("Name") + " just joined VoiceMe!",
sound: "default",
AlertType: "NewFriend"
},
badge: "Increment"
}
}, {
success: function() {
/*all is good*/
},
error: function(error) {
outcome = false;
errorOutput = error;
}
});
And the code in my app:
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
let currentInstallation = PFInstallation.currentInstallation()
if PFUser.currentUser() != nil {
currentInstallation.setObject(PFUser.currentUser()!, forKey: kParseInstallationUserKey)
}
currentInstallation.setDeviceTokenFromData(deviceToken)
currentInstallation.channels = ["global"]
currentInstallation.saveInBackgroundWithBlock { (resultBool, error) -> Void in
println("register device: --- \(resultBool) ---- error: \(error)")
}
}
Image of Installation DB on Parse:
See my answer here: https://stackoverflow.com/a/27615528/2353523 for reference
you've created your own dictionary. That's used for interactive notifications etc. Badge is outside of that dictionary that you've created which is the correct dictionary for sending pushes. That's why it doesn't increment in the payload you created under the dictionary of aps. You have to tell it to. Else, just delete the aps dict and pass your parameters through the data dict
Related
I am using firebase for push notifications and developing a mobile app using ionic/capacitor. I register the push notifications and get back a token like this...
PushNotifications.requestPermission().then( result => {
if (result.granted) {
PushNotifications.register();
}
});
PushNotifications.addListener('registration',
(token: PushNotificationToken) => {
alert('Push registration success, token: ' + token.value);
console.log(token.value);
}
);
In the firebase console I can successfully send and receive notifications when targeting my app in the user segment but when I try to target a single device by copying and pasting in the token using send test message I do not receive the push notification. Note: this works fine for me on Android.
Token looks like this...
BAAEF129E8F596F1305D0FEA2F50E21B2768FEFCA83CF19602CA0183077E441D
Capacitor uses APNS for push, that token you got is a APNS token and those don't work on FCM.
Not sure how did you integrate FCM into Capacitor, but you should convert the APNS token to FCM token.
It's something like this
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
InstanceID.instanceID().instanceID { (result, error) in
if let error = error {
NotificationCenter.default.post(name: Notification.Name(CAPNotifications.DidFailToRegisterForRemoteNotificationsWithError.name()), object: error)
} else if let result = result {
NotificationCenter.default.post(name: Notification.Name(CAPNotifications.DidRegisterForRemoteNotificationsWithDeviceToken.name()), object: result.token)
}
}
}
For more information you can check this guide about how to integrate FCM into Capacitor.
I checked OneSignal documentation but I couldn't understand clearly as beginner how setting dictionary as a post notification's additional data (like postID, userID, type) in iOS Native SDK using Swift to decide and redirect when user interacted with notification.
For posting I'm doing only like that:
OneSignal.sendTag("username", value: "\(user)")
OneSignal.postNotification(["contents": ["en": "#\(user) added an additive to your '\(title)' experience: \"\(strLast)\""],
"include_player_ids": [postOwnerPlayerID],
For receiving:
OneSignal.initWithLaunchOptions(launchOptions, appId: "______", handleNotificationReceived: nil, handleNotificationAction: {
(result) in
// This block gets called when the user reacts to a notification received
let payload = result?.notification.payload
//Try to fetch the action selected
if let additionalData = payload?.additionalData {
print("payload")
print(additionalData)
}
// After deciding which action then I can redirect user..
let username: String? = UserDefaults.standard.string(forKey: KEY_UID)
if username != nil {
if let tabbarController = self.window!.rootViewController as? UITabBarController {
tabbarController.selectedViewController = tabbarController.viewControllers?[2]
// NotificationCenter.default.post(name: Foundation.Notification.Name(rawValue: "notificationsUp"), object: nil)
}
}
}, settings: [kOSSettingsKeyInFocusDisplayOption : OSNotificationDisplayType.none.rawValue])
You set the data field as a key in the dictionary passed to OneSignal.postNotification like the following.
OneSignal.postNotification(["contents": ["en": "Test Message"],
"include_player_ids": ["3009e210-3166-11e5-bc1b-db44eb02b120"],
"data": ["postID": "id"]])
Then you need to get ready your keys from additionalData from the payload in the handleNotificationAction function.
if let additionalData = payload?.additionalData {
let postID: String? = additionalData["postID"]
}
Example from iOS in objC to send additional data...
[OneSignal postNotification:#{#"contents":#{#"en":text},
#"include_player_ids":oneSignalIds,
#"data":#{#"key": #"value"},
}];
And to receive the data...
[OneSignal initWithLaunchOptions:launchOptions
appId:ONESIGNAL_APPID
handleNotificationReceived:^(OSNotification *notification) {
if (notification.payload.additionalData) {
NSDictionary* additionalData = notification.payload.additionalData;
if (additionalData[#"key"]){
NSLog(#"Received Data - %#", additionalData[#"key"]);
}
}
}
handleNotificationAction:nil
settings:#{kOSSettingsKeyInAppAlerts:#YES}];
Hope it helps someone :)
Thanks to #jkasten helped me in the right direction! helped me get rid of the AnyHashable warning I was getting.
Swift 3 code (change PATH to the additionalData parameter you want to output):
let PATH = notification!.payload.additionalData["PATH"]
print("PATH: ",PATH as Any)
If you're looking to do the same but in the Notification Service Extension, take a look at our updated documentation.
The Notification Service Extension is used for:
- Badges
- Influenced Opens with Firebase Analytics
- Media Attachments
- Action Buttons
We migrated from UA to One Signal. We are sending push from cloud code like
var pushInfo = {
"app_id" : "xxxxxx",
"data": {
"objectId": objectId,
"placeId": placeId,
},
"included_segments": ["All Users"],
"contents": {"en": message}
};
var headers = {
"Content-Type": "application/json; charset=utf-8",
"Authorization": "Basic XXXXX"
};
var options = {
host: "onesignal.com",
port: 443,
path: "/api/v1/notifications",
method: "POST",
headers: headers,
};
var https = require('https');
var req = https.request(options, function(res) {
res.on('data', function(data) {
console.log("Response:");
console.log(JSON.parse(data));
});
});
req.on('error', function(e) {
console.log("ERROR:");
console.log(e);
});
req.write(JSON.stringify(pushInfo));
req.end();
In my AppDelegate.m I do
[OneSignal initWithLaunchOptions:launchOptions appId:#"XXXXX"];
Now earlier when a notification is received and user Taps on it, it used to call
-(void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
Q. This is not getting called now. How do I handle it with OneSignal.
Q. What I need to do to handle a silent notification (no visible badge/banner etc)
I Assume you are testing/running your app on an iOS10 device,
I looked at OneSignal SDK Code and I think the SDK automatically uses the new UserNotifications Framework (add in iOS10), when iOS10 is detected on device.
In this case, the AppDelegate method you mentioned above does not get invoked, instead methods in UNUserNotificationCenterDelegate get invoked, which are captured by SDK to record clicks/views.
To Fix the issue, Create a new class which implements OSUserNotificationCenterDelegate and provide its instance to OneSignal using [OneSignal setNotificationCenterDelegate:yourCustomNotificationCenterDelegateInstance]
Please note that application:didReceiveRemoteNotification:fetchCompletionHandler: is still called when a silent push notification (content-available: 1) arrives, but its not called when user taps the notification if UNUserNotificationCenterDelegate is used.
Also, there was an issue on iOS 10.0.X where the application:didReceiveRemoteNotification was called instead of application:didReceiveRemoteNotification:fetchCompletionHandler: See: https://forums.developer.apple.com/thread/54332 , but I doubt if this is the case with you.
One Signal Notification Integration
Use Following Block Of Code TO Handle PushNotification Message Content
Put below code in AppDelegate's ApplicationDidFinishLaunch Options method
let notificationReceivedBlock: OSHandleNotificationReceivedBlock = { notification in
print("Received Notification: \(notification!.payload.notificationID)")
}
let notificationOpenedBlock: OSHandleNotificationActionBlock = { result in
// This block gets called when the user reacts to a notification received
let payload: OSNotificationPayload = result!.notification.payload
var fullMessage = payload.body
print("Message = \(String(describing: fullMessage))")
if payload.additionalData != nil {
if payload.title != nil {
let messageTitle = payload.title
print("payload.category \(payload.category)")
print("payload.subtitle \(payload.subtitle)")
print("Message Title = \(messageTitle!)")
}
let additionalData = payload.additionalData
if additionalData?["actionSelected"] != nil {
fullMessage = fullMessage! + "\nPressed ButtonID: \(String(describing: additionalData!["actionSelected"]))"
}
}
}
let onesignalInitSettings = [kOSSettingsKeyAutoPrompt: false,
kOSSettingsKeyInAppLaunchURL: true]
OneSignal.initWithLaunchOptions(launchOptions,
appId: "Your App Id",
handleNotificationReceived: notificationReceivedBlock,
handleNotificationAction: notificationOpenedBlock,
settings: onesignalInitSettings)
**Use following block of code To Receive OneSingnal Notification Content**
OneSignal.inFocusDisplayType = OSNotificationDisplayType.notification;
// Recommend moving the below line to prompt for push after informing the user about
// how your app will use them.
OneSignal.promptForPushNotifications(userResponse: { accepted in
print("User accepted notifications: \(accepted)")
})
application:didReceiveRemoteNotification:fetchCompletionHandler: is the correct selector for background silent content-available notifications. Make sure you are using the latest 2.2.2 OneSignal SDK as there were some fixes to maintain compatibility with the older AppDelegate selectors.
You may want to look into using UNNotificationServiceExtension with mutable-content for iOS 10 devices as this still works when the app has been swiped away.
In my case all I had to do is remove the Firebase handlers in app delegate. OneSignal can hijack the events from FCM. This way I had both the OneSingal Push notification and the ones delivered by Firebase. As for One signal I used a simple copy and paste from their examples.
THIS ARE THE PARTS THAT I REMOVED FROM MY AppDelegate.m
REMOVE IMPORTS -> #import "RNFirebaseMessaging.h"
REMOVE IMPORTS -> #import "RNFirebaseNotifications.h"
IN: - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
REMOVE ->[RNFirebaseNotifications configure];
REMOVE HANDLERS:
- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification {
[[RNFirebaseNotifications instance]
didReceiveLocalNotification:notification];
}
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo
fetchCompletionHandler:(nonnull void (^).
(UIBackgroundFetchResult))completionHandler{
[[RNFirebaseNotifications instance]
didReceiveRemoteNotification:userInfo
fetchCompletionHandler:completionHandler];
[UIApplication sharedApplication].applicationIconBadgeNumber += 1;
}
- (void)application:(UIApplication *)application
didRegisterUserNotificationSettings:(UIUserNotificationSettings
*)notificationSettings {
[[RNFirebaseMessaging instance]
didRegisterUserNotificationSettings:notificationSettings];
}
import { Platform } from 'react-native'
import OneSignal, { NotificationReceivedEvent, OpenedEvent } from 'react-native-onesignal'
import Config from '../../Config/Config'
import State from '../../State/State'
interface WithSuccess {
success: boolean
}
interface ExternalUserIdResultI {
push: WithSuccess
email: WithSuccess
sms: WithSuccess
}
class Messaging {
public Init = () => {
//OneSignal Init Code
OneSignal.setLogLevel(Config.messaging.debugLevel, 0)
OneSignal.setAppId(Platform.OS === 'ios' ? Config.messaging.iosAppId : Config.messaging.androidAppId)
//END OneSignal Init Code
//Prompt for push on iOS
OneSignal.promptForPushNotificationsWithUserResponse((response: boolean) => {
console.log("Prompt response:", response);
})
//Method for handling notifications received while app in foreground
OneSignal.setNotificationWillShowInForegroundHandler((notificationReceivedEvent: NotificationReceivedEvent) => {
console.log("OneSignal: notification will show in foreground:", notificationReceivedEvent);
let notification = notificationReceivedEvent.getNotification();
console.log("notification: ", notification);
notificationReceivedEvent.complete(notification)
})
//Method for handling notifications opened
OneSignal.setNotificationOpenedHandler((notification: OpenedEvent) => {
console.log("OneSignal: notification opened:", notification);
})
OneSignal.addSubscriptionObserver(event => {
console.log("OneSignal: subscription changed:", event);
})
}
public SendTag = (key: string, value: string) => {
OneSignal.sendTag(key, value)
}
public SetExternalUserId = (externalUserId: string) => {
//#ts-ignore
OneSignal.setExternalUserId(externalUserId, (results: ExternalUserIdResultI) => {
// The results will contain push and email success statuses
console.log('Results of setting external user id');
console.log(results);
// Push can be expected in almost every situation with a success status, but
// as a pre-caution its good to verify it exists
if (results.push && results.push.success) {
console.log('Results of setting external user id push status:');
console.log(results.push.success);
}
// Verify the email is set or check that the results have an email success status
if (results.email && results.email.success) {
console.log('Results of setting external user id email status:');
console.log(results.email.success);
}
// Verify the number is set or check that the results have an sms success status
if (results.sms && results.sms.success) {
console.log('Results of setting external user id sms status:');
console.log(results.sms.success);
}
});
}
public RemoveExternalUserId = () => {
//#ts-ignore
OneSignal.removeExternalUserId((results: ExternalUserIdResultI) => {
// The results will contain push and email success statuses
console.log('Results of removing external user id');
console.log(results);
// Push can be expected in almost every situation with a success status, but
// as a pre-caution its good to verify it exists
if (results.push && results.push.success) {
console.log('Results of removing external user id push status:');
console.log(results.push.success);
}
// Verify the email is set or check that the results have an email success status
if (results.email && results.email.success) {
console.log('Results of removoing external user id email status:');
console.log(results.email.success);
}
});
}
}
export default new Messaging()
I'm using advanced targeting with parse. On the Parse dashboard it shows that the notification was sent successfully, but it doesn't show to whom when I click on the notification in the dashboard. I have also set up my PFInstallation correctly in App Delegate. Here is my code to send the notification:
let message = "\(PFUser.currentUser()?.username) has invited you to a match!"
let username = defender
let query = PFInstallation.query()!
query.whereKey("username", equalTo: username)
let push = PFPush()
push.setQuery(query)
push.setMessage(message)
push.sendPushInBackgroundWithBlock { (success: Bool, error: NSError?) -> Void in
if error == nil && success{
print("push notification sent to user \(defender)!")
}
}
Username is the target username. The dashboard shows the username as a key. These are the push notification's details:
Push Details
PUSH ID
hOX5Ulhcds
TARGETING
username is ""
SENDING TIME
December 21st, 2015 at 2:02 PM
EXPIRATION
None
FULL TARGET
{
"username": ""
}
FULL DATA
{
"alert": "Optional(\"John Cena\") has invited you to a match!"
}
This is the code I run when the user logs in or signs up:
let currentInstallation = PFInstallation.currentInstallation()
currentInstallation["username"] = PFUser.currentUser()?.username
currentInstallation.saveInBackground()
How do I make my code work?
I have created some cloud code for my Parse database and I want it to be called when the a user installs the app. I have implemented Push so I want it to get triggered when the user registers for Push Notifications. I also want to be able to pass the "deviceToken" into my cloud function.
Here is the function that I have so far:
Parse.Cloud.define("newListing", function(request, response) {
var ListingClass = Parse.Object.extend("Listings");
var listing = new ListingClass();
listing.set("Name","--");
listing.set("DeviceID",request.params.param_DeviceID);
listing.save(null,{
success:function(listing) {
response.success(listing);
},
error:function(error) {
response.error(error);
}
});
});
Summary:
I want to call the cloud function "newListing" when the user registers for Push Notifications
Any ideas? Thanks!
You only get the deviceToken when the didRegisterForRemoteNotificationsWithDeviceToken method is called in the AppDelegate, so place your cloud code calling function in there.
Swift code:
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
// Do your normal token handling to set the device token in the Installation and save to Parse.com
let currentInstallation = PFInstallation.currentInstallation()
currentInstallation.setDeviceTokenFromData(deviceToken)
currentInstallation.saveInBackground()
// Call your cloud code function
let deviceTokenAsString = .... // You need to implement this code
PFCloud.callFunctionInBackground("newListing", withParameters: ["param_DeviceID": deviceTokenAsString]) { results, error in
// Error handling, and any other functionality you need when your cloud function is complete
}
}
Below is a solution that I came up with that didn't require the app to be modified. This cloud function gets called after a new installation occurs.
Parse.Cloud.afterSave(Parse.Installation, function(request, response) {
var newID = request.object.get("deviceToken");
var query = new Parse.Query("Listings");
query.equalTo("DeviceID", newID);
query.find({
success: function(results) {
if(results.length > 0){
response.success("ID already exists");
}else{
var ListingClass = Parse.Object.extend("Listings");
var listing = new ListingClass();
listing.set("Name","--");
listing.set("DeviceID",newID);
listing.save(null,{
success:function(listing) {
response.success(listing);
},
error:function(error) {
response.error(error);
}
});
}
},
error: function() {
response.error("ID fail");
}
});
});