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

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.

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 :)

Related

ios How to launch application in push notification

I am facing issue in launch of particular application page when user will get push notification in ios.
is this possible to do?
if yes, can you please help me in that.
You could send a notification to yourself when you get any remote notification and by registering the UIViewController to this notification, you could open a particular UIViewController once notification is received.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
[[NSNotificationCenter defaultCenter] postNotificationName:#"pushNotification" object:nil userInfo:userInfo];
}
In your FirstViewController.m register for listening to this notification.
-(void)viewDidLoad{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(pushNotificationReceived) name:#"pushNotification" object:nil];
}
Inside the method you could open particular viewController
-(void)pushNotificationReceived{
[self presentViewController:self.secondViewController animated:YES completion:nil];
}
Finally un-register current viewController from notification in dealloc method
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Let me know if you face any problem with this.

Open ViewController from didFinishLaunchingWithOptions

I'm sending push notification to app which is terminated and it seems that only this method is triggered. I want to open ViewController when app is launched with push notification, but it doesn't do anything just opens app.
I tried to achieve that with this code:
if (launchOptions != nil) {
NSDictionary* userInfo = [launchOptions valueForKey:#"UIApplicationLaunchOptionsRemoteNotificationKey"];
NSDictionary *apsInfo = [userInfo objectForKey:#"aps"];
if (apsInfo)
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"openNews" object:nil userInfo:userInfo];
}
}
and tried this as well ..
if (launchOptions != nil) {
NSDictionary* userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (userInfo != nil)
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"openNews" object:nil userInfo:userInfo];
}
}
Any ideas how to launch ViewController when app is terminated with push notification?
So I have tried to save notification info to NSUserDefaults if launchOptions != nil just to check if Im receiving notification info and that part of code is triggered and it is but for some reason this part is not working:
[[NSNotificationCenter defaultCenter] postNotificationName:#"openNews" object:nil userInfo:userInfo];
but Im using same method and everywhere and it works fine
That won't work because you are posting a notification with no one to catch it yet.
Hmm, what you can do here is to set the initial ViewController of your application when it receives the launchOptions you specified.
You can set it using this:
self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"YourStoryboard" bundle:nil];
UIViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"YourStoryboardId"];
self.window.rootViewController = viewController;
[self.window makeKeyAndVisible];
Be careful though in designing your navigations. Especially if you want your initial ViewController to be a page with back button that access the navigation stack.
Since you will make it your initial view controller, if it tries to popViewController, it will pop to nothing.
EDIT:
If you want it to be opened from the MainVC with a delay, you can put tnis in your MainVC
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(openAnotherVC:)
name:#"YourPostNotificationName" object:nil];
Then navigate to your desired VC in openAnotherVC: method

set badge on tabbar item when notification received

