Best way to get notification to rootViewController - ios

I am looking for some advice as to the best way to handle this situation:
In my appDelegate I call registerForRemoteNotificationTypes in didFinishLaunchingWithOptions as follows:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Let the device know we want to receive push notifications
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
[[self window] setBackgroundColor:[UIColor blackColor]];
[self.navigationController setNavigationBarHidden:YES];
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
return YES;
}
However, I need to wait for a device token to be returned before my app can continue, so I was wondering what the best place to wait for this before continuing? Currently my viewDidLoad gets called, but I sometimes have failures because the device token did not come back yet.
I was thinking that I could move my code from viewDidLoad to myViewDidLoad and was hoping I could wait for a notification that would happen once I receive the device token and then call myViewDidLoad.
But, I am not sure where to set the notification. I know this is a basic question, but I am still learning iOS.
Any suggestions would be most helpful.

You don't wait. Never, never wait.
When dealing with asynchronous stuff like networking (as here), you must write your app in such a way that it can proceed in some way even if the networking hasn't happened yet. It's that simple.
Your second impulse is much more correct. When you get the remote notification in the app delegate (I'm assuming), the app delegate can talk to the view controller in question and give it the new info and tell it to update itself (and the view controller can then update the view to display whatever needs displaying at that point).
Since you know you're going to need to do this, you need to set up "lines of sight" communication in advance. You have all the pieces of the puzzle in your hands in the app delegate, so you can arrange to hang on to a reference to that view controller, for when you'll need it later.

I posted a comment to my question with my solution. Not ideal, but it works for now.

Related

"Application windows are expected to have a root view controller" message when adding view immediately after launch, iOS 9 only

My app sends a request at startup and displays a brief message to users when it succeeds, by means of MTStatusBarOverlay. Unfortunately, my current implementation seems to run afoul of iOS 9's view life cycle paradigms. I get the message
Application windows are expected to have a root view controller at the end of application launch
and the app crashes. The app works fine on iOS 7 & 8.
From searching around online, it seems like this might occur when trying to add the message view to the view hierarchy before the root view controller is established for the app's UIWindow, but that doesn't seem to be the case here, see below.
Here is an excerpt of the UIApplicationDelegate implementation:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[...]
self.window.rootViewController = [[MyViewController alloc] init];
[...]
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[MyDataManager sendRequestWithCompletion:^{
// Displays a message with MTStatusBarOverlay
[self showSuccessOverlay];
}];
}
application:didFinishLaunchingWithOptions: is called before applicationDidBecomeActive: so it seems like there should never be an issue with rootViewController being established.
Why is this happening? What's different about iOS 9 that's causing the app to break?
MTStatusBarOverlay is a subclass of UIWindow, so instantiating one during app launch adds that UIWindow to the list that iOS checks for a populated rootViewController when launch completes.
I was able to work around the issue by instantiating and assigning a dummy controller before using the overlay, like so:
[MTStatusBarOverlay sharedInstance].rootViewController = [UIViewController new];
[[MTStatusBarOverlay sharedInstance] postMessage:#"Message"];

Determine if Push Notifications alert view is displayed

The first time that a user accesses my app, they are asked whether they want to allow push notifications. At the same time, I have some animations going on. The push notification question is obscuring these animations.
I'm wondering if anyone knows how to determine whether this alert view is being displayed. If I can determine when it gets dismissed (regardless of what the user chooses), I could delay the start of the animations until this event occurs. Despite much research, I am at a loss as to whether this can be done or not.
Thanks for any advice.
EDIT
Thanks for the suggestions. One thing that I didn't explain is that the animations initiate from the initial view controller (SplashScreenViewController) while the push notifications happen in the AppDelegate. I need a good way to get these two classes to communicate without creating a race condition.
I think you're focused on the specific problem, which is that the alert is obscuring your animations. However, the problem you should be trying to solve is: Am I asking for remote notification registration, and if so, has it been given or not?
You should be able to do this with a combination of the methods available on UIApplication and UIApplicationDelegate. Perhaps something like this:
#implementation AppDelegate
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
if([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]) {
// Show animation
}
else {
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:<#notification type#>];
}
return YES;
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// Show animation
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
// Show animation
}
#end

iOS equivalent to Android registerActivityLifecycleCallbacks

