Detectar se o aplicativo foi iniciado / aberto a partir de uma notificação por push

172

É possível saber se o aplicativo foi iniciado / aberto a partir de uma notificação por push?

Eu acho que o evento de lançamento pode ser pego aqui:

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

    if (launchOptions != nil) {
         // Launched from push notification
         NSDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];

    }
}

No entanto, como posso detectar que ele foi aberto a partir de uma notificação por push quando o aplicativo estava em segundo plano?

joao
fonte
6
Este é um post antigo, mas muito útil. Infelizmente, as principais respostas não resolvem o problema (como indicam os comentários). Considere marcar uma nova resposta como 'aceita', pois a atual não está completa.
MobileVet
1
Esta pergunta tem mais de 100k visualizações, mas a resposta selecionada está incorreta ou completa. Para os visitantes, considere classificar por Ativo em vez de por Votos para encontrar soluções modernas.
Albert Renshaw 19/04

Respostas:

187

Veja este código:

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    if ( application.applicationState == UIApplicationStateInactive || application.applicationState == UIApplicationStateBackground  )
    {
         //opened from a push notification when the app was on background
    }
}

igual a

-(void)application:(UIApplication *)application didReceiveLocalNotification (UILocalNotification *)notification
shanegao
fonte
19
@ManuelM. Essa é uma boa resposta, pois mostra como detectar quando um aplicativo em segundo plano é trazido ao primeiro plano a partir de uma notificação por push. Para quando o aplicativo não estiver em execução, você precisará da resposta de M.Othman abaixo.
OpenUserX03
6
Estou recebendo a chamada para o aplicativo: didReceiveRemoteNotification: depois de tocar na notificação, independentemente de o aplicativo estar em segundo plano ou não estar em execução, portanto, essa resposta atende perfeitamente às minhas necessidades. Testado no iOS 7 e 8
Newtz 02/02
16
Como alguns outros apontaram, isso não detecta "iniciado / aberto a partir de uma notificação por push". Isso é chamado quando a notificação é recebida, não quando é aberta. Portanto, se você recebeu uma notificação na BG, mas tocou no ícone do aplicativo para abri-lo, o código que você tem aqui ainda será executado e você poderá abrir uma página que o usuário não pretendia abrir.
Bao Lei
4
@ManuelM. esse método não informa se o aplicativo foi aberto pelo centro de notificação versus o ícone do aplicativo se os modos em segundo plano - a notificação remota estiver marcada. Faz quando está desmarcado. Eu documentei a diferença neste post: stackoverflow.com/questions/32061897/…
Bao Lei
2
Confirmou que isso funciona com o Google Cloud Messaging.
CularBytes
127

tarde, mas talvez útil

Quando o aplicativo não está sendo executado

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

é chamado ..

onde você precisa verificar a notificação por push

NSDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (notification) {
    NSLog(@"app recieved notification from remote%@",notification);
    [self application:application didReceiveRemoteNotification:notification];
} else {
    NSLog(@"app did not recieve notification");
}
M.Othman
fonte
2
Note-se que no trecho acima, a notificação não deve ser declarado como um (UILocalNotification *), mas um (NSDictionary *)
Cosmix
1
Dessa forma, você pode ver se houve alguma notificação para o aplicativo enquanto não estiver em execução! A questão era: como detectar se o aplicativo foi aberto a partir de uma notificação. Nesse caso, o didReceiveRemoteNotification é chamado, mesmo que o aplicativo não esteja sendo executado. - Gostei da sua resposta, porque é muito importante para muitos casos, mas não a resposta correta para a pergunta.
Axel Zehden 22/09/16
Sua resposta e esta resposta estão fazendo a mesma coisa?
Honey
38

O problema que tivemos foi ao atualizar corretamente a exibição após o lançamento do aplicativo. Existem sequências complicadas de métodos de ciclo de vida aqui que ficam confusas.

Métodos do ciclo de vida

Nosso teste para iOS 10 revelou as seguintes sequências de métodos de ciclo de vida para os vários casos:

