Notificação push do iOS: como detectar se o usuário tocou na notificação quando o aplicativo está em segundo plano?


116

Existem muitos threads de stackoverflow relacionados a esse tópico, mas ainda não encontrei uma boa solução.

Se o aplicativo não está no fundo, eu posso verificar launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]na application:didFinishLaunchingWithOptions:chamada para ver se ele está aberto a partir de uma notificação.

Se o aplicativo estiver em segundo plano, todas as postagens sugerem o uso application:didReceiveRemoteNotification:e a verificação do estado do aplicativo. Mas, como experimentei (e também como o nome desta API sugere), esse método é chamado quando a notificação é recebida, em vez de tocada.

Portanto, o problema é, se o aplicativo for iniciado e depois colocado em segundo plano, e você souber que uma notificação foi recebida de application:didReceiveNotification(application:didFinishLaunchWithOptions: não será acionada neste ponto), como saber se o usuário retomou o aplicativo tocando na notificação ou apenas tocando no ícone do aplicativo? Porque se o usuário tocou na notificação, a expectativa é abrir a página mencionada naquela notificação. Caso contrário, não deveria.

Eu poderia usar handleActionWithIdentifierpara notificações de ação personalizada, mas isso só é acionado quando um botão de ação personalizada é pressionado, não quando o usuário toca no corpo principal da notificação.

Obrigado.

EDITAR:

depois de ler uma das respostas abaixo, pensei que desta forma posso esclarecer minha dúvida:

Como podemos diferenciar esses 2 cenários:

(A) 1.app vai para segundo plano; 2. notificação recebida; 3. toque do usuário na notificação; 4. aplicativo entra em primeiro plano

(B) 1.app vai para segundo plano; 2. notificação recebida; 3. o usuário ignora a notificação e toca no ícone do aplicativo posteriormente; 4. aplicativo entra em primeiro plano

Uma vez que application:didReceiveRemoteNotification:é acionado em ambos os casos na etapa 2.

Ou deve application:didReceiveRemoteNotification:ser acionado na etapa 3 apenas para (A), mas de alguma forma configurei meu aplicativo de forma errada, então estou vendo na etapa 2?


Use um valor de dicionário personalizado para sua carga útil e aja de acordo. Bem simples.
soulshined

@soulshined um dicionário na carga útil pode representar se o usuário tocou na notificação, certo? por exemplo, seu amigo A postou um artigo B, você pode dizer {usuário: A, artigo: B} na carga útil, enquanto o aplicativo está em segundo plano e você obtém didReceiveRemoteNotification. Como saber quando o aplicativo será reiniciado, se você deve exibir o artigo?
Bao Lei de

2
@soulshined Eu li a documentação e me eduquei sobre o que didReceiveRemoteNotification faz. Você realmente leu minha pergunta? De acordo com a documentação oficial da Apple, didReceiveRemoteNotification "informa ao delegado que o aplicativo em execução recebeu uma notificação remota". Estou perguntando qual é uma boa maneira de saber se o usuário tocou em uma notificação. O link do SO ao qual você se referiu é para quando o aplicativo está sendo iniciado de um novo começo, estou perguntando o cenário quando o aplicativo está em segundo plano.
Bao Lei


1
@soulshined OK, talvez eu não tenha afirmado com clareza suficiente. Quero dizer, se o aplicativo for encerrado completamente, não em segundo plano, sim, didFinishLaunching será chamado. Mas se você iniciar seu aplicativo e, em seguida, colocá-lo em segundo plano e, agora, uma notificação entrar e o usuário tocar na notificação, e agora o didFinishLaunching não será chamado novamente. Em vez disso, applicationWillEnterForeground e applicationDidBecomeActive serão chamados. Como você pode saber se o aplicativo está entrando em primeiro plano porque o usuário tocou na notificação ou no ícone do aplicativo?
Bao Lei de

Respostas:


102

OK, eu finalmente descobri.

Nas configurações de destino ➝ guia Capacidades ➝ Modos de fundo, se você marcar "Notificações remotas", application:didReceiveRemoteNotification:será acionado assim que a notificação chegar (contanto que o aplicativo esteja em segundo plano) e, nesse caso, não há como saber se o usuário tocará na notificação.

