31

I recently upgraded one of my test iphones to iOS 8 and then upgraded the PUSH registration code as below (using xCode 6)

-(BOOL)hasNotificationsEnabled {

    NSString *iOSversion = [[UIDevice currentDevice] systemVersion];
    NSString *prefix = [[iOSversion componentsSeparatedByString:@"."] firstObject];
    float versionVal = [prefix floatValue];


    if (versionVal >= 8)
    {

        NSLog(@"%@", [[UIApplication sharedApplication]  currentUserNotificationSettings]);
        //The output of this log shows that the app is registered for PUSH so should receive them

        if ([[UIApplication sharedApplication] currentUserNotificationSettings].types != UIUserNotificationTypeNone) {

            return YES;

        }

    }
    else
    {
        UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
        if (types != UIRemoteNotificationTypeNone){
            return YES;
        }

    }

    return NO;
}

-(void)registerForPUSHNotifications {

    NSString *iOSversion = [[UIDevice currentDevice] systemVersion];
    NSString *prefix = [[iOSversion componentsSeparatedByString:@"."] firstObject];
    float versionVal = [prefix floatValue];


    if (versionVal >= 8)
    {


            //for iOS8
        UIUserNotificationSettings *settings =
        [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert |
         UIUserNotificationTypeBadge |
         UIUserNotificationTypeSound categories:nil];
        [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
        [[UIApplication sharedApplication] registerForRemoteNotifications];


    }
    else
    {

            [[UIApplication sharedApplication] registerForRemoteNotificationTypes: (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];


    }
}

Despite this upgrade and the fact that [[UIApplication sharedApplication] currentUserNotificationSettings] shows PUSH is enabled for the device, I am not receiving PUSH notifications.

I am using Parse and doing everything by the book as far as they are concerned ( https://parse.com/tutorials/ios-push-notifications) .

Is anyone experiencing the same issue? Is there something else that I may be missing ?

Meet Doshi
  • 4,241
  • 10
  • 40
  • 81
Zigglzworth
  • 6,645
  • 9
  • 68
  • 107
  • 1
    It would be helpful to see the code that you are using to register for push notifications as the API has changed for iOS 8. See here: http://stackoverflow.com/questions/24049266/get-the-push-notification-list-on-ios8 – eremzeit Sep 18 '14 at 20:02
  • What do you mean? the code is there.. – Zigglzworth Sep 21 '14 at 08:11
  • Oh, weird. I didn't see that there was more code below. My bad. – eremzeit Sep 23 '14 at 01:57
  • Just make sure that you do not using test app with Production server. After 4 years in iOS development I still getting into this trap. – Mike Keskinov Feb 10 '15 at 01:40
  • This link worked for me..!http://stackoverflow.com/questions/4086599/why-didregisterforremotenotificationswithdevicetoken-is-not-called/26115072#26115072 –  Apr 12 '16 at 09:58

8 Answers8

67

The way to register for push notifications has been changed in iOS 8: Below is the code for all versions till iOS 9:

if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)])
{
    [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];

}
else
{
    [[UIApplication sharedApplication] registerForRemoteNotificationTypes:
     (UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert)];
}

In case you want to check whether push notifications are enabled or not use below code:

- (BOOL) pushNotificationOnOrOff
{
    if ([UIApplication instancesRespondToSelector:@selector(isRegisteredForRemoteNotifications)]) {
        return ([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]);
    } else {
        UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
        return (types & UIRemoteNotificationTypeAlert);
    }
}

#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

Above code will run on Xcode 6+ only...

Meet Doshi
  • 4,241
  • 10
  • 40
  • 81