I tried to set badgeValue for UITabBarItem when push notification is received.I am using this code. Here the UITabBarController is not a rootViewController. I tried the same thing in resign active method but there also its not working.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
UITabBarController *tabBarController = (UITabBarController *)[[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"tabBarController"] ;
[[tabBarController.tabBar.items objectAtIndex:2] setBadgeValue:#"1"];
}
I think that you can use NSNotificationCenter to post notification when you received a remoteNotification
In your UITabBarController initialize method
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(myNotificationReceived:) name:#"pushNotification" object:nil];
And
In myNotificationReceived:
[[self.tabBar.items objectAtIndex:2] setBadgeValue:#"1"];
When you receive a remote notification
[[NSNotificationCenter defaultCenter] postNotificationName:#"pushNotification" object:nil userInfo:userInfo];
In this way, you can get whole RemoteNotification information

Switching the view after applicationDidBecomeActive

I am currently developing an app that will need to return to another view after running in the background for more than five minutes. In order to do this, I will have to have a timer running in the background after the the Home button has been pressed or in case of an interruptions such as an SMS or a telephone call, then, after five minutes the app will need to go to another view. I know that the applicationDidBecomeActive method will have to be used, but how? I also know that a view can be refreshed in applicationDidBecomeActive but how is that done? ( I am not using storyboards.)
Actually, you should do this with the applicationDidEnterBackground applicationWillEnterForeground delegate methods of UIAppDelegate or by registering to the appropriate system notifications (didBecomeActive is called on other occasions too, such as when a UIAlertView is dismissed from screen).
This should be something in the lines of (may include syntax problems, I'm textbox-coding here):
In the viewDidLoad method of your view controller, register to the notifications:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
Implement the willEnterForeground: and didEnterBackground: methods. In willEnterForeground: sample the current time using CACurrentMediaTime() or [NSDate date]. In didEnterBackground: sample the time again and calculate the time difference. Since this method is implemented inside the view controller, you can manipulate the subviews of your self.view as you wish.
Do not forget to remove the observers on your dealloc method (viewDidUnload is deprecated since iOS 6.0, so beware):
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil]
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil]
Here's how you can do it. I just made a test app and I confirm that it works beautifully. Code:
#import "AppDelegate.h"
#import "ViewController.h"
#import "theView.h"
NSTimer *theTimer;
UIViewController *theViewController;
BOOL theTimerFired = NO;
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
// Set a 5 minute timer (300 seconds)
theTimer = [NSTimer scheduledTimerWithTimeInterval:300.0 target:self selector:#selector(presentVC) userInfo:nil repeats:NO];
}
- (void)presentVC
{
// Set a boolean to indicate the timer did fire
theTimerFired = YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Check to see if the timer did fire using the previous boolean we created
if (theTimerFired == YES)
{
theViewController = [[UIViewController alloc]initWithNibName:#"theView" bundle:nil];
[self.viewController presentViewController:theViewController animated:YES completion:NULL];
[theTimer invalidate];
}
}
#end

Post NSNotification to new ViewController

I have Tabbed Application with 5 tabs.
App starts on tab with index 0
When my app recive push notification, i want to push new view controller in tab with index 1.
My code:
AppDelegate
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)pushData {
UITabBarController *tabb = (UITabBarController *)self.window.rootViewController;
tabb.selectedIndex = 1;
[[NSNotificationCenter defaultCenter] postNotificationName:#"pushImage" object:#"this is my item id from pushData"];
}
ProfileViewController (tab index 1)
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(pushImage:) name:#"pushImage" object:nil];
}
-(void) pushImage: (NSNotification*) notification {
NSString* text = notification.object;
NSLog(#"My id from pushData: %#", text);
}
My problem is that the ProfileViewController can not response to the notification, because the initialisation not already done, when the AppDelegate fire the notification.
If a manually open the tab 1 and switch back to tab 0 again, an then post the notification, it perfectly respons to it. So i need to post notification after the tab 1 is loaded, how can i hand this?
My solution of pushing new VC from AppDelegate in TabBarApplication
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)pushData {
// ......
if([[pushData objectForKey:#"type"] integerValue] == 0){
// ....
}
else if([[pushData objectForKey:#"type"] integerValue] == 2){
[self handleLikePush:pushData applicationState:application.applicationState];
}
}
-(void)handleLikePush:(NSDictionary *)pushData applicationState:(UIApplicationState) applicationState{
//..
DetailImageViewController *detailImage = [[DetailImageViewController alloc] initWithImageId:imageId];
[self pushViewControllerToCurrentTab:detailImage];
}
}
- (void)pushViewControllerToCurrentTab:(UIViewController *)vc{
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
UINavigationController *selectedTabNC = (UINavigationController *)tabBarController.selectedViewController;
if (selectedTabNC != nil) {
[selectedTabNC pushViewController:vc animated:YES];
}
else{
NSLog(#"NavigationController not found");
}
}
You can use
addObserver:instanceOfOtherClass
instead of addObserver:self
In appDelegate add these lines :
ProfileViewController *pvController=[ProfileViewController new];
[[NSNotificationCenter defaultCenter] addObserver:pvController selector:#selector(pushImage:) name:#"pushImage" object:nil];
to this method
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)pushData {
UITabBarController *tabb = (UITabBarController *)self.window.rootViewController;
tabb.selectedIndex = 1;
[[NSNotificationCenter defaultCenter] postNotificationName:#"pushImage" object:#"this is my item id from pushData"];
//**** add here
ProfileViewController *pvController=[ProfileViewController new];
//[[NSNotificationCenter defaultCenter] addObserver:pvController selector:#selector(pushImage:) name:#"pushImage" object:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"pushIamge" object:pvController];// userInfo:[NSDictionary dictionaryWithObject:#"1,2,3,4,5" forKey:#"categories_ids"]];
}
Have you tried adding the addObserver: method to your view controller's init method?

Resources