Se você desmarcar essa caixa, application:didReceiveRemoteNotification:será acionado apenas quando você tocar na notificação.

É um pouco estranho que marcar esta caixa altere o comportamento de um dos métodos de delegação do aplicativo. Seria melhor se essa caixa estiver marcada, a Apple usa dois métodos de delegação diferentes para receber notificações e tocar em notificações. Acho que a maioria dos desenvolvedores sempre quer saber se uma notificação foi tocada ou não.

Esperançosamente, isso será útil para qualquer pessoa que tenha esse problema. A Apple também não documentou claramente aqui, então demorei um pouco para descobrir.

insira a descrição da imagem aqui


6
Eu tenho os Modos de fundo definidos como "DESLIGADOS" completamente, mas ainda sou notificado quando uma notificação chega com o aplicativo em modo de fundo.
Gottfried

5
Ótimo! Isso me ajudou. No entanto, é uma pena que eu precise desligar as notificações remotas. Eu gostaria de pré-buscar dados quando uma notificação push chegar E detectar o toque do usuário. Não consigo encontrar uma maneira de conseguir isso.
Peter Fennema

1
Defino o modo de fundo para o modo "ON" e habilito as notificações remotas. Ainda não foi possível detectar o evento de notificação.
kalim sayyad

1
Eu tenho o mesmo problema O modo de fundo está definido como "LIGADO" e habilitado para notificação remota, mas ainda não sou notificado quando a notificação chega ao aplicativo no modo de fundo
Swapnil Dhotre

@Bao - Acho que vai causar rejeição na Appstore, já que essa opção é basicamente usada para baixar conteúdo relacionado à notificação em segundo plano. Portanto, se você não estiver baixando nenhum conteúdo, a Apple pode rejeitar seu aplicativo. developer.apple.com/library/ios/documentation/iPhone/Conceptual/…
niks

69

Estou procurando a mesma coisa que você e realmente encontrei uma solução que não requer notificação remota para ser marcada.

Para verificar se o usuário tocou ou se o aplicativo está em segundo plano ou ativo, basta verificar o estado do aplicativo em

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{

    if(application.applicationState == UIApplicationStateActive) {

        //app is currently active, can update badges count here

    }else if(application.applicationState == UIApplicationStateBackground){

        //app is in background, if content-available key of your notification is set to 1, poll to your backend to retrieve data and update your interface here

    }else if(application.applicationState == UIApplicationStateInactive){

        //app is transitioning from background to foreground (user taps notification), do what you need when user taps here

    }

Para mais informações, verifique:

Referência da estrutura UIKit> Referência de classe UIApplication> UIApplicationState


10
Que tal o aplicativo ser UIApplicationStateInactive como resultado do envolvimento do usuário com a Central de notificações (deslizar de cima para baixo) ou o centro de controle (deslizar de baixo para cima) enquanto o aplicativo está na tela. Aparentemente, não há como saber se o APNS foi recebido no estado Inativo ou se o usuário realmente o tocou.
Kostia Kim

1
@KostiaKim Você está certo, mas esse é um caso super-extremo ... fora isso, esta resposta é a mais válida em termos de separação entre o aplicativo em primeiro plano ... o usuário tocando ... o aplicativo em segundo plano
Honey

Obrigado, isso meio que desmistifica tudo. Lembre-se de que, para que o método delegado seja chamado quando o aplicativo estiver em segundo plano, a notificação push deve incluir a content-availablechave, mas a notificação deve ser silenciosa (ou seja, não incluir um som ou emblema) conforme declarado nos documentos oficiais .
Nickkk

1
@KostiaKim Na verdade, consegui corrigir o problema de não saber se o centro de controle estava aberto ou se o usuário realmente tocou na notificação adicionando um booleano definido como verdadeiro no func applicationDidEnterBackground(_ application: UIApplication)e falso no. func applicationDidBecomeActive(_ application: UIApplication)Isso me permitiu mostrar o no aplicativo notificações quando o aplicativo está inativo devido ao centro de controle ou à lista de notificações
DatForis

3
fico confuso por que a Apple não apresentou um evento diferente ou simplesmente adicionou um sinalizador nos metadados para indicar que o usuário realmente interagiu com a notificação. Ter que 'deduzir' se o usuário executou uma ação com base nas informações do ambiente sobre o estado do aplicativo não é razoável para um bit de informação importante que afeta a expectativa do usuário quanto ao comportamento do aplicativo. Talvez a única coisa a fazer seja criar uma ação personalizada para abrir o aplicativo com essas informações?
Allan Nienhuis

20

De acordo com iOS / XCode: como saber se o aplicativo foi lançado com um clique na notificação ou no ícone do aplicativo trampolim? você tem que verificar o estado do aplicativo em didReceiveLocalNotification assim:

if ([UIApplication sharedApplication].applicationState == UIApplicationStateInactive)
{
    // user has tapped notification
}
else
{
    // user opened app from app icon
}

Embora não faça totalmente sentido para mim, parece funcionar.


1
Não funcionaria neste cenário. Eu tentei isso antes. Quando essa caixa de seleção foi marcada (veja minha resposta aceita para detalhes), quando a notificação chegar, sua linha com o comentário "// usuário tocou na notificação" será inserida, mesmo que o usuário não tenha tocado na notificação (a notificação acabou de chegar )
Bao Lei

3
Discordo. Eu marquei "Notificações remotas" em minhas capacidades de segundo plano e funciona da maneira descrita na minha resposta. Também marquei a opção "Busca de plano de fundo". Talvez isso cause a mudança?
Werner Kratochwil

você poderia marcar minha resposta com +1? ;-) Ainda preciso de alguns votos para participar mais do stackoverflow. Muito obrigado
Werner Kratochwil