DELEGATE METHODS CALLED WHEN OPENING APP  

Opening app when system killed or user killed  
    didFinishLaunchingWithOptions  
    applicationDidBecomeActive    

Opening app when backgrounded  
    applicationWillEnterForeground  
    applicationDidBecomeActive  

DELEGATE METHODS WHEN OPENING PUSH

Opening push when system killed
    [receiving push causes didFinishLaunchingWithOptions (with options) and didReceiveRemoteNotification:background]
    applicationWillEnterForeground
    didReceiveRemoteNotification:inactive
    applicationDidBecomeActive

Opening push when user killed
    didFinishLaunchingWithOptions (with options)
    didReceiveRemoteNotification:inactive [only completionHandler version]
    applicationDidBecomeActive

Opening push when backgrounded
    [receiving push causes didReceiveRemoteNotification:background]
    applicationWillEnterForeground
    didReceiveRemoteNotification:inactive
    applicationDidBecomeActive

O problema

Ok, agora precisamos:

  1. Determinar se o usuário está abrindo o aplicativo a partir de um push
  2. Atualize a visualização com base no estado de envio
  3. Limpe o estado para que as aberturas subsequentes não retornem o usuário à mesma posição.

O mais complicado é que a atualização da exibição deve ocorrer quando o aplicativo realmente se torna ativo, que é o mesmo método de ciclo de vida em todos os casos.

Esboço da nossa solução

Aqui estão os principais componentes da nossa solução:

  1. Armazene uma notificationUserInfovariável de instância no AppDelegate.
  2. Situado notificationUserInfo = nilem ambos applicationWillEnterForegrounde didFinishLaunchingWithOptions.
  3. Situado notificationUserInfo = userInfoemdidReceiveRemoteNotification:inactive
  4. Desde applicationDidBecomeActivesempre chamar um método personalizado openViewFromNotificatione passar self.notificationUserInfo. Se self.notificationUserInfofor nulo, retorne mais cedo; caso contrário, abra a exibição com base no estado de notificação encontrado em self.notificationUserInfo.

Explicação

Ao abrir a partir de um push didFinishLaunchingWithOptionsou applicationWillEnterForegroundsempre é chamado imediatamente antes didReceiveRemoteNotification:inactive, primeiro redefinimos o notificationUserInfo nesses métodos para que não haja estado obsoleto. Então, se didReceiveRemoteNotification:inactiveé chamado, sabemos que estamos abrindo a partir de um push, então definimos o self.notificationUserInfoque é captado porapplicationDidBecomeActive para encaminhar o usuário para a visualização correta.

Há um caso final: o usuário tem o aplicativo aberto no alternador de aplicativos (ou seja, tocando duas vezes no botão de início enquanto o aplicativo está em primeiro plano) e recebe uma notificação por push. Apenas neste casodidReceiveRemoteNotification:inactive é chamado e nem WillEnterForeground nem didFinishLaunching são chamados, portanto, você precisa de algum estado especial para lidar com esse caso.

Espero que isto ajude.

