Peça permissão do usuário para receber UILocalNotifications no iOS 8

115

Eu configurei notificações locais no App Delegate usando este:

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    UILocalNotification *notification = [[UILocalNotification alloc]init];
    [notification setAlertBody:@"Watch the Latest Episode of CCA-TV"];
    [notification setFireDate:[NSDate dateWithTimeIntervalSinceNow:5]];
    [notification setTimeZone:[NSTimeZone defaultTimeZone]];
    [application setScheduledLocalNotifications:[NSArray arrayWithObject:notification]];
}

Quando executo o aplicativo e saio dele, recebo um erro dizendo:

2014-06-07 11: 14: 16.663 CCA-TV [735: 149070] Tentando agendar uma notificação local {fire date = Saturday, June 7, 2014 at 11:14:21 Pacific Daylight Time, time zone = America / Los_Angeles (PDT) offset -25200 (Daylight), repeat interval = 0, repeat count = UILocalNotificationInfiniteRepeatCount, next fire date = Saturday, June 7, 2014 at 11:14:21 Pacific Daylight Time, user info = (null)} com um alerta mas não recebi permissão do usuário para exibir alertas

Como posso obter a permissão necessária para exibir os alertas?

Dannysandler
fonte
1
Acho que o aplicativo rejeitou a permissão uma vez, você pode tentar habilitar em Configurações. Mas, por falar nisso, UILocalNotification não precisa da permissão do usuário ..
iphonic
Experimente registerUserNotificationSettings. Se fosse o iOS 8, este tópico teria respondido sua pergunta. Mas,
vá em

Respostas:

237

Como o iOS 8, você precisa pedir permissão ao usuário para mostrar notificações de seu aplicativo, isso se aplica a notificações remotas / push e locais. No Swift você pode fazer assim,

Atualização para Swift 2.0

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
    // Override point for customization after application launch.
    if(UIApplication.instancesRespondToSelector(Selector("registerUserNotificationSettings:")))
    {
        let notificationCategory:UIMutableUserNotificationCategory = UIMutableUserNotificationCategory()
        notificationCategory.identifier = "INVITE_CATEGORY"
        notificationCategory.setActions([replyAction], forContext: UIUserNotificationActionContext.Default)

        //registerting for the notification.
        application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes:[.Sound, .Alert, .Badge], categories: nil))
    }
    else
    {
       //do iOS 7 stuff, which is pretty much nothing for local notifications.
    }
    return true
}

Swift 3.2

if(UIApplication.instancesRespond(to: #selector(UIApplication.registerUserNotificationSettings(_:)))){
     let notificationCategory:UIMutableUserNotificationCategory = UIMutableUserNotificationCategory()
     notificationCategory.identifier = "INVITE_CATEGORY"
     notificationCategory.setActions([replyAction], forContext: UIUserNotificationActionContext.Default)

     //registerting for the notification.
        application.registerUserNotificationSettings(UIUserNotificationSettings(types:[.sound, .alert, .badge], categories: nil))
}
else{
        //do iOS 7 stuff, which is pretty much nothing for local notifications.
    }

A sintaxe do Objective C também é muito semelhante.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    if ([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)]){
        [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
    }
    // Override point for customization after application launch.
    return YES;
}

Para verificar os tipos de notificação registrados atualmente, você pode usar o método da classe UIApplication,

- (UIUserNotificationSettings *)currentUserNotificationSettings

Portanto, se o usuário disse não ao seu aplicativo, esta função deve retornar uma configuração sem quaisquer tipos nela.

Eu escrevi um tutorial sobre isso, você pode vê-lo aqui .

Satheeshwaran
fonte
1
Se o usuário negar a permissão, como você determina isso mais tarde de forma programática?
jjxtra de
@satheeshwaran Quando eu uso este código, ele está funcionando bem com o simulador com iOS8. Eu queria o destino de implantação do meu aplicativo a partir do iOS7. Então, quando eu executar esse código em um dispositivo iOS7, eu recebo este erro: dyld: Symbol not found: _OBJC_CLASS_$_UIUserNotificationSettings. Existe alguma outra maneira no Swift de solicitar permissões ao usuário para trabalhar no iOS7? por favor ajude.
Raghavendra
@Raghav UIUserNotificationSettings está disponível apenas no iOS 8 e o que você está enfrentando é o comportamento correto. Você não deve usar isso no iOS 7.
Satheeshwaran
1
-1 para verificar a versão do iOS UIDevice. stackoverflow.com/a/25735175/1226304 answer tem uma abordagem melhor para fazer isso.
derpoliuk de
3
@derpoliuk Atualizado conforme resposta para benefício de todos, ok ??
Satheeshwaran de
38

