Tester si l’application est devenue active à partir d’une notification UILocalNotification

Y a-t-il un moyen de savoir si l’application est devenue active à partir d’une notification locale?

Je sais qu’il existe un moyen de tester si l’application a été lancée à partir d’une alerte de notification locale; mais s’il était juste assis à l’arrière-plan et reçu une notification?

J’ai besoin d’exécuter du code différent lorsque l’application est devenue active:

  1. A partir d’une notification locale.
  2. Juste est devenu actif 🙂

Y a-t-il un moyen de le faire?

Je crains que Sylter ne soit pas correct. Lorsqu’une application entre en premier plan à partir de l’arrière-plan , soit par une action directe de l’utilisateur, soit par une réponse de l’utilisateur à une UILocalNotification , elle ne déclenche pas l’ applicationDidFinishLaunchingWithOptions . Il appelle cependant applicationWillEnterForeground et applicationDidBecomeActive . Cela peut être vérifié avec quelques NSLogs .

Le problème demeure donc: si une application entre en premier plan en arrière-plan, il est impossible de savoir si l’application entre en premier plan en réponse à la réponse d’un utilisateur à UILocalNotification ou si elle entre simplement en avant-plan. Sauf…

Une fois que l’application est entrée au premier plan, elle recevra l’ application:DidReceiveLocalNotification la méthode application:DidReceiveLocalNotification : si l’application est entrée au premier plan en réponse à une UILocalNotification .

Le problème est que toute modification de l’interface utilisateur effectuée dans l’ application:DidReceiveLocalNotification: méthode en réponse à la réception de UILocalNotification se produit après que l’application a déjà pénétré au premier plan, créant une expérience inquiétante pour l’utilisateur.

Est-ce que quelqu’un a trouvé une solution?

J’ai trouvé la solution à cette question dans le conseil de @ naveed sur la vérification de l’état de l’application lorsque la méthode didReceiveNotification est appelée. Pas besoin de vérifier les variables, etc. lorsque l’application reprend depuis l’arrière-plan.