Eric Conner
fonte
Finalmente algo que funciona, obrigado! Eu queria criar um sinalizador "appResuming" e abrir a tela nos receivemétodos quando o estado do aplicativo estiver ativo ou o aplicativo estiver sendo retomado. Isso pode causar problemas com a alteração de VCs quando o aplicativo ainda está inativo. Sua solução parece ótima, até que a Apple altere o ciclo de vida novamente.
shelll
E no iOS 9, os métodos do ciclo de vida são chamados da mesma maneira e ordem? Eu já não tenho dispositivos iOS 9, portanto não posso testá-lo adequadamente.
shelll
2
Existem mais dois casos extremos, exceto o alternador de aplicativos. 1) Quando o centro de notificação é puxado de cima e sobrepõe o aplicativo 2) Quando o painel do iOS com wifi / BT / etc é puxado de baixo e sobrepõe o aplicativo. Nos três casos, apenas o applicationWillResignActiveé chamado e depois o applicationDidBecomeActive. Portanto, após o applicationWillResignActivechamado, não salve a notificação recebida até que um applicationDidEnterBackgroundou applicationDidBecomeActiveseja chamado.
shelll
Obrigado por adicionar esses casos @shelll. Sempre fica mais complicado! Não tenho certeza sobre o iOS9. Eu diria que provavelmente é seguro assumir que eles são iguais, mas quem sabe.
Eric Conner
Apenas um aviso. Eu estava testando o iOS 11 Beta 9 hoje e descobri que, no caso de seu aplicativo estar em primeiro plano, bloquear o telefone e selecionar uma notificação por push na tela de bloqueio, ele chama didReceiveRemoteNotification: background antes de chamar applicationWillEnterForeground em vez de o que estamos vendo no iOS 10, onde ele chama applicationWillEnterForeground e, em seguida, didReceiveRemoteNotification: inactive - portanto, esse é um caso extremo ainda não coberto. Na minha opinião, isso é um bug no código do iOS, mas, considerando a proximidade do lançamento do iOS 11, é algo para estar ciente.
Roy
24

Esta é uma publicação bem usada ... mas ainda falta uma solução real para o problema (como é apontado nos vários comentários).

A pergunta original é sobre a detecção de quando o aplicativo foi iniciado / aberto a partir de uma notificação por push, por exemplo , um usuário toca na notificação. Nenhuma das respostas realmente cobre esse caso.

O motivo pode ser visto no fluxo de chamadas quando uma notificação chega, application:didReceiveRemoteNotification...

é chamado quando a notificação é recebida E novamente quando a notificação é tocada pelo usuário. Por causa disso, você não pode dizer apenas olhando se UIApplicationStateo usuário tocou nele.

Além disso, você não precisa mais lidar com a situação de 'partida a frio' do aplicativo, application:didFinishLaunchingWithOptions...como application:didReceiveRemoteNotification...é chamado novamente após o lançamento no iOS 9+ (talvez 8 também).

Então, como você pode saber se o usuário tocou na cadeia de eventos? Minha solução é marcar o horário em que o aplicativo começa a sair do segundo plano ou iniciar a frio e verificar o horário application:didReceiveRemoteNotification.... Se for menor que 0,1s, você pode ter certeza de que o toque acionou a inicialização.

Swift 2.x

class AppDelegate: UIResponder, UIApplicationDelegate {

  var wakeTime : NSDate = NSDate()        // when did our application wake up most recently?

  func applicationWillEnterForeground(application: UIApplication) {    
    // time stamp the entering of foreground so we can tell how we got here
    wakeTime = NSDate()
  }

  func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
    // ensure the userInfo dictionary has the data you expect
    if let type = userInfo["type"] as? String where type == "status" {
      // IF the wakeTime is less than 1/10 of a second, then we got here by tapping a notification
      if application.applicationState != UIApplicationState.Background && NSDate().timeIntervalSinceDate(wakeTime) < 0.1 {
        // User Tap on notification Started the App
      }
      else {
        // DO stuff here if you ONLY want it to happen when the push arrives
      }
      completionHandler(.NewData)
    }
    else {
      completionHandler(.NoData)
    }
  }
}

Swift 3

class AppDelegate: UIResponder, UIApplicationDelegate {

    var wakeTime : Date = Date()        // when did our application wake up most recently?

    func applicationWillEnterForeground(_ application: UIApplication) {
      // time stamp the entering of foreground so we can tell how we got here
      wakeTime = Date()
    }

  func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {

      // ensure the userInfo dictionary has the data you expect
      if let type = userInfo["type"] as? String, type == "status" {
        // IF the wakeTime is less than 1/10 of a second, then we got here by tapping a notification
        if application.applicationState != UIApplicationState.background && Date().timeIntervalSince(wakeTime) < 0.1 {
          // User Tap on notification Started the App
        }
        else {
          // DO stuff here if you ONLY want it to happen when the push arrives
        }
        completionHandler(.newData)
      }
      else {
        completionHandler(.noData)
      }
    }
}