Pankaj Wadhwa
  • 3,073
  • 3
  • 28
  • 37
  • 6
    The check should really use `respondsToSelector`, not the OS version. – Kevin Oct 10 '14 at 22:17
  • 1
    The answer could be wrong. in the 'else', it should be, UIRemoteNotificationTypeXXXXX instead of UIUserNotificationType****. UIUserNotificationType**** is not defined in earlier iOS'es and will crash the app. – karim Oct 13 '14 at 11:44
  • well @karim its 100% tested code and its work great.. I couldn't understand how people can down vote the answers without testing themselves.So its better to test first before down voting someone's answer... And for your kind information my app is live and push notifications are working on iOS 8 and before versions too. – Pankaj Wadhwa Oct 13 '14 at 12:55
  • 5
    Well, you know UIUserNotificationTypeBadge and so on are defined iOS 8.0. So xcode 5 will have compiler error. However, since its a constant it will work runtime in the devices less than 8.0. Its better to use old constants, [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)]; – karim Oct 13 '14 at 13:32
  • @karim hahaha people usually are. Well in answer's context i would like to say that best practice is to dump older stuff and adopt the newer one and would suggest you to do the same rather than down voting others answers for no stupid/no reasons. – Pankaj Wadhwa Oct 13 '14 at 13:44
  • @pankajwadhwa But I am getting compile time error for "registerUserNotificationSettings". How can I create a build? – Developer Oct 14 '14 at 17:56
  • @Developer make sure you are on Xcode 6. it's not suppose to give any type of error on Xcode 6. – Pankaj Wadhwa Oct 15 '14 at 05:22
  • @Developer if you are on Xcode 6 and getting error then deleting derived data may do the trick... – Pankaj Wadhwa Oct 15 '14 at 05:30
  • @Developer what was the problem?? – Pankaj Wadhwa Oct 15 '14 at 05:48
  • This was the main issue: http://stackoverflow.com/questions/26366453/which-xcode-to-be-used-to-upload-new-version-on-itunes-compatible-with-ios-8 But when I uploaded the build with XCode 6.0.1, it worked. Still surprised why it didn't work with XCode 6.1(Not Beta) – Developer Oct 15 '14 at 06:09
  • After above, follow these steps to reset notification setting: 1. Delete the apps who don't have Push notifications any more form the device. 2. Turn the device off completely and turn it back on. 3. Go to settings and set date and time ahead a day 4. Turn the device off again et turn it back 5. Install the apps again and It'll ask for notification like the first time you installed It. This will reset the notification settings. – Muzammil Dec 04 '14 at 11:36
  • 2
    The documentation says that you should only call registerForRemoteNotifications once didRegisterUserNotificationSettings is called – Daniel Galasko Feb 03 '15 at 08:08
12

Add this line at the top of your .m file

 #define IS_OS_8_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)

1) You have to put condition when you register for push notification in IOS8. add this code in application did finish launch.

if(IS_IOS_8_OR_LATER) {
    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];
 }

2) Then add this methods for IOS8

#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

Then you notification delegate method will be called. hope this will help you !!!

Nits007ak
  • 743
  • 7
  • 15
  • Using this method only works if you distribute for one OS version only. If you set your base version to 7 and distribution target to 8, this will crash on iOS 7; if you set your distribution target to 7, it won't work on 8. The correct method is a runtime check, as in penkaj's answer (though that currently uses the wrong runtime check, see my comment on it). – Kevin Oct 10 '14 at 22:22
  • yeah ok you can put if else condition then no issue. Thanx!! – Nits007ak Oct 13 '14 at 03:39
  • I edited the code now it will work for ios7 as well and yeah you have to add those old delegate methods of remote notification for below IOS8 to get callback whenever any push arrives. – Nits007ak Oct 13 '14 at 03:47
  • @Nits007ak, did you test push on real device ios 7? – tesmojones Oct 29 '14 at 05:10
  • @tesmojones yes I test its working fine...u need to add old delegate methods of remote notification didRegisterForRemoteNotificationsWithDeviceToken, didReceiveRemoteNotification for below IOS8 – Nits007ak Oct 30 '14 at 05:58
4

Runtime and old compiler safe options ... if you run in old xcode (5.0 or earlier)

    // changes of API in iOS 8.0
- (void) registerForPushNotification
{
    NSLog(@"registerForPushNotification");
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
    {

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0 //__IPHONE_8_0 is not defined in old xcode (==0). Then use 80000

        NSLog(@"registerForPushNotification: For iOS >= 8.0");

        [[UIApplication sharedApplication] registerUserNotificationSettings:
            [UIUserNotificationSettings settingsForTypes:
                (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge)
                                              categories:nil]];
        [[UIApplication sharedApplication] registerForRemoteNotifications];
#endif
    } else {
        NSLog(@"registerForPushNotification: For iOS < 8.0");
        [[UIApplication sharedApplication] registerForRemoteNotificationTypes:
         (UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];
    }
}
karim
  • 15,408
  • 7
  • 58
  • 96
4
if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)]) {
    // For iOS 8

[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];

 [[UIApplication sharedApplication] registerForRemoteNotifications];
} 

