Search code examples
iosuiviewcontrolleruilocalnotificationnsnotificationcenternsnotifications

Handle UILocalNotification in UIViewController using NSNotification after the app has been terminated


I'm working with UILocalNotification and I would like to notify one of my controller that the notification has been received even if the app has been terminated.

In my appDelegate I implemented this function:

-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
    if ([application applicationState] == UIApplicationStateInactive) {
        [[NSNotificationCenter defaultCenter] postNotificationName:@"localNotificationReceived" object:notification.userInfo];
    }
}

In my UIViewController I implemented the observer on the viewDidLoad method

- (void)viewDidLoad
{
    [super viewDidLoad];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didlocalNotificationReceived:) name:@"localNotificationReceived" object:nil];   

}

It works perfectly when the app run in background. Since the viewDidLoad method has already been called and the Observer is waiting..

The issue is when I kill the app. Then the observer of my controller is gone and the didlocalNotificationReceived method is never called.

I think that it's because when I receive the localNotification and run the app again. The didReceiveLocalNotification: method is called before the viewDidLoad of my UIViewController. Then the observer is created after the PostNotificationName then the observer receives nothing.

I would like to know if there is some best practices or pattern to handle this kind of issue.

I know that the didFinishLaunchingWithOptions method is called before didlocalNotificationReceived so there is probably something to do there.

UPDATE :

I also discovered that when is app is terminated. Once you tap the notification, It opens the app, call the function didFinishLaunchingWithOptions but never call didReceiveLocalNotification. So I think that I'm gonna handle both cases differently.


Solution

  • Ok I found the answer.

    I actually, initialize manually my storyboard, and be cautious that I initialize my main view before posting the NSNotification

    My didFinishLaunchingWithOptions: method in my appDelegate looks like that:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
    
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
        UIViewController *vc =[storyboard instantiateInitialViewController]; //call the initWithCoder: method of my controller
    
        if (launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]) {
            UILocalNotification *localNotification = launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
            [[NSNotificationCenter defaultCenter] postNotificationName:@"localNotificationReceived" object:localNotification.userInfo];
        }
    
        self.window.rootViewController = vc;
        [self.window makeKeyAndVisible];
    
        return YES;
    }
    

    Then in my UIViewController I create the NSNotification observer in the initWithCoder: method instead of in viewDidLoad:

    - (instancetype)initWithCoder:(NSCoder *)aDecoder {
        self = [super initWithCoder:aDecoder];
        if (self) {
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didlocalNotificationReceived:) name:@"localNotificationReceived" object:nil];
        }
        return self;
    }
    
    - (void)didlocalNotificationReceived:(NSNotification *)notification
    {
        //Execute whatever method when received local notification
    }
    

    And when the app is not killed I still use the didReceiveLocalNotification: method:

    -(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
    {
        if ([application applicationState] == UIApplicationStateInactive) {
            [[NSNotificationCenter defaultCenter] postNotificationName:@"localNotificationReceived" object:notification.userInfo];
        }
    }
    

    I'm not sure if it's the best practice. But it works well !

    Hope it'll help :)