18

I need device token to implement push notification in my app.
How can I get device token since didRegisterForRemoteNotificationsWithDeviceToken method is not working on iOS 8. I tried this code in app delegate but it is not giving me device token.

    [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
    [[UIApplication sharedApplication] registerForRemoteNotifications];
Pankaj
  • 397
  • 1
  • 3
  • 13
  • You could have easily found this in the iOS 8 documentation: [UIUserNotificationSettings](https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIUserNotificationSettings_class/index.html#//apple_ref/occ/cl/UIUserNotificationSettings) – rckoenes Jun 26 '14 at 08:52
  • i registered for UIUserNotificationSettings but how it will give me device token. – Pankaj Jun 26 '14 at 08:57
  • 1
    Did you implement the `application:didRegisterUserNotificationSettings:`? It is in the documentation! – rckoenes Jun 26 '14 at 10:53

8 Answers8

56

Read the code in UIApplication.h.

You will know how to do that.

First:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

add Code like this

#ifdef __IPHONE_8_0
  //Right, that is the point
    UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIRemoteNotificationTypeBadge
|UIRemoteNotificationTypeSound
|UIRemoteNotificationTypeAlert) categories:nil];
    [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
#else
    //register to receive notifications
    UIRemoteNotificationType myTypes = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound;
    [[UIApplication sharedApplication] registerForRemoteNotificationTypes:myTypes];
#endif

if you not using both Xcode 5 and Xcode 6 ,try this code

if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) {
  UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIRemoteNotificationTypeBadge
      |UIRemoteNotificationTypeSound
      |UIRemoteNotificationTypeAlert) categories:nil];
  [application registerUserNotificationSettings:settings];
} else {
  UIRemoteNotificationType myTypes = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound;
  [application registerForRemoteNotificationTypes:myTypes];
}

(Thanks for @zeiteisen @dmur 's remind)


Second:

Add this Function

#ifdef __IPHONE_8_0
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
    //register to receive notifications
    [application registerForRemoteNotifications];
}

- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void(^)())completionHandler
{
    //handle the actions
    if ([identifier isEqualToString:@"declineAction"]){
    }
    else if ([identifier isEqualToString:@"answerAction"]){
    }
}
#endif

And your can get the deviceToken in

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken

if it still not work , use this function and NSLog the error

-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
Community
  • 1
  • 1
Madao
  • 3,756
  • 1
  • 22
  • 16
  • Thanks for your answer. I will check that and let you know. – Pankaj Jun 30 '14 at 12:28
  • Thank you, this worked for me but, shouldn't it be `UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert` instead of `UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert`? – Juan de la Torre Aug 20 '14 at 18:52
  • 10
    This will crash on iOS versions below iOS 8. #ifdef is a COMPILER function, and is evaluated at compile time. You still need a run-time check! – Dan VanWinkle Aug 26 '14 at 14:58
  • 2
    You could possibly use `if (([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0 && [[[UIDevice currentDevice] systemVersion] floatValue] < 9.0))` inside the method – cannyboy Oct 15 '14 at 08:40
  • @Madao Which delegate method will hit when push notification reaches device and app is in background.Whether push notification really awakes the app, when it is in background? – user2533604 Jan 03 '15 at 07:28
  • 2
    I tried this but (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken is not being called for some reason on iOS 8. (At least none of my commands, including NSLog are going through.) Any possible solution? – John Mar 31 '15 at 07:29
  • @John Note that this is not working on the simulator. – CedricSoubrie Oct 21 '15 at 10:45
11

Appending a small validation to @Madao's response in case you are having crashes on older iOS versions:

#ifdef __IPHONE_8_0
if(NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_7_1) {
    UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert) categories:nil];
    [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
}
#endif

UIRemoteNotificationType myTypes = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound |     UIRemoteNotificationTypeNewsstandContentAvailability;
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:myTypes];

What the __IPHONE_8_0 macro does is just allowing you to compile in older versions of xCode/iOS, you don't get compilation errors or warnings, but running the code on devices with iOS 7 or lower will cause a crash.

Juan de la Torre
  • 1,297
  • 9
  • 19
  • This is what I was just about to comment on Madao's post. – Dan VanWinkle Aug 26 '14 at 14:56
  • 2
    I did this and it works, if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) { UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeSound | UIUserNotificationTypeAlert categories:nil]; [application registerUserNotificationSettings:settings]; } else { [application registerForRemoteNotificationTypes: UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound]; } – zumzum Sep 19 '14 at 04:06
3

