NSNotification delay from class A to class B - ios

My NSNotification is delayed.
In myVC, I have two buttons, buttonA and buttonB. Each one is linked to their respective pdfs, pdfA and pdfB. There is a button Push, which is pressed after A or B is pressed. Push brings the user to the RVC where there is a UIWebView.
I want it so that by pushing either A or B, the UIWebView will display the respective pdf file. To debug this, I set it so that instead of changing the pdf, it will display text using NSLog. However, it doesn't work until after go back to myVC from the RVC by pressing a different button.
in myVC.m file:
- (IBAction)open_pictures_A:(id)sender
{
//do some button alert popup action/whatever button does
RootViewController *dataObject = [RootViewController new];
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:dataObject
forKey:#"buttonA"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"MyNotification"
object:nil
userInfo:userInfo];
}
And there is one like that for buttonB, but forKey would be "buttonB"
in the viewDidLoad for myVC.m I have
[[NSNotificationCenter defaultCenter] postNotificationName:#"MyNotification"
object:nil];
in my RVC,
RVC.h I have
#property (nonatomic, strong) NSString *property1;
And in the RVC.m I have
- (void)viewDidLoad
{
// other stuff
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(eventListenerDidReceiveNotification:)
name:#"MyNotification"
object:nil];
}
- (void)eventListenerDidReceiveNotification:(NSNotification *)notif
{
if ([[notif name] isEqualToString:#"MyNotification"])
{
NSLog(#"Successfully received the notification from buttonB!");
NSDictionary *userInfo = notif.userInfo;
RootViewController *dataObject = [userInfo objectForKey:#"buttonB"];
// Your response to the notification should be placed here
NSLog(#"dataObject.property1 -> %#", dataObject.property1);
}
}
However, the log entry only shows up when I press a button to exit out of the RVC back to myVC
Here is the code I use to go from mVC to RVC
-(IBAction)goToRVC{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
RootViewController *RVC = [storyboard instantiateViewControllerWithIdentifier:#"Root"];
[UIApplication sharedApplication].delegate.window.RootViewController = RVC;
}
Then from RVC to myVC
-(IBAction)backtomyVC{
[[[UIApplication sharedApplication] delegate] performSelector:#selector(myVC)];
[self disconnect];
}
Why is my notification action being delayed?

It's not because of delay.
When you press your button A or B you create new RVC object and you post the notification however you don't present/push RVC view controller (you just initialise it) so the RVC hasn't fired viewDidLoad method and it hasn't register itself as an observer.
After that you call goToRVC method where you create RVC object and you add it to the view hierarchy so the viewDidLoad method is call and the object register itself as an observer.
When you go back to mVC the RVC is not deallocated yet so it receive the notification and you can see the log.
Hope it's clear.

Related

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

How to dismiss middle viewControllers and come back to rootViewController

I have three view controllers, say root, middle, last. I want to go back from lastViewController to rootViewController.
I tried custom delegate and NSNotificationCenter. But while going back the middleViewController flashes for a second (it's viewWillDisappear gets called).
I'm using presentViewController/dismissViewController and not navigation. I don't want that middleViewController getting flashed.
I have below code in rootViewCotroller:
-(void)viewDidLoad {
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(actionBackToRoot) name:#"BackToRoot" object:nil];
}
-(void)actionBackToRoot : (NSNotification *) notification{
NSDictionary *userInfo = notification.userInfo;
UIViewController *lastViewController = [userInfo objectForKey:#"viewController"];
[middleViewController dismissViewControllerAnimated:NO completion:nil];
[lastViewController dismissViewControllerAnimated:NO completion:nil];
}
On click of close button of lastViewController, code as below -
[dict setValue:self forKey:#"viewController"];
[[NSNotificationCenter defaultCenter]postNotificationName:#"BackToRoot" object:nil userInfo:dict];
Does anyone have solution for this?
Thanks in advance.
Did you tried lastViewController.navigationController.popToRootViewController(animated: true )

Open app through notification banner when app is not running state iOS

I have tabbar controller having three tab, attached three navigation controller I want to go to second controller of navigation controller just like whatsapp. I handled successfully for background state, but for not running state. Below is my code in didfinishlaunch delegate method of uiapplication.
if (launchOptions != nil) {
// Launched from push notification
NSDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
[self performSelector:#selector(notificationObserverAfterDelay:) withObject:notification afterDelay:2.0];
}
-(void)notificationObserverAfterDelay:(NSDictionary *)userInfo
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"RefreshUIChatNotRunningState" object:self userInfo:userInfo];
}
Nazish Ali,
Sending out notification after delay of 2 seconds is really not a good idea.
Declare a method which accepts your userInfo dictionary as argument in your viewController having tab bar.
lets call it as,
YourViewController.h
-(void)appStartedFromPushNotification : (NSDictionary *)userInfo;
YourViewController.m
-(void)appStartedFromPushNotification : (NSDictionary *)userInfo {
//now the control has reached yourViewController with tab bar
//change the tab bar selection and perform segue and load the next viewController and use prepareforSgue to pass the data
}
Now why YourViewController only why not the actualViewController which anyway needs to be loaded ??? Reason you dont want mess with navigation stack manually. Because YourViewController is the child of UINavigationController and UINavigationController is your rootView controller YourViewController will be loaded automatically. So just access it and ask it to perform the tab change and load viewControllers so your navigation stack will continue to be stable :)
finally in Appdelegate,
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSDictionary *userInfo = [launchOptions valueForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (launchOptions) {
[self handleReceiveRemoteNotification: userInfo];
}
return YES;
}
Declare a method in AppDelegate.m and access the YourViewController instance and handover data to it thats all :)
- (void) handleReceiveRemoteNotification:(NSDictionary *)userInfo{
UINavigationController *rootViewController = (UINavigationController *)self.window.rootViewController;
for(UIViewController *viewController in rootViewController.viewControllers){
if([viewController isKindOfClass:[YourViewController class]]) {
[viewController appStartedFromPushNotification:userInfo];
}
}
That should do the job :) Happy coding :)

How do I access a view controller from AppDelegate.m during a background refresh

I am trying to implement background refresh in my app. I have a method refreshMessages that is inside one of my UIViewControllers (I am using a UINavigationController). I need to access this controller from AppDelegate.m.
Usually I would do something like this:
UINavigationController *navigationController = [self.window.rootViewController navigationController];
to get the navigation controller and I can then access them from there. But surely this won't work now. The app is in the BACKGROUND and no window is showing. Any thoughts on how to get around this? Thanks!
Here is my background refresh:
-(void)application:(UIApplication *)application
performFetchWithCompletionHandler:
(void (^)(UIBackgroundFetchResult))completionHandler {
NSLog(#"Doing the background refresh");
UINavigationController *navigationController = [self.window.rootViewController navigationController];
NSLog(#"Number of controllers is %d", [[navigationController viewControllers] count]);
//There are zero controllers?!!! Must be because no window is showing
completionHandler(UIBackgroundFetchResultNewData);
}
Yes, you are right. The window will be nil since it will no longer be in the view hierarchy.
A cheeky solution would be to hold the reference of the view controller in the AppDelegate by creating a property in it. You can assign this when the application goes to the background by subscribing to the relevant notification in the view controller.
Edit: Code added
In your AppDelegate.h
#property (nonatomic, weak) YourViewController *yourViewController;
In YourViewController.m, somewhere in viewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
appWillResignActive's implementation
-(void)appWillResignActive:(id)sender {
YourAppDelegate *delegate = [UIApplication sharedApplication].delegate;
delegate.yourViewController = self;
}
In your completion handler
[self.yourViewController refreshMessages]
Also, make sure you remove the observer once the view controller is deallocated.

open a viewcontroller from storyboard when receive apple pushnotification

i am developing a app that has a storyboard with 3 view controllers and app in push notification enabled. and when i receive a push notification and when i tap on notification alert it should open a second view controller from my storyboard let me show my code.
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"pushNotification" object:nil userInfo:userInfo];
}
and then storyboard loads which is actually my first view controller which also have a button in it to second view controller and that is the controller i want to load. and here is the code in my first view controller.
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(pushNotificationReceived) name:#"pushNotification" object:nil];
}
-(void)pushNotificationReceived{
NSString * storyboardName = #"DealerMainStoryboard";
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle: nil];
UIViewController * vc = [storyboard instantiateViewControllerWithIdentifier:#"DealerBuyRequests"];
[self presentViewController:vc animated:YES completion:nil];
}
so when i receive notification with this code app crashes when i tap on notification.
You need to get some error log but check this.
UIViewController * vc =
[storyboard instantiateViewControllerWithIdentifier:#"DealerBuyRequests"];
I don't think you want to create a new UIViewController , unless you really named your controller "UIViewController".
So check again the class name of the View you want to present modally
DealerBuyRequestsViewController * vc =
[storyboard instantiateViewControllerWithIdentifier:#"DealerBuyRequests"];
Make sure the StoryBoard Id of this View controller matches DealerBuyRequests or you will get errors.

Resources