Eu testei isso nos dois casos (aplicativo em segundo plano, aplicativo não em execução) no iOS 9 ou superior e funciona como um encanto. 0.1s também é bastante conservador, o valor real é ~ 0.002s, então 0.01 também é bom.

MobileVet
fonte
1
Essa parece ser a única solução funcional que diferencia entre tocar na notificação e abrir a barra de status no aplicativo.
Liviucmg
4
Esta é a única solução funcional de todo o StackOverflow. A única coisa que gostaria de acrescentar é que, quando você oferece suporte ao iOS 10 e superior, basta usar a UNNotificationCenterAPI, especificamente os métodos UNNotificationCenterDelegate. Essas API chamam o userNotificationCenter(UNUserNotificationCenter, didReceive: UNNotificationResponse, withCompletionHandler: @escaping () -> Void) método func apenas quando o usuário realmente toca na notificação.
DenHeadless
como ele procura o swift 3?
Jochen Österreicher
A solução não funciona enquanto um aplicativo está no estado inativo (o usuário desliza para baixo no centro de notificação ou desliza para cima no centro de controle) e recebe uma notificação. Quando o usuário toca na notificação, o aplicativo não recebe applicationWillEnterForeground chamadas, como resultado, a solução falha ao detectar a torneira.
precisa saber é o seguinte
@DevGansta Quando você adiciona sua turma como UNUserNotificationCenter.current().delegateem application:didFinishLaunchingWithOptions, o aplicativo chama userNotificationCenter(didReceive response)após a torneira no caso que você descreveu
Dorian Roy
22

Quando o aplicativo é encerrado e o usuário toca na notificação por push

public func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
   if launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] != nil {
      print("from push")
    }
}

Quando o aplicativo está em segundo plano e o usuário toca na notificação por push

Se o usuário abrir o aplicativo a partir do alerta exibido pelo sistema, o sistema poderá chamar esse método novamente quando o aplicativo estiver prestes a entrar em primeiro plano, para que você possa atualizar sua interface com o usuário e exibir informações referentes à notificação.

public func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
  if application.applicationState == .inactive {
    print("from push")
  }
}

Dependendo do seu aplicativo, ele também pode enviar push silencioso com o content-availableinterior aps, então esteja ciente disso também :) Consulte https://stackoverflow.com/a/33778990/1418457

onmyway133
fonte
2
Apenas responda que não parece um truque sujo e correto. O que estou perdendo é se o aplicativo estiver em segundo plano e o usuário o abrir manualmente, como verificar isso? Enquanto ainda é possível verificar a partida a frio e o envio a partir do plano de fundo.
Jochen Österreicher
1
@ JochenÖsterreicher Oi, eu resumo aqui, por favor verifique medium.com/@onmyway133/…
onmyway133
19

Swift 2.0 para o estado 'Não em execução' (notificação local e remota)

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {


// Handle notification
if (launchOptions != nil) {

    // For local Notification
    if let localNotificationInfo = launchOptions?[UIApplicationLaunchOptionsLocalNotificationKey] as? UILocalNotification {

        if let something = localNotificationInfo.userInfo!["yourKey"] as? String {
            self.window!.rootViewController = UINavigationController(rootViewController: YourController(yourMember: something))
        }


    } else

    // For remote Notification
    if let remoteNotification = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] as! [NSObject : AnyObject]? {

        if let something = remoteNotification["yourKey"] as? String {
            self.window!.rootViewController = UINavigationController(rootViewController: YourController(yourMember: something))
        }
    }

}


return true
}
Włodzimierz Woźniak
fonte
15

No application:didReceiveRemoteNotification:cheque se você recebeu a notificação quando o aplicativo está em primeiro ou segundo plano.