To get Device token in iOS8 +

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//This code will work in iOS 8.0 xcode 6.0 or later
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
{
    [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
    [[UIApplication sharedApplication] registerForRemoteNotifications];
}
else
{
    [[UIApplication sharedApplication] registerForRemoteNotificationTypes: (UIRemoteNotificationTypeNewsstandContentAvailability| UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
}
return YES;
}

- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
NSString* deviceToken = [[[[deviceToken description]
                         stringByReplacingOccurrencesOfString: @"<" withString: @""]
                         stringByReplacingOccurrencesOfString: @">" withString: @""]
                         stringByReplacingOccurrencesOfString: @" " withString: @""] ;
NSLog(@"Device_Token     -----> %@\n",deviceToken);
}
ChenSmile
  • 3,401
  • 4
  • 39
  • 69
1

2020 solution

Six steps,

1. library

enter image description here

2. add to AppDelegate

// APNs:

func application(_ application: UIApplication,
      didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    print(">> getting an APNs token works >>\(deviceToken)<<")
    let tokenAsText = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
}

func application(_ application: UIApplication,
      didFailToRegisterForRemoteNotificationsWithError error: Error) {
    print(">> error getting APNs token  >>\(error)<<")
}

3. in your app

For example, after user logon

import UserNotifications

and

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    askNotifications()
}

func askNotifications() {
    let n = UNUserNotificationCenter.current()
    n.requestAuthorization(options: [.alert, .sound, .badge]) {
        granted, error in
        print("result \(granted) \(String(describing: error))")

        guard granted else {
            print("not granted!")
            return
        }

        DispatchQueue.main.async {
            UIApplication.shared.registerForRemoteNotifications()
        }
    }
}

4. capabilities tab

do three items

enter image description here

beware of old articles: there is no longer an "on switch" there.

5. tethered phone, no simulator

it will not work in simulator. must be a phone

enter image description here

tethered phone is fine, console will work fine

6. and finally ... WIFI DANCE

With 100% repeatability, as of 2020 you MUST do the following:

  1. completely erase app from tethered phone
  2. build to the phone and run from Xcode (it definitely won't work)
  3. force quit the app
  4. completely erase app from tethered phone
  5. turn off both wifi/cell
  6. build to the phone and run from Xcode (obviously it won't work)
  7. force quit the app

then in exactly this order:

  1. completely erase app from tethered phone
  2. turn on connectivity (either wifi or cell is fine - no problem)
  3. build to the phone and run from Xcode

It now works. (And will work from here onwards.)

The WIFI DANCE is just one of those weird Apple things. They have not fixed it yet (Xcode 11.3). In fact it has become "100% repeatable": you have to do the WIFI DANCE.

Fattie
  • 27,874
  • 70
  • 431
  • 719
0

Aside from the rest of the condescending answers to this question, the most likely reason that this issue might occur for an informed developer who has implemented all the required delegate methods is that they are using a wild card provisioning profile (why wouldn't you? It makes it so easy to create and test development apps!)

In this case, you'll probably see the error Error Domain=NSCocoaErrorDomain Code=3000 "no valid 'aps-environment' entitlement string found for application"

In order to test notifications, you actually have to go back to 2000 and late, log into developer.apple.com, and set up your application-specific provisioning profile with push notifications enabled.

  1. Create an App ID corresponding to your app's bundle identifier. Be sure to check off "push notifications" (currently 2nd option from the bottom).
  2. Create a provisioning profile for that App ID.
  3. Go through the rest of the horrible provisioning steps we'd all love to forget.
  4. ?
  5. Profit!
dave
  • 1,150
  • 1
  • 13
  • 22
0

On your developer account make sure you have your push notifications setup properly in your app ID and then you need to regenerate and download your provisioning profile. My problem was that I had downloaded the provisioning profile but xcode was running the incorrect one. To fix this go to your Target Build Settings, scroll down to Code Signing, under the provisioning profile section make sure that you are using the correct provisioning profile that matches the name of the one you generated (there should be a dropdown with options if you have installed more than one).

Trianna Brannon
  • 1,246
  • 11
  • 12
0

@madoa answer is absolutely correct. Just note that it is not working in the simulator.

In this case

-(void)application:(UIApplication *)application
     didFailToRegisterForRemoteNotificationsWithError:(NSError *)error

is called with a error REMOTE_NOTIFICATION_SIMULATOR_NOT_SUPPORTED_NSERROR

Avijit Nagare
  • 8,482
  • 7
  • 39
  • 68
CedricSoubrie
  • 6,657
  • 2
  • 39
  • 44
0

Here is the solution.

in applicationDidFinishLaunchingWithOptions:

{

 UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
    UIUserNotificationSettings *mySettings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
    [[UIApplication sharedApplication] registerUserNotificationSettings:mySettings];
}


- (void)application:(UIApplication*)application didRegisterUserNotificationSettings:(nonnull UIUserNotificationSettings *)notificationSettings
{
    [application registerForRemoteNotifications];
}


- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(nonnull NSError *)error
{
    NSLog(@" Error while registering for remote notifications: %@", error);
}

- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(nonnull NSData *)deviceToken
{
  NSLog(@"Device Token is : %@", deviceToken);
}
YSR fan
  • 705
  • 6
  • 11