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.
Related
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".
I am using Sinch with ManagedPush to receive notifications for VoIP via PushKit. In my app I have three possible states:
Online
Offline
"Not available for incoming calls, but I can make calls"
The user can change this state at any time during the App session.
Following documentation:
https://www.sinch.com/docs/voice/ios/#unregisterapushdevicetoken
I am using unregisterPushNotificationDeviceToken as well as stopListeningOnActiveConnection
This seems to work to get the person not to receive incoming calls and still having the client active to make calls. Where I am having issues is on setting the user back online. The active connection works, but I can't seem to be able to register again for the notifications, to receive the call when the app is in background.
Questions like Sinch SDK - How to logout user? don't solve how to put the user back online.
I have considered using the SinchService component https://github.com/sinch/SinchService-iOS but I can see from the code that the logout functionality terminates the client. And I am interested in not terminating the client, but stop receiving notifications and then receving them again on the same session.
What I have tried:
Not available for incomming calls button does:
[_client stopListeningOnActiveConnection];
[_client unregisterPushNotificationDeviceToken];
Online button does:
[_client startListeningOnActiveConnection];
[[UIApplication sharedApplication] registerForRemoteNotifications];
The reason I am calling registerForRemoteNotifications is to force the didRegisterForRemoteNotificationsWithDeviceToken delegate method to be called so I can call the Managed Push to register again for notifications. I thought that might do the trick but it didn't.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
[self.push application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
Most likely this doesn't work because this goes through PushKit and the way to register is different.
As workaround that works for me is recreating the client when the user switches to online status back:
[_client terminateGracefully];
_client = nil;
[self initSinchClientWithUserId:[BCDataProvider loggedInUser].callerId];
Is there any way I can get back on being registered for VoIP notifications, after a unregisterPushNotificationDeviceToken without having to recreate the client?
Thanks!!
If I am not wrong, basically you need to achieve VOIP, by online / offline etc you need to manage user to allow for call or not.
You can use socket architecture, which keeps broandcast user is online / offline, so on that basis you can manage to allow for call or not.
If you are working with VOIP, then below method has to be in use.
-(void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type
{
///tell the Sinch SDK about the push token so we can
///give that to users that want to call this user.
[_client registerPushNotificationData:credentials.token];
}
Below mentioned method will not be in use.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
}
Using push notification, will always provide you new unique device token, so once you unregistered for offline, then again for online you would not be able to register with same device token.
Using push kit( Silent push notification ) you will always get same device token registering after unregistered.
If user says no to push notification permission in the initial launch of application, will the APN device token be received by the application ?
There is a very important exception you should be aware of. If you enable the Background Mode "Remote Notifications", you will get a token from iOS regardless of the user's response to the permission question. You will be able to send silent push notifications to your app using this token (a push with the content-available key set to 1 in the 'aps' section of the json
silent notification means if your app is in the foreground you will get a hit in didReceiveRemoteNotification and if your app is in the background it will also get a call (in the background) to the same delegate function.
BUT - this will not be visible to the user. so it still gives you a way to communicate with your app, albeit a limited one.
No, you need to have the user permission to get the token
IMPORTANT EDIT: This answer is incorrect. Please look at the answer below. I cannot delete that as it is an accepted answer.
User must give permission for you to get the device token.
If user gives permission, - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken method is executed in AppDelegate and you'll get the device token.
If user refuses to give permission, - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error method is executed and you'll only get corresponding NSError object.
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 finished this tutorial here
http://mobiforge.com/developing/story/programming-apple-push-notification-services
and now i can send a push notification to my device using my application.
However i cannot figure out how to send push notifications not just to my specific device token but to all devices with my application.
You have to send the message to each device individually, i.e.: in a loop
Apples Push Notification servers only know to direct messages to specific devices.
It is the developer's responsibility to pass the tokens you receive from the
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
method to your server ( through an HTTP post for instance ) and keep track of all the existing tokens.
If/when you want to send a message to all the devices - you simply need to iterate all the existing device tokens.