Sim, esta é a solução. tnx.
Afshin,

Observe que isso será um falso positivo se a notificação for enviada enquanto o usuário estiver em uma tela diferente (por exemplo, se ele puxar para baixo a barra de status e, em seguida, receber uma notificação de seu aplicativo).
Kevin Cooper

16

Se alguém quiser no Swift 3.0

switch application.applicationState {
    case .active:
        //app is currently active, can update badges count here
        break
    case .inactive:
        //app is transitioning from background to foreground (user taps notification), do what you need when user taps here
        break
    case .background:
        //app is in background, if content-available key of your notification is set to 1, poll to your backend to retrieve data and update your interface here
        break
    default:
        break
    }

para swift 4

switch UIApplication.shared.applicationState {
case .active:
    //app is currently active, can update badges count here
    break
case .inactive:
    //app is transitioning from background to foreground (user taps notification), do what you need when user taps here
    break
case .background:
    //app is in background, if content-available key of your notification is set to 1, poll to your backend to retrieve data and update your interface here
    break
default:
    break
}

Em qual gatilho devemos adicionar este código, didReceiveRemoteNotification ou didFinishLaunchingWithOptions?
Dashrath

2
Em didReceiveRemoteNotification
Hamid Shahsavari

@Hamid sh ,,,, Recebi notificação push em todos os estados, ou seja, quando o aplicativo está em forground, background, close (encerrado) ..! mas meu problema é como incrementar a contagem de crachás quando o aplicativo está no estado de segundo plano e no estado de fechamento (encerramento) ?? Por favor, diga-me em detalhes como eu faço isso ....? a contagem de meus emblemas de aplicativo só aumenta quando o aplicativo está no estado inicial .....? se possível, diga-me brevemente .......!
Kiran jadhav

8

Se você selecionou "Modos de fundo"> "Notificações remotas" == SIM, toque no evento de notificação chegará em:

-(void)userNotificationCenter:(UNUserNotificationCenter *)center **didReceiveNotificationResponse**:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler.

Isso me ajudou. Por favor aproveite :)


Eu vi que a Apple adicionou isso, mas não consegui descobrir como obter a carga útil personalizada da notificação dessa forma.
sudo

Esta função é chamada quando o aplicativo está em um estado de segundo plano e se torna ativo e também quando o aplicativo é iniciado do estado morto
Nikhil Muskur

@NikhilMuskur apenas se as "notificações remotas" estiverem habilitadas, certo?
Zorayr

@Zorayr yup isso mesmo
Nikhil Muskur

8

Também tive esse problema - mas no iOS 11 com o novo UserNotificationsFramework.

