J'ai une application qui utilise des notifications locales. Dans iOS 7, tout fonctionne bien, mais dans iOS 8, l'application doit demander la permission de l'utilisateur pour afficher les notifications. Pour demander la permission dans iOS 8, j'utilise:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
}
Cela fonctionne très bien dans Xcode 6 et dans iOS 8. Lorsque j'ouvre le même projet dans Xcode 5, l'erreur est un problème sémantique. "Utilisation de l'identifiant non déclaré 'UIUserNotificationSettings'."
Comment puis-je faire fonctionner l'application avec iOS 7 et 8 et que les notifications fonctionnent correctement sur les deux versions.
La réponse suivante fait quelques hypothèses:
Code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// None of the code should even be compiled unless the Base SDK is iOS 8.0 or later
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
// The following line must only run under iOS 8. This runtime check prevents
// it from running if it doesn't exist (such as running under iOS 7 or earlier).
if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) {
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
}
#endif
}
Tout cela est couvert dans le Apple Guide de compatibilité du SDK .
J'ai implémenté simplement -
if([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)]) {
[[UIApplication sharedApplication] registerForRemoteNotifications];
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert) categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
} else {
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge];
}
Jusqu'à présent, cet extrait de code fonctionne pour moi, et il est simple à réutiliser:
//-- Set Notification
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)];
}
Si vous souhaitez toujours compiler avec des SDK plus anciens ou si vous avez une raison particulière de le faire, vous pouvez essayer d'appeler l'ensemble de manière dynamique:
#ifdef __IPHONE_8_0
#define USING_IOS8_SDK
#else // iOS <8 SDK compatibility definitions
#define UIUserNotificationTypeNone (0)
#define UIUserNotificationTypeBadge (1 << 0)
#define UIUserNotificationTypeSound (1 << 1)
#define UIUserNotificationTypeAlert (1 << 2)
#endif
...
if ([_sharedApplication respondsToSelector:NSSelectorFromString(@"registerUserNotificationSettings:")])
{
NSUInteger settingsParam = (UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound);
id categoriesParam = nil;
#ifdef USING_IOS8_SDK
// Perform direct call when using iOS 8 SDK
[_sharedApplication registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:settingsParam categories:categoriesParam]];
#else
#pragma clang diagnostic Push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
// Do the dynamic stuff
// Get UIUserNotificationSettings class reference
Class settings = NSClassFromString(@"UIUserNotificationSettings");
if (settings) {
// Prepare class selector
SEL sel = NSSelectorFromString(@"settingsForTypes:categories:");
// Obtain a method signature of selector on UIUserNotificationSettings class
NSMethodSignature *signature = [settings methodSignatureForSelector:sel];
// Create an invocation on a signature -- must be used because of primitive (enum) arguments on selector
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.selector = sel;
invocation.target = settings;
// Set arguments
[invocation setArgument:&settingsParam atIndex:2];
[invocation setArgument:&categoriesParam atIndex:3];
// Obtain an instance by firing an invocation
NSObject *settingsInstance;
[invocation invoke];
[invocation getReturnValue:&settingsInstance];
// Retain an instance so it can live after quitting method and prevent crash :-)
CFRetain((__bridge CFTypeRef)(settingsInstance));
// Finally call the desired method with proper settings
if (settingsInstance)
[_sharedApplication performSelector:NSSelectorFromString(@"registerUserNotificationSettings:") withObject:settingsInstance];
}
#pragma clang diagnostic pop
#endif
}
Cela devrait parfaitement compiler sur le SDK iOS 7 et fonctionner (efficacement) sur les appareils iOS 8, ignoré sur les anciens.
La migration de votre projet vers le SDK iOS 8 est une autre solution, pensé, mais il y a encore des options.
if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound categories:nil];
[application registerUserNotificationSettings:settings];
}