Se foi recebido em segundo plano, inicie o aplicativo a partir da notificação.

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) {
        NSLog(@"Notification received by running app");
    } else {
        NSLog(@"App opened from Notification");
    }
}
Madhu
fonte
3
Observe que "O aplicativo aberto a partir da notificação" será um falso positivo se a notificação for enviada enquanto o usuário estiver em uma tela diferente (por exemplo, se eles abrirem a barra de status e receberem uma notificação do seu aplicativo).
Kevin Cooper
4
@ Kevin Exatamente. Ele faz você se perguntar por que a Apple aparentemente colocar um estagiário para projetar o processo de tratamento das notificações de ...
Andreas
como podemos detectar se nós batemos na notificação recebida no estado ativo
Mayank Jain
13

Para rápida:

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
    PFPush.handlePush(userInfo)

    if application.applicationState == UIApplicationState.Inactive || application.applicationState == UIApplicationState.Background {
        //opened from a push notification when the app was in the background

    }

}
LondonGuy
fonte
4

Sim, você pode detectar por esse método no appDelegate :

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
      /* your Code*/
}

Para Notificação local:

- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification
{
         /* your Code*/
}
Impossível
fonte
1
Este método não é chamado se o aplicativo não estiver sendo executado. Isso é o que pedimos aqui #
586
Meu problema não é lidar com a notificação, mas sim saber se ela foi aberta quando você clica no banner (quando o aplicativo está em segundo plano).
João
3

se alguém quiser a resposta rapidamente 3

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
    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
    }
}
Hamid Shahsavari
fonte
mas como saber se a aplicação é aberta tocando notificação de envio quando o aplicativo é encerrado
user3804063
1
quando alguém pressionar o botão, o aplicativo será aberto, independentemente de ter sido encerrado ou não. e o caso .inactive está chamando
Hamid Shahsavari
1
Eu preciso para detectar se o aplicativo é aberto gravando o impulso e quer navegar para o conteúdo respectivo vi instagram fazendo isso
user3804063
E as notificações locais?
Amir Shabani
3

Publicando isso para usuários do Xamarin.

A chave para detectar se o aplicativo foi iniciado por meio de uma notificação por push é o AppDelegate.FinishedLaunching(UIApplication app, NSDictionary options)método e o dicionário de opções que é passado.

O dicionário opções terão esta chave nele se é uma notificação local: UIApplication.LaunchOptionsLocalNotificationKey.

Se for uma notificação remota, será UIApplication.LaunchOptionsRemoteNotificationKey.

Quando a chave é LaunchOptionsLocalNotificationKey, o objeto é do tipo UILocalNotification. Você pode examinar a notificação e determinar qual é a notificação específica.

Dica profissional: UILocalNotificationnão possui um identificador, da mesma forma UNNotificationRequest. Coloque uma chave de dicionário na UserInfo contendo um requestId para que, ao testar o UILocalNotification, você tenha um requestId específico disponível para basear alguma lógica.

Eu descobri que, mesmo em dispositivos iOS 10+ que, ao criar notificações de localizações usando o UNUserNotificationCenter's AddNotificationRequest& UNMutableNotificationContent, que quando o aplicativo não está sendo executado (eu o matei)), é iniciado tocando na notificação no centro de notificações, que o dicionário ainda contém o UILocalNotificaitonobjeto

Isso significa que meu código que verifica o lançamento baseado em notificação funcionará em dispositivos iOS8 e iOS 10+

public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
    _logger.InfoFormat("FinishedLaunching");

    if(options != null)
    {
        if (options.ContainsKey(UIApplication.LaunchOptionsLocalNotificationKey))
        {
            //was started by tapping a local notification when app wasn't previously running.
            //works if using UNUserNotificationCenter.Current.AddNotificationRequest OR UIApplication.SharedApplication.PresentLocalNotificationNow);

            var localNotification = options[UIApplication.LaunchOptionsLocalNotificationKey] as UILocalNotification;

            //I would recommended a key such as this :
            var requestId = localNotification.UserInfo["RequestId"].ToString();
        }               
    }
    return true;
}
Wes
fonte
2

Diretamente da documentação para

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo:nil

