objective-c ios8 apple-push-notifications

objective c - registerForRemoteNotificationTypes: no es compatible con iOS 8.0 y versiones posteriores



objective-c ios8 (15)

Así que resulta que dado que AnyObject es el sucesor espiritual de id, puede llamar a cualquier mensaje que desee en AnyObject. Eso es el equivalente a enviar un mensaje a id. OK bastante justo. Pero ahora agregamos el concepto de que todos los métodos son opcionales en AnyObject , y tenemos algo con lo que podemos trabajar.

Teniendo en cuenta lo anterior, tenía la esperanza de poder lanzar UIApplication.sharedApplication () a AnyObject, luego crear una variable igual a la firma del método, establecer esa variable en el método opcional y luego probar la variable. Esto no parece funcionar. Supongo que cuando se compila con el SDK de iOS 8.0, el compilador sabe dónde cree que debería estar ese método, por lo que optimiza todo esto en una búsqueda de memoria. Todo funciona bien hasta que intento probar la variable, momento en el que obtengo un EXC_BAD_ACCESS.

Sin embargo, en la misma charla de WWDC donde encontré que la gema de que todos los métodos son opcionales, usan el Encadenamiento opcional para llamar a un método opcional, y esto parece funcionar. La parte coja es que tienes que intentar llamar al método para saber si existe, lo que en el caso de registrarse para recibir notificaciones es un problema porque estás tratando de averiguar si este método existe antes de crear un UIUserNotificationSettings objeto. Parece que llamar a ese método con nil está bien, por lo que la solución que parece funcionar para mí es:

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) }

Después de una gran búsqueda relacionada con esto, la información clave provino de esta charla de WWDC https://developer.apple.com/videos/wwdc/2014/#407 justo en el centro en la sección sobre "Métodos opcionales en protocolos"

En Xcode 6.1 beta el código anterior ya no funciona, el código siguiente funciona:

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) }

Cuando intente registrarse para recibir notificaciones push bajo iOS 8.x:

application.registerForRemoteNotificationTypes(UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound)

Obtuve el siguiente error:

registerForRemoteNotificationTypes: is not supported in iOS 8.0 and later.

¿Alguna idea de cuál es la nueva forma de hacerlo? Funciona cuando ejecuto esta aplicación Swift en iOS 7.x.

EDITAR

En iOS 7.x cuando incluyo el código condicional que obtengo (ya sea SystemVersion condicional o #if __IPHONE_OS_VERSION_MAX_ALLOWED> = 80000)

dyld: Symbol not found: _OBJC_CLASS_$_UIUserNotificationSettings


Como describió, deberá usar un método diferente basado en diferentes versiones de iOS. Si su equipo usa tanto Xcode 5 (que no conoce ningún selector de iOS 8) como Xcode 6, entonces deberá usar la compilación condicional de la siguiente manera:

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) { // use registerUserNotificationSettings } else { // use registerForRemoteNotificationTypes: } #else // use registerForRemoteNotificationTypes: #endif

Si solo está utilizando Xcode 6, puede seguir con esto:

if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) { // use registerUserNotificationSettings } else { // use registerForRemoteNotificationTypes: }

El motivo es que la forma en que obtiene los permisos de notificación ha cambiado en iOS 8. Una UserNotification es un mensaje que se muestra al usuario, ya sea de forma remota o local. Necesitas obtener permiso para mostrar uno. Esto se describe en el video de la WWDC 2014 "Novedades en las notificaciones de iOS"


Creo que esta es la mejor manera de mantener la compatibilidad hacia atrás si seguimos este enfoque, está funcionando para mi caso y espero que funcione para usted. También es bastante fácil de entender.

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)]; }


Después de Xcode 6.1 Beta, el siguiente código funciona, edite levemente el código de Tom S que dejó de funcionar con la versión beta 6.1 (trabajó con la versión beta anterior):

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) }


Esta es una forma más limpia que estoy haciendo y funciona muy bien.

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]; }


No pude averiguar a qué se debe configurar la variable NSSet de "categorías", por lo que si alguien me completa, editaré esta publicación con mucho gusto. Lo siguiente, sin embargo, muestra el diálogo de notificación push.

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

Edición: recibí una notificación de envío para enviar a mi teléfono con este código, así que no estoy seguro de que el parámetro de categorías sea necesario.


Para el Swift-inclinado:

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) }


Para 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; }

Para iOS10

https://.com/a/39383027/3560390


Puedes usar esto

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;


Si desea agregar soporte a IOS7 IOS8 puede aplicar este código en su proyecto.

-(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 } }


Si todo lo que necesita es el código de ios 8, debería hacerlo.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { [application registerUserNotificationSettings: [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]]; [application registerForRemoteNotifications]; } return YES; }


Sobre la base de la respuesta de @prasath. Así es como lo haces en 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) }


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]) }


iOS 8 ha cambiado el registro de notificaciones de una manera no compatible con versiones anteriores. Si bien es necesario que admita iOS 7 y 8 (y mientras que las aplicaciones creadas con 8 SDK no son aceptadas), puede verificar los selectores que necesita y llamarlos condicionalmente para la versión en ejecución.

Aquí hay una categoría en UIApplication que ocultará esta lógica detrás de una interfaz limpia que funcionará en Xcode 5 y Xcode 6.

Encabezamiento:

//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

Implementación:

//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


para iOS 8 y superior

UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert) categories:nil]; [application registerUserNotificationSettings:settings];