our project currently has a Node jS backend and we want to implement push notifications for iOS. We did some research and figured out that that we will have to store the tokens that APN gives us in our DB in order to send push notifications to specific devices. Can someone confirm this or is there a better way of sending notifications?
Secondly, I also found that when devices go through software updates that this changes their token so does that mean we must have capability to update the token in our DB because it will change often. This is also pretty important. Is there also any other times that the token might change?
Lastly, are there any good libraries in Node for sending push notifications?
Thanks in advance!
You must send the notification accessToken to the server, Its like address for notification to be delivered. You dont have to worry about the variation in the accesstoken because you have to send it when you login everytime so the new updated accesstoken will append in server too.You have to register for remote notifiation in your Appdelegate like this and later send the saved token in nsuserdefault to the server in login API.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
UIApplication.sharedApplication().registerForRemoteNotifications()
return true
}
//Called if successfully registered for APNS.
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
// let deviceTokenString = NSString(format: "%#", deviceToken) as String
var tokenStr = deviceToken.description
tokenStr = tokenStr.stringByReplacingOccurrencesOfString("<", withString: "", options: [], range: nil)
tokenStr = tokenStr.stringByReplacingOccurrencesOfString(">", withString: "", options: [], range: nil)
tokenStr = tokenStr.stringByReplacingOccurrencesOfString(" ", withString: "", options: [], range: nil)
print(deviceToken.description)
print(tokenStr)
//save the token in NSUserDefaults
NSUserDefaults.standardUserDefaults().setObject(deviceTokenString, forKey: "deviceToken")
}
//Called if unable to register for APNS.
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
print(error)
}
Reference Apple's Documentation
The device token is your key to sending push notifications to your app
on a specific device. Device tokens can change, so your app needs to
reregister every time it is launched and pass the received token back
to your server. If you fail to update the device token, remote
notifications might not make their way to the user’s device. Device
tokens always change when the user restores backup data to a new
device or computer or reinstalls the operating system. When migrating
data to a new device or computer, the user must launch your app once
before remote notifications can be delivered to that device.
I have been looking for an optimal solution for this but seems there's no clear way of handling it since we are not aware when the token will change.
Does this this mean we should store the token in our database and fetch it every time a user opens the app and then try to compare them ? That is not efficient enough I guess.
In my opinion, keep the token in db for the server then keep a keep a copy in local storage so you can compare that with one that is generated every time the app is opened, unless the two are not the same, then you can update the one in the db.
Related
I'm going through process of adding push notification on my iOS app by following a book and some docs on push notification.
Push notification was working fine for few days and then all of a sudden I started getting 3 push notification at a time and then it gradually increased to 7.
// this function is called from didFinishLaunchingWithOptions function
func requestForNotification(_ application: UIApplication){
UNUserNotificationCenter.current().requestAuthorization(options: [.badge,.sound,.alert]) { (granted, _) in
guard granted else {return}
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = deviceToken.reduce(""){$0 + String(format: "%02x",$1) }
sendTokenToService(token: token)
print("device token is:::::::::: \(token)")
}
//Send token to local server
func sendTokenToService(token:String){
var params = [String:AnyObject]()
params["token"] = token as AnyObject
APIManager.shared.request(apiRouter: APIRouter.init(endpoint: .addAdminToken(param: params))) { (response, success) in
if success, let response = response["response"] {
print(response)
}
}
}
registerForRemoteNotifications() is being called only once but I found this on apple's official docs:
registerForRemoteNotifications() method: UIKit might call it in other rare circumstances. For example, UIKit calls the method when the user launches an app after having restored a device from data that is not the device’s backup data. In this exceptional case, the app won’t know the new device’s token until the user launches it.
Any idea how to resolve this issue ?
Push notifications are delivered once per device token. If you have access to your push notification server (or push provider if you are not managing it yourself), you can verify that is the case. When you are building the app and installing on a device, the new build will most likely be generating a new token. This also happens when the user uninstalls/installs the app. This could be the reason you're getting multiple notifications. Apple is supposed to invalidate old device tokens and send feedback back to your server. For more information on how Apple sends you feedback, here's a link to their Apple APNS docs.
My application is using remote notification to remind users of planned actions. User may be informed even after a few months.
Application is updating device token during each time it starts (sends the received token to the server).
But there is a problem. Sometimes device token becomes invalid. (backend-service got error "Invalid token" from APNS). I know that it's normal that device token can change. But there is a case when user set reminder on after a few months and doesn't use app during this time.
How do I update device token when it was expired and when application is turned off?
At every time app running it will call below AppDelegate method, So you need to send the APN token every time app run:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let tokenParts = deviceToken.map { data -> String in
return String(format: "%02.2hhx", data)
}
let token = tokenParts.joined()
print("token :: ", token)
//TODO:- now you need to send above token to your API that saved this token to user profile
}
My coworker and I are building an app and implementing push notification feature utilizing AWS SNS API.
The issue here is that some devices work fine but some like iPhone 6,7,8 receive no notification regardless of the OS version (they're mostly iOS 11,12).
We've covered the basic by checking the below points:
notification turned on on all test devices
testers grant permission to receive notification at the app launch time
the p12 certificate uploaded to AWS is valid and the format is correct
The error messages we got from AWS CloudWatch Logs mostly were "bad device token" or "unregistered", but we are sure that we upload the device token as soon as we got it from APNS.
EDIT: actual code added
Register for notification (within didFinishLaunchingWithOptions)
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
if let error = error {
print("\(error.localizedDescription)")
} else {
application.registerForRemoteNotifications()
}
}
Get token
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("Successfully registered for notifications!")
// upload token to our database for later use
}
Is there anything we are missing?
I want to make a request every 15 minutes. Then I want to make a push notification to the user.
I use this code.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
// push
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: [UIUserNotificationType.Sound, UIUserNotificationType.Alert, UIUserNotificationType.Badge],categories:nil))
// background fetch
let settings = UIUserNotificationSettings(forTypes: [UIUserNotificationType.Sound, UIUserNotificationType.Alert, UIUserNotificationType.Badge], categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
UIApplication.sharedApplication().setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
return true
}
func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
print("Complete");
completionHandler(UIBackgroundFetchResult.NewData)
getData();
}
func getData() -> Void {
let p = PushService()
p.Push("Changed")
}
It works fine but only one time.
And when I close this app via task manager there is no push.
So how I can send push notifications every 15 minutes even when the user closed my app. (like WhatsApp or another messenger)
I use a local push.
As mentioned by myself and others, I wouldn't recommend going about it in the app. Instead, you should use Parse.com Cloud Code for managing and setting Push Notifications:
https://parse.com/apps/quickstart#cloud_code/window
This links shows how to set up Cloud Code using your terminal. You can automate Push Notifications using Parse as well. Parse has relatively good documentation which can help you get started.
Here's the push notification homepage on Parse: https://parse.com/products/push
Here are the parse docs for Push Notifications which will take you through registering your app for Push Notifications as well: https://parse.com/docs/ios/guide#push-notifications
Go down to "Sending Pushes" in the docs. This is where Parse will help you set up Push notifications with Cloud Code on Parse.
Once you set it up, go to your project, then "Core" and click jobs, and you'll get this screen (note, this is my project and I'm deleting posts. You can name this whatever you want):
Use this link for your push notification javascript code as a tutorial: How to send Push Notifications with Parse.com Cloudcode it's a good example. On Parse, as the image I posted above shows, you can get specific on when you want the push notification to push to the user.
Hope that helps
I have an app currently in beta-testing that has messaging functionality. It's set up to deliver a push notification when a user receives a new message from another user. The only time push notifications work is when a user sends a message to me specifically. If I try to send a message to any other user or any users message each other that don't include me, push notifications do not work. Only messages sent to me trigger push notifications on my device.
Here are some simple screenshots from Parse showing one push that sent properly and one that did not.
This is a private message sent from another user named "Alissa" to me in which I receive the push notification properly (as you can see by "pushes sent" = 1):
Here are the details of said push:
Now, here is a private message sent from my device, the same device that received the push notification properly, back to "Alissa". As you can see, the "pushes sent" = 0, meaning my device sent the message but the recipient did not receive the push notification:
And here are the details of that push, containing virtually identical information to the working one sent to me:
Finally, here is another push not working sent between "Alissa" and another user that is not me, therefore 2 users separate from my device.
This is the pattern when I look at a list of all pushes from users in my app. They all have "pushes sent" = 0 except for when a push is sent to my device, "pushes sent = 1".
I've printed to the console in my push notification method completion handlers and they indicate that the push was sent successfully when I send a message to another user. I will also point out that my device is being used for development of this app.
Also as a side note, it did not always use to be like this. A couple weeks ago everything was working normally. I released multiple new builds and never had a problem.
Can anyone guide me in the right direction here?
Edit: Below I've included more details including code in my app and
details of my developer account and Parse backend.
Relevant code in my app
The following is code I have in my AppDelegate as recommended by Parse to include for setting up push notifications. It's worth noting that the println statements "did register user notification settings" and "did register for remote notifications with device token" are both logged properly on app launch.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// ...
let userNotificationTypes = UIUserNotificationType.Alert | UIUserNotificationType.Badge
let settings = UIUserNotificationSettings(forTypes: userNotificationTypes, categories: nil)
application.registerUserNotificationSettings(settings)
application.registerForRemoteNotifications()
// ...
}
func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
println("did register user notification settings")
}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
PFInstallation.currentInstallation().setDeviceTokenFromData(deviceToken)
PFInstallation.currentInstallation().saveInBackground()
println("did register for remote notifications with device token")
}
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
println("didFailToRegisterForRemoteNotificationsWithError: ", error.localizedDescription)
}
This is code I have included when a message is sent to another user. It's also worth nothing that the println statement "success" is logged properly.
PFInstallation.query().whereKey("user", equalTo: incomingUser)
PFPush().setQuery(pushQuery)
let senderName = PFUser.currentUser()!.objectForKey("name") as! String
let data = [
"alert" : "New message from \(senderName)",
"badge" : "Increment"
]
push.setData(data)
push.sendPushInBackgroundWithBlock { (success: Bool, error: NSError?) -> Void in
if success {
println("success")
} else {
println(error?.localizedDescription)
}
}
Relevant details from my developer account
Here is a screenshot showing my App ID in my developer portal. It seems to show that push notifications are enabled properly (I deleted and re-added all certificates with a new provisioning profile)
Relevant details from my Parse account
Here are my push notifications settings within Parse. All this information was updated yesterday when I recreated certificates.
This might provide some level of insight although I'm not sure what. This is a screenshot of my PFInstallations table in Parse. An installation is created anytime someone logs in/opens the app. I just ran the application on my device and my record in the table is the top one. What's different from the rest is that there is a value in the "deviceToken" column only for my device. When I delete my PFInstallation record and restart the app to recreate it, there is always a value created under "deviceToken".
Relevant details from Xcode
Here is an expanded view of the code signing in my Xcode build settings. This code signing is identical in both the project and the target code signing.
Again, push notifications are working for all users messaging other users and only not working when a message is sent from my device to any other user.
Start with what makes your device unique, and it's status as a development environment device is the likely culprit.
A good place to check is your device's build and the consequent push certificate being used. If your device is in dev mode (meaning you are building and deploying to your phone via Xcode), then it will use the push certificate for Development instead of Production. (For more on this difference, see this great article by Ray Wenderlich)
The key factor here is that only your device will use this different certificate. If it's revoked/broken/not installed, only your device will have this problem.
You can also test this by deploying the app to your phone via TestFlight / HockeyApp / etc. instead of letting Xcode load it.
UPDATE:
Just pouring over the code, checking for errors. One thing already of note: your didFinishLaunchingWithOptions includes an extra PFInstallation.currentInstallation().saveInBackground() - you should remove that and only have it in the didRegisterForRemoteNotificationsWithDeviceToken method.
This is why only your device has an ID in it's PFInstallation, and is probably why your device can't reach anyone else - the push system is working, but it no no address to call out to from there; it would be a silent fail on the push, not on the parse system.
Have you tried having your users send a push to each other, or only to you and from you?