Can worklight return the device token for Android/iPhone/BB and if so how?
More specifically, I'm not looking for the "device id" but the native device token.
Worklight can return the "device id", but this is different than the device token. For example Worklight: How to get current device ID for Push subscription states how to get the "device id" using the call
WL.Client.getUserInfo("wl_deviceNoProvisioningRealm", "userId");
Unfortunately this returns something different than the device token. When using the native iPhone call like so and comparing it to the WL deviceid it's obvious they are different.
- (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSMutableDictionary *results = [NSMutableDictionary dictionary];
NSString *token = [[[[deviceToken description] stringByReplacingOccurrencesOfString:#"<"withString:#""]
stringByReplacingOccurrencesOfString:#">" withString:#""]
stringByReplacingOccurrencesOfString: #" " withString: #""];
[results setValue:token forKey:#"deviceToken"];
#if !TARGET_IPHONE_SIMULATOR
[results setValue:[[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleDisplayName"] forKey:#"appName"];
[results setValue:[[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleVersion"] forKey:#"appVersion"];
NSUInteger rntypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
// Set the defaults to disabled unless we find otherwise...
NSString *pushBadge = #"disabled";
NSString *pushAlert = #"disabled";
NSString *pushSound = #"disabled";
if(rntypes & UIRemoteNotificationTypeBadge){
pushBadge = #"enabled";
}
if(rntypes & UIRemoteNotificationTypeAlert) {
pushAlert = #"enabled";
}
if(rntypes & UIRemoteNotificationTypeSound) {
pushSound = #"enabled";
}
[results setValue:pushBadge forKey:#"pushBadge"];
[results setValue:pushAlert forKey:#"pushAlert"];
[results setValue:pushSound forKey:#"pushSound"];
// Get the users Device Model, Display Name, Token & Version Number
UIDevice *dev = [UIDevice currentDevice];
[results setValue:dev.name forKey:#"deviceName"];
[results setValue:dev.model forKey:#"deviceModel"];
[results setValue:dev.systemVersion forKey:#"deviceSystemVersion"];
[self successWithMessage:[NSString stringWithFormat:#"%#", token]];
#else
[self successWithMessage:[NSString stringWithFormat:#"%#", #"simulator generated"]];
#endif
}
Moreover, the native device token is needed for a third party notification platform which is outside of worklight and so using worklights messaging system isn't feasible.
You're correct APNs device token and Worklight deviceId is two different things. In case you require APNs device token for using some 3rd party notification platform you can override the didRegisterForRemoteNotificationsWithDeviceToken method in your application delegate thus receiving full control over the device token once it arrives from APNs
Related
I have implemented push notifications in a project, and I am getting the push notification tokens correctly. I am sending this tokens with login api.
-(void)application:(UIApplication* )application didRegisterForRemoteNotificationsWithDeviceToken:(NSData* )deviceToken
{
NSString *devToken = [[deviceToken description] stringByTrimmingCharactersInSet:
[NSCharacterSet characterSetWithCharactersInString:#"<>"]];
devToken = [devToken stringByReplacingOccurrencesOfString:#" " withString:#""];
NSLog(#"token: %#",devToken);
self.pushNotificationToken = devToken;
[[NSUserDefaults standardUserDefaults] setObject:devToken forKey:#"PushToken"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
I am getting token perfectly. But some time I think it may take some more time to generate a token. Because of the login api does not contain token. I have checked database stored the token. Some time it is NULL. When I logout and login then the database contains the token and works perfectly. What is the perfect way to sending push token to server?
after experimenting this scenario push token generation may take anytime.There is not specific time duration. Mostly it will generate when app launches but we can not ensure.So better call api when we get token in delegate
-(void)application:(UIApplication* )application didRegisterForRemoteNotificationsWithDeviceToken:(NSData* )deviceToken
{
NSString *devToken = [[deviceToken description] stringByTrimmingCharactersInSet:
[NSCharacterSet characterSetWithCharactersInString:#"<>"]];
devToken = [devToken stringByReplacingOccurrencesOfString:#" " withString:#""];
NSLog(#"token: %#",devToken);
//Call API to Update Device Token
}
Call the api to update token when token generated.
This question already has answers here:
UIDevice uniqueIdentifier deprecated - What to do now?
(32 answers)
Closed 5 years ago.
I am creating an ios app, since the udid is deprecated How can we uniquely identify the app.
You can use both UDID & UUID
For UUID- let uuid = NSUUID().uuidString
For UDID- let Identifier = UIDevice.current.identifierForVendor?.uuidString
Below Code Generate Unique ID for device and IT IS UNIQUE upto you will RESET the device. Happy coding...:)
- (NSString *)createNewUUID {
NSString *UUID = [[NSUUID UUID] UUIDString];
return UUID;
}
//Check And Retrive UniqueIdentifier
-(void)checkAndGenerateUniqueIdentifier{
//Retrive UDID
NSString *retrieveuuid = [SSKeychain passwordForService:[[NSBundle mainBundle] bundleIdentifier] account:[[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleNameKey]];
if (retrieveuuid == nil) {
NSString *uuid = [self createNewUUID];
NSLog(#" Create new uuid : %#",uuid);
//Store the password in Keychain
NSError *error = nil;
[SSKeychain setPassword:uuid forService:[[NSBundle mainBundle] bundleIdentifier] account:[[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleNameKey] error:&error];
if ([error code] == SSKeychainErrorNotFound) {
NSLog(#"Password not found");
}
}
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//Check And Retrive UniqueIdentifier
[self checkAndGenerateUniqueIdentifier];
return YES;
}
Use Third party Keychain wrapper or iOS Keychain API for Store UUID.
I have some problems receiving push notifications under IOS.
the didReceiveRemoteNotification fires but somewhere my code crashesh in the NSNotificationCenter. That's not the problem but basically I even don't receive nothing on the phone. Is the didReceiveRemoteNotification implementation correct ? or I missed something ?
This is what I send:
"aps":[],"custom":["hello IOS","0","48.213822","16.389186","1.39593410094E+12",null]}
And this the source.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
if (isAttentionViewOpen) {
NSLog(#"Sent Notification to view!");
[[NSNotificationCenter defaultCenter] postNotificationName:#"PushIn" object:self userInfo: userInfo];
}
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken {
//#if !TARGET_IPHONE_SIMULATOR
NSString *appName = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleDisplayName"];
NSString *appVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleVersion"];
NSUInteger rntypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
NSString *pushBadge = #"disabled";
NSString *pushAlert = #"disabled";
NSString *pushSound = #"disabled";
if(rntypes == UIRemoteNotificationTypeBadge){
pushBadge = #"enabled";
}
else if(rntypes == UIRemoteNotificationTypeAlert){
pushAlert = #"enabled";
}
else if(rntypes == UIRemoteNotificationTypeSound){
pushSound = #"enabled";
}
else if(rntypes == ( UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert)){
pushBadge = #"enabled";
pushAlert = #"enabled";
}
else if(rntypes == ( UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)){
pushBadge = #"enabled";
pushSound = #"enabled";
}
else if(rntypes == ( UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)){
pushAlert = #"enabled";
pushSound = #"enabled";
}
else if(rntypes == ( UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)){
pushBadge = #"enabled";
pushAlert = #"enabled";
pushSound = #"enabled";
}
// Get the users Device Model, Display Name, Unique ID, Token & Version Number
UIDevice *dev = [UIDevice currentDevice];
CFUUIDRef uuidObject = CFUUIDCreate(kCFAllocatorDefault);
NSString *uuidStr = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, uuidObject);
CFRelease(uuidObject);
deviceUuid = uuidStr;// dev.uniqueIdentifier;
NSString *deviceName = dev.name;
NSString *deviceModel = dev.model;
NSString *deviceSystemVersion = dev.systemVersion;
// Prepare the Device Token for Registration (remove spaces and < >)
NSString *deviceToken = [[[[devToken description]
stringByReplacingOccurrencesOfString:#"<"withString:#""]
stringByReplacingOccurrencesOfString:#">" withString:#""]
stringByReplacingOccurrencesOfString: #" " withString: #""];
// SENT IT TO SERVER
Since your aps does not hold any data not notification is shown to the user.
The -application:didReceiveRemoteNotification is only called if your app is in the foreground, not when it is in the background.
For this you need to use application:didReceiveRemoteNotification:fetchCompletionHandler:, but this is only available on iOS 7.
If you want to handle push notification in the background then implement the above mentioned method and register that you app want to do remote-notification in the background.
There are many thing to keep in mind when doing this, see the comment in the Apple documentation of application:didReceiveRemoteNotification:fetchCompletionHandler:
Implement this method if your app supports the remote-notification
background mode. This method is intended as a means for apps to
minimize the time that elapses between the user seeing a push
notification and the app displaying the associated data. When a push
notification arrives, the system displays the notification to the user
and launches the app in the background (if needed) so that it can call
this method. Use this method to download any data related to the push
notification. When your method is done, call the block in the handler
parameter.
Unlike the application:didReceiveRemoteNotification: method, which is called only when your app is running, the system calls this method regardless of the state of your app. If your app is suspended or not running, the system wakes up or launches your app and puts it into the background running state before calling the method. If the user opens your app from the system-displayed alert, the system calls this method again so that you know which notification the user selected.
When this method is called, your app has up to 30 seconds of wall-clock time to perform the download operation and call the specified completion handler block. In practice, your app should call the handler block as soon as possible after downloading the needed data. If you do not call the handler in time, your app is terminated. More importantly, the system uses the elapsed time to calculate power usage and data costs for your app’s background downloads.
My published application makes use of the CFUUID and SSKeychain in order to identify the device (and to keep that ID unchanged even if the app is uninstalled and reinstalled)
I save those device ID in the server, and I recently noticed that some users have several of those IDs for the same real device. The only explanation I see is that the ID is not being saved or loaded from the Keychain and thus the device generates a new one. The strange thing is that it works fine on some other devices running the same iOS version.
Any ideas on what could be going on?
This is my related code in (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
NSString* identifier = #"appName";
NSString* serviceName = #"com.company.appName";
NSString *retrieveuuid = [SSKeychain passwordForService:serviceName account:identifier];
if (retrieveuuid == nil) {
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
NSString *uuid = (NSString*) string;
[SSKeychain setPassword:uuid forService:serviceName account:identifier];
}
EDIT: My guess is that retrieveuuid == nil is not working as expected for some reason. Later in the app I register for push notifications and send the push token to the server together with this CFUUID that I read with the same exact line [SSKeychain passwordForService:serviceName account:identifier] but when it is sent to the server it is not nil (So I can see several CFUUIDs with the same push token).
EDIT 2 to attach more actual code.
AppDelegate.m
NSString* identifier = #"appName";
NSString* serviceName = #"com.company.appName";
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//Creating UUID
NSString *retrieveuuid = [AppDelegate getDeviceId];
if (retrieveuuid == nil) {
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
NSString *uuid = (NSString*) string;
[SSKeychain setPassword:uuid forService:serviceName account:identifier];
}
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
}
+ (NSString*) getDeviceId {
return [SSKeychain passwordForService:serviceName account:identifier];
}
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
NSString *newToken = [deviceToken description];
newToken = [newToken stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"<>"]];
newToken = [newToken stringByReplacingOccurrencesOfString:#" " withString:#""];
_deviceToken = [[NSString alloc] initWithString:newToken];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *user = [prefs objectForKey:#"appNameUsername"];
if(user && ![user isEqualToString:#""]){
RestClient *rest = [[RestClient alloc] init];
rest.delegate = self;
rest.tag = 2;
[rest updateToken:newToken ForUsername:user];
[rest release];
}
}
RestClient.m
-(void) updateToken:(NSString *)token ForUsername:(NSString *)userName{
NSArray* info = [NSArray arrayWithObject: [NSMutableDictionary dictionaryWithObjectsAndKeys:
userName, #"Username",
token, #"TokenNo",
[[UIDevice currentDevice].model hasPrefix:#"iPad"] ? #"iPad" : #"iPhone", #"Device",
[UIDevice currentDevice].systemVersion, #"OSVersion",
[AppDelegate getDeviceId], #"DeviceID",
#"updateToken", #"CMD",
nil]];
[self doAction:info];
}
The doAction method just sends the data to the server and then callback the delegate, that part works fine. I can see on the server the logs of receiving this command:
"JSONCMD":"[
{ "TokenNo" : "f0d3aa21758350333b7e6315c38_EDIT_257c1838f49c43049f8380ec1ff63",
"AppVersion" : "1.0.4",
"Username" : "user#server.com",
"CMD" : "updateToken",
"OSVersion" : "7.0.4",
"DeviceID" : "9B794E11-6EF7-470C-B319-5A9FCCDAFD2B",
"Device" : "iPhone"
}
]
I see 2 possible candidates causing the strange behaviour, the NSStrings in the controller body and the static getDevice method. However, I don't see how this could work in many devices but fail in others.
I've had the same problem and I've found the solution. The Problem occurs with devices where user has the passcode set to unlock the phone. On iOS7 some code can run in the background (push notifications, background fetch) and if you save something in the keychain with standard accessibility type, you cannot read it when the device is locked (and the app is running in the background). Try to set this:
[SSKeychain setAccessibilityType:kSecAttrAccessibleAlways];
before you read/write the keychain.
Hope it helps.
NSString *retrieveuuid = [SSKeychain passwordForService:serviceName
account:identifier];
The retrieveuuid depends with the service, witch is always the same thing, but it depends also with the account. I think your problem is that one user can have multiple accounts in the same device, then your code are generating a UUID for each account.
I am using the APNS for receiving push notifications in my application.
The problem is that I am getting the same notifications on some devices but not all. What could the problem be here since I have been trying for about 15 days to solve this issue with no success. Th device token is updating successfully. Because had it not been then I wouldn't have been getting notification on any device. But the strange thing is I am getting it on half the devices. Please help!!
Here is the code for registering and receiving notifications. I don't have any code for server side. But as I said that the notifications are working on some devices. On android too they are working.
I have 3 devices here with me and its working on two of them.
iPad2:5.0.1
iPodTouch:4.3.3
Its not working on another iPod touch that I have on version:5.1
Also its showing successfully registered for APNS in all devices. But not sending notifications on some. What can the issue be? Is there something that I am missing?
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken
{
deviceToken = [devToken retain];
NSLog(#"Registered for APNS %#", deviceToken);
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSMutableString *dev = [[NSMutableString alloc] init];
NSRange r;
r.length = 1;
unsigned char c;
for (int i = 0; i < [deviceToken length]; i++)
{
r.location = i;
[deviceToken getBytes:&c range:r];
if (c < 10) {
[dev appendFormat:#"0%x", c];
}
else {
[dev appendFormat:#"%x", c];
}
}
[ud setObject:dev forKey:#"DeviceToken"];
[ud synchronize];
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
NSLog(#"Failed to register %#", [error localizedDescription]);
deviceToken = nil;
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
for(int i=0;i<[viewControllers count];i++)
{
if([[viewControllers objectAtIndex:i] isKindOfClass:[Confirmation class]])
{
Confirmation *map = (Confirmation*)[[self.navigationController viewControllers] objectAtIndex:i];
[map setFinalInfo];
[self.navigationController popToViewController:[[self.navigationController viewControllers] objectAtIndex:i] animated:YES];
}
}
}
1 ) There is no guarantee that push notifications will actually be delivered, even if the APNS server accepted them.
2 ) As far as your server is concerned, push notifications are fire-and-forget; there is no way to find out what the status of a notification is after you’ve sent it to APNS. The delivery time may also vary, from seconds up to half an hour.
3 ) Also, the user’s iPhone may not be able to receive push notifications all the time. They could be on a WiFi network that does not allow connections to be made to APNS because the required ports are blocked. Or the phone could be turned off.
4 ) APNS will try to deliver the last notification it received for that device when it comes back online, but it will only try for a limited time. Once it times out, the push notification will be lost forever!
Are you setting the "expiry" to 0 in the APN message? If you set it to 0 push messages are try-once-and-forget, else Apple may try to deliver them till the time specified.
The other thing to check might be to be absolutely certain no device id's from your development test runs end up in the list of device id's you are actually going to send push messages in production. One faulty device id and Apple will cease the SSL connection and not process any more APN messages.
I replaced the following code:
deviceToken = [devToken retain];
NSLog(#"Registered for APNS %#", deviceToken);
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSMutableString *dev = [[NSMutableString alloc] init];
NSRange r;
r.length = 1;
unsigned char c;
for (int i = 0; i < [deviceToken length]; i++)
{
r.location = i;
[deviceToken getBytes:&c range:r];
if (c < 10) {
[dev appendFormat:#"0%x", c];
}
else {
[dev appendFormat:#"%x", c];
}
}
[ud setObject:dev forKey:#"DeviceToken"];
[ud synchronize];
By this:
[devToken retain];
NSLog(#"~~~~devToken=%#",devToken);
NSString *dt = [[devToken description] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:#"<>"]];
dt = [dt stringByReplacingOccurrencesOfString:#" " withString:#""];
// //DeviceToken = dt;
NSUserDefaults *def = [NSUserDefaults standardUserDefaults];
[def setObject:dt forKey:#"DeviceToken"];
[def synchronize];
and its working fine now!!
The problem was it wasnt updating the device toekn correctly for some devices which I am not sure why as it was working well for some.
Thanks guys!!