I developed an Android app where I used the application.registerActivityLifecycleCallbacks (http://developer.android.com/reference/android/app/Application.html) to know when each Activity is started and stopped and record it for analytics purposes. I am now developing the iOS version and I cannot find an equivalent to this method to monitor the UIViewControllers or UIView displayed by the app.
Anybody has an idea ? I am a beginner on iOS so I may not be taking the right approach, feel free to suggest other ideas.
Edit
After the first answer I felt I should be more precise.
I am actually developing a SDK for other developers to include in their apps so I want the impact of the SDK on their code to be as small as possible.
I first thought about doing a BaseActivity/BaseUIViewController that developers would have to extend in all the Activity/UIViewController but it felt heavy and since both language don't allow multiple inheritance this would greatly impact their code. This is why the registerActivityLifecycleCallbacks method is great in Android because they only have to give me an Application or Activity object.
Is there a solution for iOS or I will have to create a BaseController ?
Thank you in advance.
I've not run into anything that specific as the application.registerActivityLifecycleCallbacks in iOS, but you could always implement your own using the methods that exist within the AppDelegate and each class.
From the AppDelegate, you are provided the methods to determine the state of the overall application such as determining when the application finished loading, when it enters the background, and so forth. Details on these states can be found in the UIApplicationDelegate Protocol Reference page.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NSLog("Application did finish launching with options: %#", launchOptions);
return YES;
}
For each view controller, you can add your implementation to the individual view controller lifecycle methods within each file. Methods such as viewDidLoad, viewWillAppear, viewDidAppear, and viewDidLayoutSubviews are available.
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"View %# did load", self);
}

How to call a view controller from AppDelegate in iOS

I am creating an iOS app in which I have the following requeriment: the app should show the login screen when it starts the first time and also that screen must also be shown when the app comes from the background to the foreground, in case it has been sent to background during run time.
I have handled to show the screen in both cases. However, when I do it and the app comes from the background, and I click the texfield to type my password, the app gets frozen, and it fails in a thread that I don't know what it means.
I call the screen to be shown when the app comes from background like this, in the applicationWillEnterForeground, in AppDelegate:
self.window=[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
RoomRootViewController* room = [[RoomRootViewController alloc] init];
[[self window] setRootViewController:room];
[self.window makeKeyAndVisible];
Is this the correct way to do so?
Thanks a lot in advance ! I am completely lost with this as I am very new in iOS, so any help will be very appreciated.
Attached in an image you can see where the app fails.
The code you are currently using is completely deleting the root view controller of your app window, in other words, you are deleting all the views and view controllers in your app. If you are not using ARC, this is just one huge memory leak. If you are, it's still not a very good idea.
In your applicationWillEnterForeground: method, try using this code instead:
RoomRootViewController* room = [[RoomRootViewController alloc] init];
[self.window.rootViewController presentViewController:room
animated:NO
completion:nil];
This will display the RoomRootViewController over the top of all your app's current views, instead of deleting them. You can then dismiss it like this:
[self.window.rootViewController dismissViewControllerAnimated:YES
completion:nil];
and easily return to the rest of your app.
It will be very messy if you are using this line of code
self.window=[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
It means every time you are creating new instance of UIWindow which will contain UIViewController and all other component each time. Which means when you go to background and comes to foreground the old window instance has been flushed off and it will contain new components that's why when you click on the UITextField it has been deallocated, The reason you are getting error. Don't create new instance of window and use the code as #PartiallyFinite does.
Hope this helps.

go to another page in applicationDidEnterBackground in iOS in device

In my application, When the application goes to background I am calling a Passcode page (Passcode Page which does authentication).
My requirement is when the user launch the app from the foreground he will see the passcode page. If he enters the correct passcode then only he can see the rest pages.
In delegate.m file
- (void)applicationDidEnterBackground:(UIApplication *)application
{
PasscodeViewController *passcodeController = [[PasscodeViewController alloc] initWithNibName:#"PasscodeViewController" bundle:nil];
[navController pushViewController:passcodeController animated:YES];
}
When I am launching the application from the background then It is showing me the previous page( from which page I came to background ) for a fraction of second and after that Passcode page comes.
But I want to hide my confidential information from others (who doesn't know the passcode) that are shown in the previous page.
It is correctly working in Simulator but in Device it is not working properly.
Can you please guide me in that ?
OR
Is it the normal behavior of the iOS device ? What ever the page transition it will do, it will perform while the application is running in foreground.
I am not sure about that. Please tell me where I went wrong.
Thank you.
Every app I've used with a similar feature has operated as you describe, with the fractional-second flash before the lock view appears.
I think it's a matter of when UIKit thinks it needs to re-render... We had a similar case with a splash screen, but using applicationDidEnterBackground for adding the splash helped.
My idea is to avoid the animating, using
[navController pushViewController:passcodeController animated:NO];
Whenever your app goes background add a UIView with white background.
Whenever your app comes up push your PasscodeViewController view on top
Please add observers for UIApplicationDidEnterBackgroundNotification and UIApplicationWillEnterForegroundNotification to do the above functionality
Also be sure to remove the Observers when your view disappears
When user enters correct passcode remove the UIView.
Try applicationWillResignActive:
- (void)applicationWillResignActive:(UIApplication *)application
{
PasscodeViewController* passcodeController = [[PasscodeViewController alloc] initWithNibName:#"PasscodeViewController" bundle:nil];
[navController pushViewController:passcodeController animated:YES];
}
When Application goes to background push the passcode viewcontroller to navigationcontroller in the delegate applicationDidEnterBackground because there will be that fractional flash almost all time u can have the passcodecontroller pushed before entering background.

Resources