else 
{
    // For iOS < 8
    [[UIApplication sharedApplication] registerForRemoteNotificationTypes:
     (UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert)];
}

After this:

  1. Delete the apps who don't have Push notifications any more form the device.
  2. Turn the device off completely and turn it back on.
  3. Go to settings and set date and time ahead a day
  4. Turn the device off again and turn it back
  5. Install the apps again and It'll ask for notification like the first time you installed It.
  6. Reset your date / time again if not automatically set.

This will reset the notification settings. Re install the app and all will work fine :)

Muzammil
  • 1,529
  • 1
  • 15
  • 24
1

Try This if you have both iOS 8 and older versions:

if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)]) {
    // For iOS 8
    [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
    [[UIApplication sharedApplication] registerForRemoteNotifications];
} else {
    // For iOS < 8
    [[UIApplication sharedApplication] registerForRemoteNotificationTypes:
     (UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert)];
}

From Apple's Development site:

UIUserNotificationSettings

A UIUserNotificationSettings object encapsulates the types of notifications that can be displayed to the user by your app. Apps that use visible or audible alerts in conjunction with a local or push notification must register the types of alerts they employ. UIKit correlates the information you provide with the user’s preferences to determine what types of alerts your app is allowed to employ.

Use this class to encapsulate your initial registration request and to view the request results. After creating an instance of this class and specifying your preferred settings, call the registerUserNotificationSettings: method of the UIApplication class to register those settings. After checking your request against the user preferences, the app delivers the results to the application:didRegisterUserNotificationSettings: method of its app delegate. The object passed to that method specifies the types of notifications that your app is allowed to use.

In addition to registering your app’s alert types, you can also use this class to register groups of custom actions to display in conjunction with local or push notifications. Custom actions represent immediate tasks your app can perform in response to the notification. You define groups of actions and associate the entire group with a given notification. When the corresponding alert is displayed, the system adds buttons for each action you specified. When the user taps the button for one of the actions, the system wakes your app and calls the application:handleActionWithIdentifier:forRemoteNotification:completionHandler: or application:handleActionWithIdentifier:forLocalNotification:completionHandler: method of its app delegate. Use those methods to perform the requested action.

Sujith Thankachan
  • 3,508
  • 2
  • 20
  • 25
1

Its very simple:

Add following line in didFinishLaunchingWithOptions of your project in xcode6

 [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
 [[UIApplication sharedApplication] registerForRemoteNotifications];
0
The code below resolved:

if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
{
    UIUserNotificationType types;
    types = [[UIApplication sharedApplication] currentUserNotificationSettings].types;
    if (types & UIUserNotificationTypeAlert)
        pushEnabled=YES;
    else
        pushEnabled=NO;
}
else
{
    UIRemoteNotificationType types;
    types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
    if (types & UIRemoteNotificationTypeAlert)
       pushEnabled=YES;
    else
       pushEnabled=NO;

}
0

I tried to get the type information of the settings object and found out that the types property is basically a bitmask. Here is how you can extract the informations.

My example is in Swift and >= iOS 8.0 though.

    let settings = UIApplication.sharedApplication().currentUserNotificationSettings()

    if settings.types.rawValue & UIUserNotificationType.Alert.rawValue == UIUserNotificationType.Alert.rawValue
    {
       // can receive alert!
    }
    else
    {
       // if the user is not even able to receive alerts, show him a hint on how to reenable notifications in system settings
    }

    if settings.types.rawValue & UIUserNotificationType.Badge.rawValue == UIUserNotificationType.Badge.rawValue
    {
        // can receive badge!
    }
    if settings.types.rawValue & UIUserNotificationType.Sound.rawValue == UIUserNotificationType.Sound.rawValue
    {
        // can receive sound!
    }
Raphael
  • 3,846
  • 1
  • 28
  • 28