I'm using Azure push notifications and am noticing that APNS is sending me a new device token every time my app is launched. Per Apple's documentation, device tokens should generally remain constant:
The device token included in each request represents the identity of
the device receiving the notification. APNs uses device tokens to
identify each unique app and device combination. It also uses them to
authenticate the routing of remote notifications sent to a device.
Each time your app runs on a device, it fetches this token from APNs
and forwards it to your provider. Your provider stores the token and
uses it when sending notifications to that particular app and device.
The token itself is opaque and persistent, changing only when a
device’s data and settings are erased. Only APNs can decode and read a
device token.
Yet, based on the constant number of failed messages sent via Azure, I can deduce that my app is receiving a new device token from APNS each time I launch the app. Can somebody tell me:
Why I'm getting a new device token each time even though I'm not changing phone settings or deleting the app?
In Apple's excerpt above, what do they mean by "deleting device's data"? Exactly what data is used by APNS to determine a "unique app and device combination"?
Here's the code:
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if ([application respondsToSelector:#selector(registerUserNotificationSettings:)]) {
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge |
UIUserNotificationTypeAlert |
UIUserNotificationTypeSound)
categories:nil];
[application registerUserNotificationSettings:settings];
} else {
[application registerForRemoteNotifications];
}
}
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
//initiate registration process with Apple Push Notification service
[application registerForRemoteNotifications];
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *) token {
// Setting token with Azure hub
[[MyAzureNotificationHub sharedInfo] setDeviceTokenData:token];
}
You can read this:
Never cache device tokens; always get them from the system when you need them. Although device tokens are unique to an app and device, they can change over time. The device token can change at any time but is guaranteed to be different when the user restores their device from a backup, when the user installs your app on a new device, and when the user reinstalls the operating system. Fetching the token from the system ensures that you always have the current token needed to communicate with APNs. In addition, if the token has not changed, fetching it is fast and does not incur any significant overhead.
(Source: https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/HandlingRemoteNotifications.html#//apple_ref/doc/uid/TP40008194-CH6-SW3)
From my experience with APNS, the token changes anytime you'll register for remote notifications (app is relaunched). And each time you should resend new token to your backend, so it sends push notifications using the latest token.
Short answer is that device token should NOT change each time an app registers with APNS. From Apple:
The device token included in each request represents the identity of
the device receiving the notification. APNs uses device tokens to
identify each unique app and device combination. It also uses them to
authenticate the routing of remote notifications sent to a device.
Each time your app runs on a device, it fetches this token from APNs
and forwards it to your provider. Your provider stores the token and
uses it when sending notifications to that particular app and device.
The token itself is opaque and persistent, changing only when a
device’s data and settings are erased. Only APNs can decode and read a
device token
My app was getting a new device token each time because I was not converting the datatype of the the deviceToken from Data to string properly. APNS sends the device token as type Data but to pass the it to Azure, I needed to first convert the token to a string with the following function:
private func convertDataToString(data:NSData) -> String {
let singleChar = UnsafePointer<CChar>(data.bytes)
var tokenbuilder = String()
if data.length > 0
{
for index in 0...data.length - 1
{
tokenbuilder += String(format: "%02.2hhx", arguments: [singleChar[index]])
}
return tokenbuilder
}
else
{
return ""
}
}
In my case, I had forgotten that the position of the first char starts at 0 (not 1) and thus, the original for loop went from 0 to data.length (instead of data.length - 1), causing the function to return a string with random extra characters. Even with a string that had extra characters, the app did receive push notifications; however, we also had a significant number of invalid tokens in the Azure log. This also caused the user to receive a new deviceToken every time he/she launched the app (and registered for a device token). Once I fixed the for loop, APNS started returning the same device token to the user as long as the app wasn't deleted (thus, deleting device data) and reinstalled onto my device; thus, creating a "unique app and device combination".
Related
I've had various struggles with push notifications over the course of my app that seem to be fixed by uninstalling. I believe I've narrowed it down to expired deviceTokens.
Reading Apple's Push notification documentation, I found this:
Registration Succeeded But No Notifications Received
. . .
Your app may have sent an incorrect device token to your provider.
Your app should always ask for the device token by registering with
the push service each time it is launched. Don't store a device token
from your app and try to reuse it, because the token can change. Your
provider should then pass that same token on to the push service.
They suggest registering each time the app is launched. They also suggest best practice for push notifications is not to do that, since users don't like getting bombarded with access requests before even getting to see your app. So, just throwing the registration call into the app delegate isn't the best option. However, I am not seeing any more info on when a deviceToken expires or how to see if it has expired.
The closest thing I can find is this documentation on UIApplication's instance method isRegisteredForRemoteNotifications:
Return Value YES if the app is registered for remote notifications and
received its device token or NO if registration has not occurred, has
failed, or has been denied by the user.
My understanding is that this is the method to call to check if a user has enabled push notification services. I understand that permissions for specific notification types could all be turned off and this could still be true if the user allows push notifications. But the wording looks like it requires the app to be registered with the current deviceToken. Does this mean that I could call
[[UIApplication currentApplication] isRegisteredForRemoteNotifications]
inside the appDelegate, and if it's true, register remote notifications to update my deviceToken on my server and make sure my push notifications don't expire? Or, is this function going to run into the same thing that I currently am, where eventually the deviceToken is going to expire and this method will begin to return false instead of true, even though the user had allowed push notification?
tl;dr - Apple says deviceTokens eventually expire for push notifications. They suggest registering every time the app launches. I don't want to bombard new users with that alert. How do I ensure only users who have already accepted push notifications get re-registered?
The alert will only be shown once.
If the user previously Allowed notifications, your code will get the new token without needing any interaction from (or UI notice to) the user.
OK, the best practice for this are as follows, which we follow without failure
Always register for the Push on app launch, or in suitable AppDelegate lifecycle methods, using the following code
[[UIApplication sharedApplication] registerUserNotificationSettings: [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound) categories:nil]];
[[UIApplication sharedApplication] registerForRemoteNotifications];
The delegate for Push if it is successfully registered will get called with deviceToken
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
The delegate above will get called if there is change in deviceToken always. So whenever you get a deviceToken update it in the server.
In the server make sure you hit correct mode if you are running in Sandbox, or Production mode, discard values from Simulators they normally fails and block the service for sometime.
You can always check with your pem file and device token that Push is working or not with this online service.
Hope it helps.
Cheers.
I am developing an iOS app in which i have implemented Push Notification.
Everything is working fine.
But just wish to ask if device Token for my Apple device will ever change??
Also do we need internet connectivity for generating device token.
Thanks
device Token for my Apple device will ever change
-- YES. If you restores backup data to a new device or reinstall the operating system, the device token changes. So my suggestion is to update the server with token
do we need internet connectivity for generating device token
-- as far as I know, YES. When you register user, you call method for registration for push notification. This on successful registration call the delegate method -
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
indicating you are registered successfully for a push notification or on failure it calls -
- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
indicating failed to register for notification.
You can check it by turning off network and running your application.
Based on the Apple Documentation, the answer is YES:
The form of this phase of token trust ensures that only APNs generates
the token which it will later honor, and it can assure itself that a
token handed to it by a device is the same token that it previously
provisioned for that particular device—and only for that device.
If the user restores backup data to a new device or reinstalls the
operating system, the device token changes.
From the apple docs -
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.
Never cache a device token; always get the token from the system whenever you need it. If your app previously registered for remote notifications, calling the registerForRemoteNotifications method again does not incur any additional overhead, and iOS returns the existing device token to your app delegate immediately. In addition, iOS calls your delegate method any time the device token changes, not just in response to your app registering or re-registering.
For more APNS Doc
I was recently troubleshooting an issue with push notifications for a user. I was a bit confused by the guidance here, indicating that the token would only change in rare circumstances, such as "moving to a new device" or "re-installing the OS".
While the above events are likely valid events where the token is updated, I also see the token is updated simply when the user updates the OS on their device.
For example:
When my device runs iOS 15.4.1, and I install an application and request a push notification token, I'm provided with token "A".
After I update my device to iOS 15.7, with the same application still installed, and request a push notification token, I'm provided with token "B".
Therefore while rare events like restoring backup data to a new device or re-installing the OS are valid for this case, more frequent events such as a user simply updating the OS of their device are also valid for causing the push notification token to change.
Thus, this will likely change quite frequently and should always be requested from the device and updated on your server.
I would like to add support for sending Apple Push Notifications to a iOS app. This is a news app, so every time some important news event has occurred, then I would like to broadcast a push notification to all users and devices where the app is installed.
According to the Apple Push Notification Service documentation, in Figure 3-3 the Client App must send the device token to the Provider.
In order to be able to send Push Notifications to all devices where my app is installed, do I have to create a webservice which receives and stores device tokens from the client apps when they register for notifications? In order to send a push notification, I need a token and the payload. I want to send a push notification to all users, typically many thousand users. So how can I best get access to the device tokens so that I can send the push notification to all users?
Yes. You will have to make a web service for sending the push notifications to any of the devices that are using your app. As far as the device tokens are concerned, you can get them through your code using this :
- (void)applicationDidFinishLaunching:(UIApplication *)app {
// other setup tasks here....
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];
}
// Delegation methods
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken {
const void *devTokenBytes = [devToken bytes];
self.registered = YES;
[self sendProviderDeviceToken:devTokenBytes]; // custom method
}
- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {
NSLog(#"Error in registration. Error: %#", err);
}
You need to write this in your AppDelegate.m, this will fetch the device token needed to send the push notifications.
Also, you can store those tokens on some Database server for further usage in sending notifications. You can use these tokens the next time when you want to send some notifications to selected or all of the users.
I have a live app. in apple store that implements a push notification procedure. As known I communicate with my server to send and save device tokens.
My server is receiving device tokens correctly for some devices(Requests) and also receives null values for some other devices.
How is that could be possible in any case?!
Then you have a bug in your code, the device never generates a null token, or you have failed to implement the following delegate:
- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err
If this delegate gets called then you could/should implement a retry algorithm that retries to re-register for the APN token a few times.
When you install an app for the first time and want to register for Push notifications, the app asks you whether you want to receive alerts or not. This is being permanently saved in the settings, even after deletion of the app.
Basically, to save the token we are doing this:
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSString *token = [[[deviceToken description]
stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"<>"]]
stringByReplacingOccurrencesOfString:#" " withString:#""];
[[NSUserDefaults standardUserDefaults] setValue:token forKey:kDeviceTokenKey];
}
But the problem is, NSUserDefaults are wiped when the app is removed from the device, but the push settings are not. So it won't ask again, thus don't call the delegate method again, thus I don't have the token anymore, but push is still activated.
Is there any chance to get the token back in the described scenario?
From Push Notification Programming Guide
An application should register every time it launches and give its
provider the current token. It calls the
registerForRemoteNotificationTypes: method to kick off the
registration process.
By requesting the device token and passing it to the provider every
time your application launches, you help to ensure that the provider
has the current token for the device. If a user restores a backup to a
device or computer other than the one that the backup was created for
(for example, the user migrates data to a new device or computer), he
or she must launch the application at least once for it to receive
notifications again. If the user restores backup data to a new device
or computer, or reinstalls the operating system, the device token
changes. Moreover, never cache a device token and give that to your
provider; always get the token from the system whenever you need it.
If your application has previously registered, calling
registerForRemoteNotificationTypes: results in the operating system
passing the device token to the delegate immediately without incurring
additional overhead.
To answer your question: Call registerForRemoteNotificationTypes: on every launch, and use the latest token.
call registerForRemoteNotificationTypes on every launch of your application so your didRegisterForRemoteNotificationsWithDeviceToken method get call and you will get your device token every time from APNS. And device token for your application is same on every launch.