Aqui para mim é assim:

  • Novo lançamento: application:didFinishLaunchingWithOptions:
  • Recebido de um estado finalizado: application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
  • Recebido em primeiro plano: userNotificationCenter(_:willPresent:withCompletionHandler:)
  • Recebido em segundo plano: userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:

Esta é a maneira de fazer isso no iOS
11+

Isso é tão complicado 🤦‍♂️
Zorayr

4

No meu caso, o modo de fundo OFF não fez nenhuma diferença. No entanto, quando o aplicativo foi suspenso e o usuário tocou na notificação, pude lidar com o caso neste método de retorno de chamada:

func userNotificationCenter(_ center: UNUserNotificationCenter,
                            didReceive response: UNNotificationResponse,
                            withCompletionHandler completionHandler: @escaping () -> Void) {

}

Esta é a forma oficial que a Apple diz. De acordo com o documento da Apple, ele será chamado sempre que o usuário interagir com a IU de notificação por push. Se o aplicativo não estiver em segundo plano, ele iniciará o aplicativo no modo de segundo plano e chamará esse método.
Ryan

3

Para iOS 10 e superior, coloque-o em AppDelegate, para saber se a notificação foi tocada (funciona mesmo se o aplicativo estiver fechado ou aberto)

func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
print("notification tapped here")
}

1
Esta é a resposta correta para iOS 10+. Explicação completa fornecida em outro tópico aqui: stackoverflow.com/a/41783666/1761357 .
dead_can_dance

2

Existem dois Funcs para lidar com PushNotification recebido dentro da PushNotificationManagerclasse:

class PushNotificationManager: NSObject, MessagingDelegate, UNUserNotificationCenterDelegate{

}

Como testei o primeiro no gatilho assim que a notificação chegou

@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {

    completionHandler(UNNotificationPresentationOptions.alert)

    //OnReceive Notification
    let userInfo = notification.request.content.userInfo
    for key in userInfo.keys {
         Constants.setPrint("\(key): \(userInfo[key])")
    }

    completionHandler([])

}

E o segundo quando tocado na notificação:

@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

    //OnTap Notification
    let userInfo = response.notification.request.content.userInfo
    for key in userInfo.keys {
        Constants.setPrint("\(key): \(userInfo[key])")
    }

    completionHandler()
}

Eu também testei com os estados LIGADO e DESLIGADO de Notificação Remota (em Modos de Fundo)


1

SWIFT 5.1

UIApplication.State não funcionou para mim, porque depois de ler a impressão digital (modal é mostrado) em meu aplicativo, a notificação também é lançada na barra superior e o usuário deve clicar nela.

Eu criei

public static var isNotificationFromApp: Bool = false

em AppDelegatee eu configurei truena minha inicialização viewControllere, em seguida, na minha notificação storyboard/ viewControllereu acabei de verificar isso :)

Espero que possa ser útil


-7

Você pode configurar a carga útil da sua notificação push para chamar o delegado do aplicativo application:didReceiveRemoteNotification:fetchCompletionHandler: método aplicativo quando o aplicativo está em segundo plano. Você pode definir algum sinalizador aqui para que, quando o usuário iniciar seu aplicativo na próxima vez, você possa executar sua operação.

A partir da documentação da apple, você deve usar esses métodos para baixar novo conteúdo associado à notificação push. Além disso, para que isso funcione, você deve habilitar a notificação remota nos modos de segundo plano e sua carga útil de notificação push deve conter a content-availablechave com seu valor definido como 1. Para obter mais informações, consulte Usando notificações push para iniciar um download seção do doc da apple aqui .

Outra maneira é fazer com que o emblema conte na carga útil da notificação push. Portanto, na próxima vez que seu aplicativo for iniciado, você poderá verificar a contagem de crachás do aplicativo. Se for maior que zero, execute sua operação e zerar / limpar a contagem do crachá do servidor também.

Espero que isso ajude você.


@bhusan, você leu os detalhes da minha pergunta? Estou vendo didReceiveRemoteNotification sendo chamado quando a notificação chega (antes que o usuário toque nela). Eu queria saber se o usuário tocou nele.
Bao Lei

Você não respondeu à pergunta de OP. Ele não quer apenas saber se houve uma notificação. Ele quer saber se o usuário abriu o aplicativo tocando nessa notificação remota.
Joe C de
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.