How to update APNS device token remotely - ios

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
}

Related

Retrieve the app push token from the device automatically in swift iOS

Is it possible to get a device token (push token) automatically or without registering for push notification in iOS Swift?
Currently, we are getting device token while registering for push notifications:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
...
}
It's not possible. We can not retrieve the app push token from the device automatically or without registering for push notification in swift iOS

How can I detect when the real iOS/APNS push token is registered with Firebase Cloud Messaging (FCM)?

Firebase's didReceiveRegistrationToken as seen below is getting called even if the user hasn't granted push notification permissions. I need to make sure that APNS push tokens are being registered for analytics, as well as for saving it on my server, but this function is getting called even when the user hasn't granted push permission. 🤷‍♂️
/**
* Requirement for Firebase push notifications.
* See documentation here: https://firebase.google.com/docs/cloud-messaging/ios/client
*/
extension AppDelegate: MessagingDelegate {
// Note: This callback is fired at each app startup and whenever a new token is generated.
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)
print("Getting called even if the user hasn't opted in for push notifications!")
}
}
TL;DR: FCM token is not a reliable way to detect if APNS token is registered. Disable method swizzling in firebase and listen to APNS registration manually.
FCM Tokens: An FCM token is also known as a device instance ID; it identifies that specific app on that specific device. In other words, FCM token doesn't equal an APNS token. Here is one explanation: APNs, FCM or GCM token.
FCM Token Auto Generation: Surprisingly, even without iOS user granting permission, Firebase generates an FCM token on app launch. I guess it wants to find a way to identify the app & device pair, so as soon as you launch, you're gonna have an FCM token. If you want, you can disable auto-generation and wait for the user to opt-in.
By default, the FCM SDK generates a registration token for the client app instance on app launch. If you want to get an explicit opt-in before using Instance ID, you can prevent generation at configure time by disabling FCM. To do this, add a metadata value to your Info.plist (not your GoogleService-Info.plist):
Here lies one of the problems! Even if the user has disabled auto-generated of FCM tokens, one will be generated when the user is prompted to enable push notifications not when the user has accepted push notification permission. This seems quiet odd and I ended up reporting this to Firebase.
APNS Token Swizzling: So by default Firebase takes over AppDelegate's didRegisterForRemoteNotificationsWithDeviceToken (called "swizzling"). You can disable this and override the delegate method yourself. This will then give you access to both APNS token as well as FCM token.
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("Successfully registered for notifications!")
// Required to make FCM work since we disabled swizzling!
Messaging.messaging().apnsToken = deviceToken
// String version of APNS so we can use save it/use it later/do analytics.
let apnsToken = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
}
Getting a callback for a device ID token and getting a callback for an actual pushed message are two different things. The device ID token callback just gives you a token that identifies the device for a later push notification. It isn't actually a pushed notification itself. It's just an ID.
That said, if you disagree with the behavior of the client SDK from a security perspective, you're free to contact Firebase support directly with feedback.

How to fix 'Multiple push notification' error in swift?

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.

didRegisterForRemoteNotificationsWithDeviceToken same token each time

just noticed that every time didRegisterForRemoteNotificationsWithDeviceToken is called I get the same token back. Is this normal when does the token change? Does it remain the same for each app install?

Storing tokens for Push Notifications

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.

Resources