Se o aplicativo estiver em execução e receber uma notificação remota, o aplicativo chamará esse método para processar a notificação.

Sua implementação desse método deve usar a notificação para executar um curso de ação apropriado.

E um pouco depois

Se o aplicativo não estiver em execução quando uma notificação por push chegar, o método iniciará o aplicativo e fornecerá as informações apropriadas no dicionário de opções de inicialização.

O aplicativo não chama esse método para lidar com essa notificação por push.

Em vez disso, sua implementação do

application:willFinishLaunchingWithOptions:

ou

application:didFinishLaunchingWithOptions:

O método precisa obter os dados da carga útil da notificação por push e responder adequadamente.

Pfitz
fonte
2

Começarei com um gráfico de estados que criei para meu próprio uso para visualizá-lo com mais precisão e considerar todos os outros estados: https://docs.google.com/spreadsheets/d/e/2PACX-1vSdKOgo_F1TZwGJBAED4C_7cml0bEATqeL3P9UKpBwASlTengZOUZi3DL4ZO3 ? gid = 0 & single = true

Usando esse gráfico, podemos ver o que é realmente necessário para desenvolver um sistema robusto de manipulação de notificações que funcione em quase todos os casos de uso possíveis.

Solução completa ↓

  • Armazenar carga útil de notificação em didReceiveRemoteNotification
  • Limpar notificação armazenada no applicationWillEnterForeground e didFinishLaunchingWithOptions
  • Para lidar com casos em que o Centro de controle / centro de notificação é acionado , você pode usar um sinalizador willResignActiveCalled e defini-lo como false inicialmente. Defina como true no método applicationWillResignActive .
  • No método didReceiveRemoteNotification , salve as notificações (userInfo) somente quando willResignActiveCalled for false.
  • Repor willResignActiveCalled para false no applicationDidEnterBackground e applicationDidBecomeActive método.

Nota: Uma resposta semelhante é sugerida nos comentários da resposta de Eric, no entanto, a folha de estado ajuda a encontrar todos os cenários possíveis, como eu fiz no meu aplicativo.

Encontre o código completo abaixo e comente abaixo se algum caso específico não for tratado:

AppDelegate

class AppDelegate: UIResponder, UIApplicationDelegate {
  private var willResignActiveCalled = false

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    NotificationUtils.shared.notification = nil
    return true
  }
  func applicationWillResignActive(_ application: UIApplication) {
    willResignActiveCalled = true
  }
  func applicationDidEnterBackground(_ application: UIApplication) {
    willResignActiveCalled = false
  }
  func applicationWillEnterForeground(_ application: UIApplication) {
    NotificationUtils.shared.notification = nil
  }
  func applicationDidBecomeActive(_ application: UIApplication) {
    willResignActiveCalled = false
    NotificationUtils.shared.performActionOnNotification()
  }
  func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    if !willResignActiveCalled { // Check if app is in inactive by app switcher, control center, or notification center
      NotificationUtils.shared.handleNotification(userInfo: userInfo)
    }
  }
}

NotificationUtils : é aqui que você pode escrever todo o seu código para navegar para diferentes partes do aplicativo, manipular bancos de dados (CoreData / Realm) e fazer todo o resto que precisa ser feito quando uma notificação é recebida.

   class NotificationUtils {
  static let shared = NotificationUtils()
  private init() {}

  var notification : [AnyHashable: Any]?

  func handleNotification(userInfo : [AnyHashable: Any]){
    if UIApplication.shared.applicationState == UIApplicationState.active {
      self.notification = userInfo //Save Payload
      //Show inApp Alert/Banner/Action etc
      // perform immediate action on notification
    }
    else if UIApplication.shared.applicationState == UIApplicationState.inactive{
      self.notification = userInfo
    }
    else if UIApplication.shared.applicationState == UIApplicationState.background{
      //Process notification in background,
      // Update badges, save some data received from notification payload in Databases (CoreData/Realm)
    }
  }

  func performActionOnNotification(){
    // Do all the stuffs like navigating to ViewControllers, updating Badges etc
    defer {
      notification = nil
    }
  }
}
chetan anand
fonte
melhor colocar isso como um comentário, já que essa não é a resposta.
Maddy
@ Maddy Obrigado pela sugestão, Atualizada a resposta com todos os detalhes
chetan anand
1
func application(_ application: UIApplication, didReceiveRemoteNotification data: [AnyHashable : Any]) {
    print("Push notification received: \(data)")

    if let info = data["aps"] as? Dictionary<String, AnyObject> {
        let alertMsg = info["alert"] as! String
        print(alertMsg)
        switch application.applicationState {
        case .active:
            print("do stuff in case App is active")
        case .background:
            print("do stuff in case App is in background")
           // navigateToChatDetailViewControler(pushdata: data)
        case .inactive:
            print("do stuff in case App is inactive")
            // navigateToChatDetailViewControler(pushdata: data)
        }
    }
}
Hardik Bar
fonte
1

