I am developing an iOS application in iOS 8 .That has one module called message sending. This module developed with help of Apple push notification. My issue is some time we are not getting Push message to iOS device .The server successfully sent the message to APNS. Is it any APNS reliability issue?
i am using the following steps:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if([[UIApplication sharedApplication] respondsToSelector:#selector(registerUserNotificationSettings:)])
{
UIUserNotificationSettings* notificationSettings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeSound categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
[[UIApplication sharedApplication] registerForRemoteNotifications];
}
return YES;
}
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
[application registerForRemoteNotifications];
}
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
}
- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
{
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
}
APNs are not reliable, they are not 100% guaranteed to reach the client.
As of Apple's documentation, APNs are best effort, so many times they might not reach.
Via answer
Samraan is not wrong, Apple does not guarantee delivery, however it is pretty reliable in my experience. What I do notice from the code you have posted here is that you have an empty implementation for didReceiveRemoteNotification. When your app is active, and it receives a push notification, there is no system banner or alert shown. Instead the notification is delivered to the didReceiveRemoteNotification callback for your app to handle directly. If you have an empty definition of this function and you send a notification to that device, it may appear like the notification never arrived, but instead it was just hidden.
You could try adding a UIAlertView popup to this callback to find out if this is what is happening in your case.
UPDATE: note i was using Xcode 6.01 and deploy using this code,which not working,but when i install Xcode 6.1 version and deployed the code it works...so its may be Xcode version issue my be it will help others.
i am using following code for push notification using PARSE.com it works fine in all device below ios8,
if (application.applicationState != UIApplicationStateBackground) {
// Track an app open here if we launch with a push, unless
// "content_available" was used to trigger a background push (introduced in iOS 7).
// In that case, we skip tracking here to avoid double counting the app-open.
BOOL preBackgroundPush = ![application respondsToSelector:#selector(backgroundRefreshStatus)];
BOOL oldPushHandlerOnly = ![self respondsToSelector:#selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)];
BOOL noPushPayload = ![launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (preBackgroundPush || oldPushHandlerOnly || noPushPayload) {
[PFAnalytics trackAppOpenedWithLaunchOptions:launchOptions];
}
}
Also i test with these condition (works fine with less then ios 8
if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
if ([application respondsToSelector:#selector(registerUserNotificationSettings:)]) {
UIUserNotificationType userNotificationTypes = (UIUserNotificationTypeAlert |
UIUserNotificationTypeBadge |
UIUserNotificationTypeSound);
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:userNotificationTypes
categories:nil];
[application registerUserNotificationSettings:settings];
[application registerForRemoteNotifications];
} else
endif
{
[application registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeAlert |
UIRemoteNotificationTypeSound)];
}
even iOS 8 enabled device not receiving PUSH notifications after code update not work for me
I set notification correctly and my bundle identifier set is correct too
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
{
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
[[UIApplication sharedApplication] registerForRemoteNotifications];
}
else
{
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert)];
}
"Attempting to schedule a local notification UIConcreteLocalNotification: with a sound but haven't received permission from the user to play sounds"
- (void)registerForRemoteNotifications
{
if ([[UIApplication sharedApplication] respondsToSelector:#selector(isRegisteredForRemoteNotifications)]) {
// iOS 8 Notifications
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeAlert|UIUserNotificationTypeSound|UIUserNotificationTypeBadge) categories:nil]];
} else{
// iOS < 8 Notifications
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIUserNotificationTypeAlert)];
}
}
//added for ios8
- (void)successfullyRegisteredUserNotificationSettings
{
[[UIApplication sharedApplication] registerForRemoteNotifications];
}
I followed the guide of other users in stackoverflow to use registerUserNotificationSettings for ios8. After invoking the function, I wait for the callback in AppDelegate:
- (void)application:didRegisterUserNotificationSettings:
In the function, I call my successfullyRegisteredUserNotificationSettings method above to formally register.
Even after doing this, I still get the error. Can someone tell me why? Or has anyone encountered the same issue? How can this be solved?
So this just adds to the numerous issues i've had with the latest iOS8 beta update. My app was working fine using parse to send push notifications. It was working great on my phone and on the iOS7 simulators. However now I'm not receiving the notifications at all. Parse is saying my phone is still registered which it is. The console is giving me the error "registerForRemoteNotificationTypes: is not supported in iOS 8.0 and later." Which is understandable however I believe my code solves that issue and until this update it was. I noticed as well when running the simulator that if my app is deleted from it and then I run the program again in the simulator. No dialog box is appearing asking if I want to accept push notifications, instead it is automatically setting it so they are accepted, it's doing it on my phone as well. As a side note this could potentially be a bug with the update as my other push notifications have been late/random today from other apps. However I would appreciate someone looking over the code to check please.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[UINavigationBar appearance] setBarTintColor:[UIColor colorWithRed:252/255.0f green:230/255.0f blue:17.0/255.0f alpha:1.0]];
[[UIToolbar appearance] setBarTintColor:[UIColor colorWithRed:252/255.0f green:230/255.0f blue:17.0/255.0f alpha:1.0]];
[Parse setApplicationId:#"***"
clientKey:#"***"];
// Register for push notifications
[application registerForRemoteNotificationTypes:
UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeAlert |
UIRemoteNotificationTypeSound];
#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
return YES;
}
#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
- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)newDeviceToken {
// Store the deviceToken in the current installation and save it to Parse.
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation setDeviceTokenFromData:newDeviceToken];
[currentInstallation saveInBackground];
}
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo {
[PFPush handlePush:userInfo];
}
UPDATE: I created a new app in Parse and copied the application IDs etc into it (missing the certificates stage of setting it up as they won't change right?) And now even though the app says in settings that it is registered to receive notifications, again without asking) Parse says that it is not.
Don't know how or why? But the pushes have started working again. Haven't changed any of the settings from what is above so if anyone is looking for a solution to using parse this works apparently!
For iOS8 to receive push notification take a look on this :
Not able to set Interactive Push Notifications on iOS8
The push notification is working and appearing, but the interactive buttons aren't working.
I think there's something related to the category..
If any one found something please update.
When trying to register for push notifications under iOS 8.x:
application.registerForRemoteNotificationTypes(UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound)
I get the following error:
registerForRemoteNotificationTypes: is not supported in iOS 8.0 and later.
Any ideas what is the new way of doing it? It does work when I run this Swift app on iOS 7.x.
EDIT
On iOS 7.x when I include the conditional code I get (either SystemVersion conditional or #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000)
dyld: Symbol not found: _OBJC_CLASS_$_UIUserNotificationSettings
For iOS<10
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
//-- Set Notification
if ([application respondsToSelector:#selector(isRegisteredForRemoteNotifications)])
{
// iOS 8 Notifications
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
[application registerForRemoteNotifications];
}
else
{
// iOS < 8 Notifications
[application registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)];
}
//--- your custom code
return YES;
}
For iOS10
https://stackoverflow.com/a/39383027/3560390
As you described, you will need to use a different method based on different versions of iOS. If your team is using both Xcode 5 (which doesn't know about any iOS 8 selectors) and Xcode 6, then you will need to use conditional compiling as follows:
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
if ([application respondsToSelector:#selector(registerUserNotificationSettings:)]) {
// use registerUserNotificationSettings
} else {
// use registerForRemoteNotificationTypes:
}
#else
// use registerForRemoteNotificationTypes:
#endif
If you are only using Xcode 6, you can stick with just this:
if ([application respondsToSelector:#selector(registerUserNotificationSettings:)]) {
// use registerUserNotificationSettings
} else {
// use registerForRemoteNotificationTypes:
}
The reason is here is that the way you get notification permissions has changed in iOS 8. A UserNotification is a message shown to the user, whether from remote or from local. You need to get permission to show one. This is described in the WWDC 2014 video "What's New in iOS Notifications"
Building on #Prasath's answer. This is how you do it in Swift:
if application.respondsToSelector("isRegisteredForRemoteNotifications")
{
// iOS 8 Notifications
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: (.Badge | .Sound | .Alert), categories: nil));
application.registerForRemoteNotifications()
}
else
{
// iOS < 8 Notifications
application.registerForRemoteNotificationTypes(.Badge | .Sound | .Alert)
}
iOS 8 has changed notification registration in a non-backwards compatible way. While you need to support iOS 7 and 8 (and while apps built with the 8 SDK aren't accepted), you can check for the selectors you need and conditionally call them correctly for the running version.
Here's a category on UIApplication that will hide this logic behind a clean interface for you that will work in both Xcode 5 and Xcode 6.
Header:
//Call these from your application code for both iOS 7 and 8
//put this in the public header
#interface UIApplication (RemoteNotifications)
- (BOOL)pushNotificationsEnabled;
- (void)registerForPushNotifications;
#end
Implementation:
//these declarations are to quiet the compiler when using 7.x SDK
//put this interface in the implementation file of this category, so they are
//not visible to any other code.
#interface NSObject (IOS8)
- (BOOL)isRegisteredForRemoteNotifications;
- (void)registerForRemoteNotifications;
+ (id)settingsForTypes:(NSUInteger)types categories:(NSSet*)categories;
- (void)registerUserNotificationSettings:(id)settings;
#end
#implementation UIApplication (RemoteNotifications)
- (BOOL)pushNotificationsEnabled
{
if ([self respondsToSelector:#selector(isRegisteredForRemoteNotifications)])
{
return [self isRegisteredForRemoteNotifications];
}
else
{
return ([self enabledRemoteNotificationTypes] & UIRemoteNotificationTypeAlert);
}
}
- (void)registerForPushNotifications
{
if ([self respondsToSelector:#selector(registerForRemoteNotifications)])
{
[self registerForRemoteNotifications];
Class uiUserNotificationSettings = NSClassFromString(#"UIUserNotificationSettings");
//If you want to add other capabilities than just banner alerts, you'll need to grab their declarations from the iOS 8 SDK and define them in the same way.
NSUInteger UIUserNotificationTypeAlert = 1 << 2;
id settings = [uiUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert categories:[NSSet set]];
[self registerUserNotificationSettings:settings];
}
else
{
[self registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert];
}
}
#end
I think this is the better way to keep backwards compatibility if we go with this approach, it is working for my case and hope will work for you. Also pretty easy to understand.
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
{
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
[[UIApplication sharedApplication] registerForRemoteNotifications];
}
else
{
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert)];
}
For the Swift-inclined:
if let registration: AnyObject = NSClassFromString("UIUserNotificationSettings") { // iOS 8+
let notificationTypes: UIUserNotificationType = (.Alert | .Badge | .Sound)
let notificationSettings: UIUserNotificationSettings = UIUserNotificationSettings(forTypes: notificationTypes, categories: nil)
application.registerUserNotificationSettings(notificationSettings)
} else { // iOS 7
application.registerForRemoteNotificationTypes(.Alert | .Badge | .Sound)
}
I couldn't figure out what the "categories" NSSet variable should be set to, so if someone could fill me in I will gladly edit this post. The following does, however, bring up the push notification dialog.
[[UIApplication sharedApplication] registerForRemoteNotifications];
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert) categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
Edit: I got a push notification to send to my phone with this code, so I'm not sure the categories parameter is necessary.
So it turns out that because AnyObject is the spiritual successor to id, you can call any message you want on AnyObject. That's the equivalent of sending a message to id. Ok, fair enough. But now we add in the concept that all methods are optional on AnyObject, and we have something we can work with.
Given the above, I was hopeful I could just cast UIApplication.sharedApplication() to AnyObject, then create a variable equal to the method signature, set that variable to the optional method, then test the variable. This didn't seem to work. My guess is that when compiled against the iOS 8.0 SDK, the compiler knows where it thinks that method should be, so it optimizes this all down to a memory lookup. Everything works fine until I try to test the variable, at which point I get a EXC_BAD_ACCESS.
However, in the same WWDC talk where I found the gem about all methods being optional, they use Optional Chaining to call an optional method - and this seems to work. The lame part is that you have to actually attempt to call the method in order to know if it exists, which in the case of registering for notifications is a problem because you're trying to figure out if this method exists before you go creating a UIUserNotificationSettings object. It seems like calling that method with nil though is okay, so the solution that seems to be working for me is:
var ao: AnyObject = UIApplication.sharedApplication()
if let x:Void = ao.registerUserNotificationSettings?(nil) {
// It's iOS 8
var types = UIUserNotificationType.Badge | UIUserNotificationType.Sound | UIUserNotificationType.Alert
var settings = UIUserNotificationSettings(forTypes: types, categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
} else {
// It's older
var types = UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound | UIRemoteNotificationType.Alert
UIApplication.sharedApplication().registerForRemoteNotificationTypes(types)
}
After much searching related to this, the key info came from this WWDC talk https://developer.apple.com/videos/wwdc/2014/#407 right in the middle at the section about "Optional Methods in Protocols"
In Xcode 6.1 beta the above code does not work anymore, the code below works:
if UIApplication.sharedApplication().respondsToSelector("registerUserNotificationSettings:") {
// It's iOS 8
var types = UIUserNotificationType.Badge | UIUserNotificationType.Sound | UIUserNotificationType.Alert
var settings = UIUserNotificationSettings(forTypes: types, categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
} else {
// It's older
var types = UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound | UIRemoteNotificationType.Alert
UIApplication.sharedApplication().registerForRemoteNotificationTypes(types)
}
If you like to add support to IOS7 IOS8 you can apply this code into your project .
-(void) Subscribe {
NSLog(#"Registering for push notifications...");
if ([[UIApplication sharedApplication] respondsToSelector:#selector(registerUserNotificationSettings:)]) {
UIUserNotificationSettings* notificationSettings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
[[UIApplication sharedApplication] registerForRemoteNotifications];
} else {
[[UIApplication sharedApplication] registerForRemoteNotificationTypes: (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
}
}
-(void)application:(UIApplication *)application
didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
if (notificationSettings.types) {
NSLog(#"user allowed notifications");
[[UIApplication sharedApplication] registerForRemoteNotifications];
} else {
NSLog(#"user did not allow notifications");
UIAlertView *alert =[[UIAlertView alloc]
initWithTitle:#"Please turn on Notification"
message:#"Go to Settings > Notifications > App.\n Switch on Sound, Badge & Alert"
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles: nil];
[alert show];
// show alert here
}
}
After Xcode 6.1 Beta the code below works, slight edit on Tom S code that stopped working with the 6.1 beta (worked with previous beta):
if UIApplication.sharedApplication().respondsToSelector("registerUserNotificationSettings:") {
// It's iOS 8
var types = UIUserNotificationType.Badge | UIUserNotificationType.Sound | UIUserNotificationType.Alert
var settings = UIUserNotificationSettings(forTypes: types, categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
} else {
// It's older
var types = UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound | UIRemoteNotificationType.Alert
UIApplication.sharedApplication().registerForRemoteNotificationTypes(types)
}
You can use this
if ([application respondsToSelector:#selector(isRegisteredForRemoteNotifications)])
{
// for iOS 8
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
[application registerForRemoteNotifications];
}
else
{
// for iOS < 8
[application registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)];
}
// RESET THE BADGE COUNT
application.applicationIconBadgeNumber = 0;
Swift 2.0
// Checking if app is running iOS 8
if application.respondsToSelector("isRegisteredForRemoteNotifications") {
print("registerApplicationForPushNotifications - iOS 8")
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil));
application.registerForRemoteNotifications()
} else {
// Register for Push Notifications before iOS 8
print("registerApplicationForPushNotifications - <iOS 8")
application.registerForRemoteNotificationTypes([UIRemoteNotificationType.Alert, UIRemoteNotificationType.Badge, UIRemoteNotificationType.Sound])
}
If all you need is the ios 8 code, this should do it.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
[application registerUserNotificationSettings: [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
[application registerForRemoteNotifications];
}
return YES;
}
This is cleaner way I am doing and it just works great
if (floor(NSFoundationVersionNumber) < NSFoundationVersionNumber_iOS_8_0)
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge|
UIRemoteNotificationTypeAlert| UIRemoteNotificationTypeSound];
else {
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
[application registerForRemoteNotifications];
}
for iOS 8 and above
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert) categories:nil];
[application registerUserNotificationSettings:settings];