Coloque este código no controlador de visualização onde você irá primeiro programar as notificações (se você programá-las na inicialização, então será application:didFinishLaunchingWithOptions:):

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

Em Swift:

if(UIApplication.instancesRespondToSelector(Selector("registerUserNotificationSettings:"))) {
    UIApplication.sharedApplication().registerUserNotificationSettings(UIUserNotificationSettings(forTypes: .Alert | .Sound, categories: nil))
}

As soluções que testam contra o número da versão do sistema são abaixo do ideal e estão sujeitas a erros.

KPM
fonte
Eu usaria application.respondsToSelector(Selector("registerUserNotificationSettings"))eif ([application respondsToSelector:@selector(registerUserNotificationSettings:)])
derpoliuk
7
Bem, isso é apenas porque você está usando dentro dele, o application:didFinishLaunchingWithOptions:que fornece um applicationobjeto útil :)
KPM
18

Experimente isso para Objective-C:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:    (NSDictionary *)launchOptions
{
// are you running on iOS8?
if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) 
  {
    UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge|UIUserNotificationTypeAlert|UIUserNotificationTypeSound) categories:nil];
    [application registerUserNotificationSettings:settings];
  } 
else // iOS 7 or earlier
  {
    UIRemoteNotificationType myTypes = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound;
    [application registerForRemoteNotificationTypes:myTypes];
  }
}

Para Swift:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
// Override point for customization after application launch.
 if(UIApplication.instancesRespondToSelector(Selector("registerUserNotificationSettings:")))
 {
    application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: UIUserNotificationType.Sound | UIUserNotificationType.Alert | UIUserNotificationType.Badge, categories: nil))
 }
 else
 {
    //
 }
return true
}
Nagarjun
fonte
5

Acabei de enfrentar o mesmo problema. Parece que no iOS 8, precisamos realizar uma etapa adicional, geralmente feita dentro de:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { /*...*/ }

Você pode usar este código se quiser mantê-lo compatível com versões anteriores:

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
    if ([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)])
    {
        [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil]];
    }
#endif

O sistema se lembrará da decisão e só perguntará uma vez.

ppalancica
fonte
Melhor verificar a versão do iOS com este código if ([[UIDevice currentDevice] .systemVersion floatValue] <10)
Mehul Chuahan
1

** Notificação local com ação de três botões para iOS8 +

// Botão: EU TIVE, LEMBREI DEPOIS, PULAR **

        let completeAction = UIMutableUserNotificationAction()
        completeAction.identifier = "COMPLETE_TODO"
        completeAction.title = "I TOOK IT"
        completeAction.activationMode = .Background
        completeAction.destructive = true
        completeAction.authenticationRequired = false

        let remindAction = UIMutableUserNotificationAction()
        remindAction.identifier = "REMIND_TODO"
        remindAction.title = "REMIND LATER"
        remindAction.activationMode = .Background
        remindAction.destructive = false
        //  remindAction.authenticationRequired = false

        let skipAction = UIMutableUserNotificationAction()
        skipAction.identifier = "SKIP_TODO"
        skipAction.title = "SKIP IT"
        skipAction.activationMode = .Background
        skipAction.destructive = false
        skipAction.authenticationRequired = false


        let todoCategory = UIMutableUserNotificationCategory()
        todoCategory.identifier = "TODO_CATEGORY"
        todoCategory.setActions([completeAction, remindAction, skipAction], forContext: .Default)
        todoCategory.setActions([completeAction,remindAction,skipAction], forContext: .Minimal)


        if application.respondsToSelector("isRegisteredForRemoteNotifications")
        {

            let categories = NSSet(array: [todoCategory,todoVideoCategory])
            let types:UIUserNotificationType = ([.Alert, .Sound, .Badge])

            let settings:UIUserNotificationSettings = UIUserNotificationSettings(forTypes: types, categories: categories as? Set<UIUserNotificationCategory>)

            application.registerUserNotificationSettings(settings)
            application.registerForRemoteNotifications()

        }

    }
PSS
fonte