Existe apenas uma maneira confiável e funciona apenas no iOS 10 ou superior :

Usando o método de UNUserNotificationCenterimplementação UNUserNotificationCenterDelegate:

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

    //Here you can get your original push if you need to
    NSDictionary* pusDict = response.notification.request.content.userInfo;

    if ([response.actionIdentifier isEqualToString: UNNotificationDefaultActionIdentifier]) {
        //User tapped the notification
    } else if ([response.actionIdentifier isEqualToString: UNNotificationDismissActionIdentifier]) {
        //User dismissed the notification 
    } else if ([response.actionIdentifier isEqualToString: MYCustomActionId]) {
        //User chose my custom defined action
    }
    ...
}
Luten
fonte
0

Você pode usar:

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo

para lidar com as notificações push remotas.

Confira aqui a documentação

feliz
fonte
0
     // shanegao's code in Swift 2.0
     func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject])
    {
            if ( application.applicationState == UIApplicationState.Inactive || application.applicationState == UIApplicationState.Background ){
                    print("opened from a push notification when the app was on background")
            }else{
                    print("opened from a push notification when the app was on foreground")
            }
    }
Sean Dev
fonte
Mas e se o aplicativo foi fechado (encerrado). Como o Twitter ou Instagram-lo de alguma forma, detecta-lo e se o aplicativo é ainda fechou redireciona para nova mensagem ou as imagens ou o seu perfil etc ..
Tarvo Mäesepp
0

O problema com esta pergunta é que "abrir" o aplicativo não está bem definido. Um aplicativo é iniciado a frio a partir de um estado de não execução ou é reativado a partir de um estado inativo (por exemplo, ao voltar para outro aplicativo). Aqui está minha solução para distinguir todos esses estados possíveis:

typedef NS_ENUM(NSInteger, MXAppState) {
    MXAppStateActive = 0,
    MXAppStateReactivated = 1,
    MXAppStateLaunched = 2
};

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // ... your custom launch stuff
    [[MXDefaults instance] setDateOfLastLaunch:[NSDate date]];
    // ... more custom launch stuff
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    // Through a lot of trial and error (by showing alerts), I can confirm that on iOS 10
    // this method is only called when the app has been launched from a push notification
    // or when the app is already in the Active state.  When you receive a push
    // and then launch the app from the icon or apps view, this method is _not_ called.
    // So with 99% confidence, it means this method is called in one of the 3 mutually exclusive cases
    //    1) we are active in the foreground, no action was taken by the user
    //    2) we were 'launched' from an inactive state (so we may already be in the main section) by a tap
    //       on a push notification
    //    3) we were truly launched from a not running state by a tap on a push notification
    // Beware that cases (2) and (3) may both show UIApplicationStateInactive and cant be easily distinguished.
    // We check the last launch date to distinguish (2) and (3).

    MXAppState appState = [self mxAppStateFromApplicationState:[application applicationState]];
    //... your app's logic
}

