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?
Related
I have two classes. A and B for example.
I have created an observer in A like this
- (void)viewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(somethingHappens:) name:#"notificationName" object:nil];
}
-(void)somethingHappens:(NSNotification*)notification
{
NavigationController *navigationController = [self.storyboard instantiateViewControllerWithIdentifier:#"contentViewController"];
UIViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"loyaltyVC"];
navigationController.viewControllers = #[vc];
}
I want to call this observe from page B. I'm using like this
[[NSNotificationCenter defaultCenter] postNotificationName:#"notificationName" object:self];
Do you have any suggestion how we can do this ?
To be precise this are not push notifications. The following are just normal in-app-notifications.
Your -(void)viewDidLoad should contain a call to its super method at some point.
//in A
#implementation AThingy {
id<NSObject> observeKeeper;
}
- (void)viewDidLoad {
[super viewDidLoad];
observeKeeper = [[NSNotificationCenter defaultCenter] addObserverForName:#"notificationName" object:nil queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification * _Nonnull note) {
UINavigationController *nav = [self.storyboard instantiateViewControllerWithIdentifier:#"contentViewController"];
UIViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"loyaltyVC"];
nav.viewControllers = #[vc];
}];
}
// not mandatory but to explain it complete
-(void)dealloc {
// could be placed in some other method also.. like -viewDidDisappear:
[[NSNotificationCenter defaultCenter] removeObserver:observeKeeper];
}
#end
//in B
#implementation BThingy
-(void)postNotify {
[[NSNotificationCenter defaultCenter] postNotificationName:#"notificationName" object:nil];
}
#end
But keep in mind that NotificationCenter is working relative to its observing Objects and so it matters if you watch and post in general on (nil) or on some specific object. Why is that? Because you could decide to instantiate two objects from the same class that observe notifications and still specifically observe each notification meant for them or both.
Have you tried to print a log in method somethingHappens:?
Is your method -(void)didLoad ever been called?
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
In my app, UITabBarController is an initial ViewController build in storyBoard. I would like to observe data updating in my whole app. So I add an observer in appDelegate...
AppDelegate.m
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(shipCountBadge:) name:kShipCountBadge object:nil];
- (void)shipCountBadge:(NSNotification *)notification{
Groups *vc = [[Groups alloc] init];
[vc addBadgeCount:notification.object];
}
Group.m
- (void)addBadgeCount:(NSNumber *)count{
NSLog(#"d",[count intValue]);
[[self.tabBarController.tabBar.items objectAtIndex:2] setBadgeValue:[NSString stringWithFormat:#"%d",[count intValue]]];
}
Its doesn't change the UI at all. I would like to know how to update UI in AppDelegate actually? Anyone has an idea :)?
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
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 :)