Sur iOS7 et versions inférieures, vous gérez les notifications comme ceci:

 - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { if (application.applicationState == UIApplicationStateInactive ) { //The application received the notification from an inactive state, ie the user tapped the "View" button for the alert. //If the visible view controller in your view controller stack isn't the one you need then show the right one. } if(application.applicationState == UIApplicationStateActive ) { //The application received a notification in the active state, so you can display an alert view or do something appropriate. } } 

Mise à jour pour iOS 8: les méthodes suivantes sont désormais appelées lorsque l’application est ouverte en arrière-plan via une notification.

 - (void)application:(UIApplication *)application handleActionWithIdentifier:(NSSsortingng *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void(^)())completionHandler { } - (void)application:(UIApplication *)application handleActionWithIdentifier:(NSSsortingng *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void(^)())completionHandler { } 

Si les notifications sont reçues alors que l’application est au premier plan, utilisez les méthodes suivantes:

 - (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *) userInfo { } - (void) application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { } 

Notez qu’il n’est pas nécessaire de vérifier l’état de l’application, sauf si vous souhaitez prendre en charge d’anciennes versions du système d’exploitation de votre application.

Vous pouvez vérifier les scénarios de l’une ou l’autre application en cours d’exécution ou non lorsque l’application reçue en suivant.

 - (void)application:(UIApplication *)app didReceiveLocalNotification:(UILocalNotification *)notif { if (app.applicationState == UIApplicationStateInactive ) { NSLog(@"app not running"); }else if(app.applicationState == UIApplicationStateActive ) { NSLog(@"app running"); } // Handle the notificaton when the app is running NSLog(@"Recieved Notification %@",notif); } 

Ce que j’ai fait, j’ai testé deux scénarios, l’un est de remettre l’application au premier plan en cliquant sur l’icône, une autre par URL sys call, et de comparer toutes les variables dans UIApplication, et étonnamment j’ai finalement trouvé ce que je cherchais dans UIApplication.h:

 struct { unsigned int isActive:1; unsigned int isSuspended:1; unsigned int isSuspendedEventsOnly:1; unsigned int isLaunchedSuspended:1; unsigned int calledNonSuspendedLaunchDelegate:1; unsigned int isHandlingURL:1; unsigned int isHandlingRemoteNotification:1; unsigned int isHandlingLocalNotification:1; unsigned int statusBarShowsProgress:1; unsigned int statusBarRequestedStyle:4; unsigned int statusBarHidden:1; unsigned int blockInteractionEvents:4; unsigned int receivesMemoryWarnings:1; unsigned int showingProgress:1; unsigned int receivesPowerMessages:1; unsigned int launchEventReceived:1; unsigned int isAnimatingSuspensionOrResumption:1; unsigned int isResuming:1; unsigned int isSuspendedUnderLock:1; unsigned int isRunningInTaskSwitcher:1; unsigned int shouldExitAfterSendSuspend:1; unsigned int shouldExitAfterTaskCompletion:1; unsigned int terminating:1; unsigned int isHandlingShortCutURL:1; unsigned int idleTimerDisabled:1; unsigned int deviceOrientation:3; unsigned int delegateShouldBeReleasedUponSet:1; unsigned int delegateHandleOpenURL:1; unsigned int delegateOpenURL:1; unsigned int delegateDidReceiveMemoryWarning:1; unsigned int delegateWillTerminate:1; unsigned int delegateSignificantTimeChange:1; unsigned int delegateWillChangeInterfaceOrientation:1; unsigned int delegateDidChangeInterfaceOrientation:1; unsigned int delegateWillChangeStatusBarFrame:1; unsigned int delegateDidChangeStatusBarFrame:1; unsigned int delegateDeviceAccelerated:1; unsigned int delegateDeviceChangedOrientation:1; unsigned int delegateDidBecomeActive:1; unsigned int delegateWillResignActive:1; unsigned int delegateDidEnterBackground:1; unsigned int delegateWillEnterForeground:1; unsigned int delegateWillSuspend:1; unsigned int delegateDidResume:1; unsigned int userDefaultsSyncDisabled:1; unsigned int headsetButtonClickCount:4; unsigned int isHeadsetButtonDown:1; unsigned int isFastForwardActive:1; unsigned int isRewindActive:1; unsigned int disableViewGroupOpacity:1; unsigned int disableViewEdgeAntialiasing:1; unsigned int shakeToEdit:1; unsigned int isClassic:1; unsigned int zoomInClassicMode:1; unsigned int ignoreHeadsetClicks:1; unsigned int touchRotationDisabled:1; unsigned int taskSuspendingUnsupported:1; unsigned int isUnitTests:1; unsigned int requiresHighResolution:1; unsigned int disableViewContentScaling:1; unsigned int singleUseLaunchOrientation:3; unsigned int defaultInterfaceOrientation:3; } _applicationFlags; 

Cela contient peut-être toutes les informations auxquelles un programmeur souhaite avoir access lorsque l’application retourne au premier plan, pour être en particulier, je voudrais accéder à l’indicateur “isHandlingURL” qui dit 1 si l’application est mise au premier plan par un sys- call, 0 si l’application est mise au premier plan par l’utilisateur.

Ensuite, j’ai regardé l’adresse de “application” et “_applicationFlags”, remarquant qu’elles étaient décalées de 0x3C, qui est de 60, j’ai donc décidé d’utiliser les opérations d’adresse pour obtenir mon bit nécessaire:

 - (void)applicationWillEnterForeground:(UIApplication *)application { /* Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. */ id* app = [UIApplication sharedApplication]; app = app+15; //address increments by long words, don't know if it will be the same on device NSLog(@"Test:%x",*app); } 

qui imprime le test: 4a40012 ou 0x04a40012 si j’écris dans un format de mot long complet. Cela me donne en binary 0000 0100 1010 0100 0000 00001 0010 . En regardant dans _applicationFlags, cela nous donnera “isHandlingURL” sur le bit 6 de LSB, qui est 0. Maintenant, si j’essaie de mettre l’application en arrière-plan et de la ramener avec un appel sys URL, je reçois une impression de 4a40032 en binary est 0000 0100 1010 0100 0000 0000 0011 0010 et j’ai mon bit isHandlingURL activé! Donc, tout ce qui rest à faire est de compléter l’instruction par des opérations de décalage de bits, et le code final ressemblera à ceci:

 - (void)applicationWillEnterForeground:(UIApplication *)application { /* Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. */ id* app = (id*)[UIApplication sharedApplication]+15; BOOL isHandlingURL = ((Byte)*app>>5&0x1); if (isHandlingURL) { //do whatever I wanna do here } } 

Je peux continuer et écrire une fonction complète pour parsingr tout le _applicationFlag, mais à ce stade, il est incertain si l’incrément d’adresse est fixé à 15 sur le simulateur et la cible, mon prochain objective sera de remplacer par de la magie number ’15’ par une macro définit ou des valeurs du système afin que je puisse être sûr qu’il décalera toujours 0x3C comme requirejs, et j’ai besoin de regarder dans l’en-tête UIApplication pour s’assurer que _applicationFlag sera toujours décalé de 0x3C.

C’est tout pour le moment!

Dans votre AppDelegate:

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. UILocalNotification *localNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey]; if (localNotif) { NSLog(@"Recieved Notification %@",localNotif); //Do Something } else { //Do Something else if I didn't recieve any notification, ie The app has become active } return YES; } 

Ou, si vous voulez savoir quand l’application est en avant-plan ou en arrière-plan, vous pouvez utiliser cette méthode:

 - (void)applicationWillResignActive:(UIApplication *)application { /* Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. */ } - (void)applicationDidEnterBackground:(UIApplication *)application { /* Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. If your application supports background execution, called instead of applicationWillTerminate: when the user quits. */ } - (void)applicationWillEnterForeground:(UIApplication *)application { /* Called as part of transition from the background to the active state: here you can undo many of the changes made on entering the background. */ } - (void)applicationDidBecomeActive:(UIApplication *)application { /* Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. */ } - (void)applicationWillTerminate:(UIApplication *)application { /* Called when the application is about to terminate. See also applicationDidEnterBackground:. */ } 

Ok, voici ma solution finale et élégante pour que vous puissiez accéder à _applicationFlags déclaré en tant que structure privée dans UIApplication. Commencez par créer un en-tête “ApplicationFlag.h”:

 // // ApplicationFlag.h // PHPConnectDemo // // Created by Paul on 5/18/11. // Copyright 2011 [email protected]. All rights reserved. // #import  #ifndef APP_FLAG #define APP_FLAG #define APP_FLAG_OFFSET 15 #endif struct appFlag { unsigned int isActive:1; unsigned int isSuspended:1; unsigned int isSuspendedEventsOnly:1; unsigned int isLaunchedSuspended:1; unsigned int calledNonSuspendedLaunchDelegate:1; unsigned int isHandlingURL:1; unsigned int isHandlingRemoteNotification:1; unsigned int isHandlingLocalNotification:1; unsigned int statusBarShowsProgress:1; unsigned int statusBarRequestedStyle:4; unsigned int statusBarHidden:1; unsigned int blockInteractionEvents:4; unsigned int receivesMemoryWarnings:1; unsigned int showingProgress:1; unsigned int receivesPowerMessages:1; unsigned int launchEventReceived:1; unsigned int isAnimatingSuspensionOrResumption:1; unsigned int isResuming:1; unsigned int isSuspendedUnderLock:1; unsigned int isRunningInTaskSwitcher:1; unsigned int shouldExitAfterSendSuspend:1; unsigned int shouldExitAfterTaskCompletion:1; unsigned int terminating:1; unsigned int isHandlingShortCutURL:1; unsigned int idleTimerDisabled:1; unsigned int deviceOrientation:3; unsigned int delegateShouldBeReleasedUponSet:1; unsigned int delegateHandleOpenURL:1; unsigned int delegateOpenURL:1; unsigned int delegateDidReceiveMemoryWarning:1; unsigned int delegateWillTerminate:1; unsigned int delegateSignificantTimeChange:1; unsigned int delegateWillChangeInterfaceOrientation:1; unsigned int delegateDidChangeInterfaceOrientation:1; unsigned int delegateWillChangeStatusBarFrame:1; unsigned int delegateDidChangeStatusBarFrame:1; unsigned int delegateDeviceAccelerated:1; unsigned int delegateDeviceChangedOrientation:1; unsigned int delegateDidBecomeActive:1; unsigned int delegateWillResignActive:1; unsigned int delegateDidEnterBackground:1; unsigned int delegateWillEnterForeground:1; unsigned int delegateWillSuspend:1; unsigned int delegateDidResume:1; unsigned int userDefaultsSyncDisabled:1; unsigned int headsetButtonClickCount:4; unsigned int isHeadsetButtonDown:1; unsigned int isFastForwardActive:1; unsigned int isRewindActive:1; unsigned int disableViewGroupOpacity:1; unsigned int disableViewEdgeAntialiasing:1; unsigned int shakeToEdit:1; unsigned int isClassic:1; unsigned int zoomInClassicMode:1; unsigned int ignoreHeadsetClicks:1; unsigned int touchRotationDisabled:1; unsigned int taskSuspendingUnsupported:1; unsigned int isUnitTests:1; unsigned int requiresHighResolution:1; unsigned int disableViewContentScaling:1; unsigned int singleUseLaunchOrientation:3; unsigned int defaultInterfaceOrientation:3; }; @interface ApplicationFlag : NSObject { struct appFlag* _flags; } @property (nonatomic,assign) struct appFlag* _flags; @end 

Puis créez une implémentation “ApplicationFlag.m”:

 // // ApplicationFlag.m // PHPConnectDemo // // Created by Paul on 5/18/11. // Copyright 2011 [email protected]. All rights reserved. // #import "ApplicationFlag.h" @implementation ApplicationFlag @synthesize _flags; - (id)init { self = [super init]; if (self) { // Custom initialization _flags = (id*)[UIApplication sharedApplication]+APP_FLAG_OFFSET; } return self; } @end 

Ensuite, faites l’initialisation habituelle dans votre délégué d’application avec la propriété, synthétisez, incluez …

 applicationFlags = [[ApplicationFlag alloc] init]; 

Ensuite, vous pouvez commencer à vous référer aux drapeaux:

 - (void)applicationWillEnterForeground:(UIApplication *)application { /* Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. */ if (!applicationFlags._flags->isHandlingURL) { //Do whatever you want here } } 

Pour mieux comprendre le problème, je viens de tester le lancement de mon application à partir d’une notification locale et de contrôler l’ordre dans lequel les méthodes de délégation d’appel ont été appelées. Mes appareils de test étaient un iPod Touch 5ème génération sous iOS 7.1.1 et un iPhone 4S sous iOS 7.1.1. L’ordre des appels de méthode était le même pour les deux appareils.

Si l’application est simplement passée en arrière-plan, UILocalNotification sur une UILocalNotification pour lancer l’application appelle applicationWillEnterForeground: application:didReceiveLocalNotification: puis application:didReceiveLocalNotification: et enfin applicationDidBecomeActive: application:didReceiveLocalNotification: Notez que la séquence d’appels de méthodes est différente de la réponse de @jaredsinclair, qui a été écrite il y a quelques années et a probablement été testée sur une version différente d’iOS.

Si l’application est terminée (par iOS ou par l’utilisateur en supprimant l’application depuis le défilement latéral multitâche), UILocalNotification sur une UILocalNotification pour lancer à nouveau l’ applicationDidBecomeActive: appelle uniquement applicationDidBecomeActive: UILocalNotification L’ application:didReceiveLocalNotification: méthode application:didReceiveLocalNotification: N’EST PAS APPELÉE.

Comment j’ai testé la séquence de rappel de la méthode de délégué d’application: Dans le délégué d’application, j’ai créé un NSMutableArray et l’ai NSMutableArray avec une chaîne chaque fois applicationWillEnterForeground: application:didReceiveLocalNotification: et applicationDidBecomeActive: ont été appelés. Ensuite, j’ai affiché le contenu du tableau des deux dernières méthodes car je n’étais pas sûr de l’ordre dans lequel elles seraient appelées. Lorsque l’application vient de l’arrière-plan, c’est seulement lorsque j’obtiens deux UIAlertView , mais uniquement parce que les deux méthodes sont appelées l’une après l’autre.

Dans tous les cas, je voudrais également avancer que rien ne permet de savoir si l’application a été lancée depuis UILocalNotification si l’application provient d’un état terminé. Quelqu’un veut aider à confirmer en reproduisant le test?

 - (void)application:(UIApplication *)application didReceiveLocalNotification:(NSDictionary *)userInfo { if ( application.applicationState == UIApplicationStateActive ) // app was already in the foreground else // app was just brought from background to foreground }