- (MXAppState)mxAppStateFromApplicationState:(UIApplicationState)state {
    if (state == UIApplicationStateActive) {
        return MXAppStateActive;
    } else {
        NSDate* lastLaunchDate = [[MXDefaults instance] dateOfLastLaunch];
        if (lastLaunchDate && [[NSDate date] timeIntervalSinceDate:lastLaunchDate] < 0.5f) {
            return MXAppStateLaunched;
        } else {
            return MXAppStateReactivated;
        }
    }
    return MXAppStateActive;
}

E MXDefaultsé apenas um pequeno invólucro para NSUserDefaults.

Skensell
fonte
0

Para swift

 func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]){

    ++notificationNumber
    application.applicationIconBadgeNumber =  notificationNumber;

    if let aps = userInfo["aps"] as? NSDictionary {

        var message = aps["alert"]
        println("my messages : \(message)")

    }
}
idris yıldız
fonte
0

Xcode 10 Swift 4.2

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {

    let state : UIApplicationState = application.applicationState
    if (state == .Inactive || state == .Background) {
        // coming from background
    } else {
        // App is running in foreground
    }
}
Prashant Gaikwad
fonte
0

Para o iOS 10 ou superior, você pode usar esse método para saber quando sua notificação é clicada, independentemente do estado do aplicativo.

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

    //Notification clicked
    completionHandler()
}
manishsharma93
fonte
0

A resposta de M.Othman está correta para aplicativos que não contêm delegado de cena Para aplicativos de delegado de cena Isso funcionou para mim no iOS 13

Aqui está o código para o qual deve ser escrito a cena de conexão

if connectionOptions.notificationResponse == nil { 
//Not opened from push notification
} else {
  //Opened from push notification
}

Código para o delegado do aplicativo dar suporte a versões anteriores didFinishLaunchingWithOptions

let notification = launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification]
        if (notification != nil) {

            //Launched from push notification
        } else {

            //Launch from other source
        }
Noman Haroon
fonte
-1

Para usuários rápidos:

Se você deseja iniciar uma página diferente na abertura do push ou algo assim, é necessário fazer check-in didFinishLaunchingWithOptionscomo:

let directVc: directVC! = directVC(nibName:"directVC", bundle: nil)
let pushVc: pushVC! = pushVC(nibName:"pushVC", bundle: nil)

if let remoteNotification = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] as? NSDictionary {
     self.navigationController = UINavigationController(rootViewController: pushVc!)
} else {
     self.navigationController = UINavigationController(rootViewController: directVc!)
}
self.window!.rootViewController = self.navigationController
AAA
fonte
O delegado não tem navegação de
membroController
1
Crie um controlador de navegação no arquivo AppDelegate.h. Estou usando e funciona!
AAA
-1

EM SWIFT:

Estou executando o Push Notifications (com busca em segundo plano). Quando meu aplicativo está em segundo plano e recebo uma notificação por push, descobri que didReceiveRemoteNotification em appDelegate seria chamado duas vezes; uma vez quando a notificação é recebida e outra quando o usuário clica no alerta de notificação.

Para detectar se o alerta de notificação foi clicado, verifique se o valor bruto applicationState == 1 dentro do didReceiveRemoteNotification no appDelegate.

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject: AnyObject]) {
    // If not from alert click applicationState(1)
    if (application.applicationState.rawValue != 1) {
        // Run your code here
    }
}

Eu espero que isso ajude.

Johnny5
fonte
-1

Quando o aplicativo está em segundo plano como Shanegao, você pode usar

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    if ( application.applicationState == UIApplicationStateInactive || application.applicationState == UIApplicationStateBackground  )
    {
         //opened from a push notification when the app was on background
    }
}

Mas se você deseja iniciar o aplicativo e quando o aplicativo é fechado e deseja depurá-lo, você pode ir para Editar esquema e, no menu esquerdo, selecione Executar e, em seguida, selecione Iniciar Aguardar o lançamento do executável. e iniciar o aplicativo quando você clique na notificação push

Editar esquema> Executar> Aguarde o lançamento do executável

salmancs43
fonte