I am looking for feedback on a best practice approach when you want to present a LoginViewController as a "ModalViewController" in an app with TabBarController.
The case is this:
- Application supports user sessions with login/logout functionality
- When the app is launched it tries to login automaticly and either display TabBarController directly or the LoginViewController (displayed as a ModalViewController to enable a dismiss animation on login)
- The application has a TabBarController with a number of tabs.
- Each time a ViewController is presented the user's state is checked
- If the user isn't logged in a ModalViewController with login functionality should be presented
- If the user manually logout the same ModalViewController should be presented
I have tried to come up with several different designs to handle this and no one feels completely "correct". I have worked on one project where this was handled using a subclassed UIWindow which replaced the apps default UIWindow. The subclass then listened for different Notifications such as "UserLoggedInSuccessfully", "UserLoggedOut" etc, to handle presentation of the ModalViewController.
Are there any other solutions to this? I would appreciate any input and experiences of this kind of solution.
There are different solutions for this problem , you could try these alternatives but first one is the recommended approach I would say,
You can make your HomeViewController which will be first tab of TabBarController as default to open every time, and make the LoginViewController separately, in your AppDelegate you could check the login session in didFinishLaunchingWithOptions and if the session is not valid then show the LoginViewController as ModalViewController otherwise show the TabBarController with all the tabs included.
Alternative could be to have all the Controllers in The Tab but when you present LoginViewController , after user has signed in, add all the controllers in a MutbaleArray and delete the current object which is LoginViewController. Once user is signed in , save the info in the keychain and this way user will not have to login again and again, this way you can avoid showing the LoginViewController every time, once user tries to log out , then add the LoginViewController in an Array and then add all the TabBarController objects after LoginViewController.
You may to do the following ,
when user open the app you have to show hime the loginviewcontroller as the rootviewcontroller then when login successfully you have to make the tabbarviewcontroller as the rootViewcontroller .
then each time when the user run the app you have to check if he has a valid session you will make a tabar as a root
if the the session expire when you on the tabbar , you have to change the root view controller from your app delegate to sign in
Related
I have a little app that I'm building and wanted some opinions on the way I'm implementing login. Here's my plan:
1) Open app
2) Load ContainerViewController (The container view controller will play a little animation that shows the app is doing some loading)
3) The ContainerViewController checks if a token exists in the keychain. If a token does exist, then check if its valid or not.
3a) If the token is valid, add HomeViewController (a view controller that is the root of a navigation controller that is basically the main page of the app) as a child view controller.
3b) If the token is invalid, add LoginViewController (a view controller that basically handles the signing in process) as a child view controller.
If you sign in successfully at the login page, loginviewcontroller will be removed as a child and homeviewcontroller will be added as a child.
Is using container and child view controllers the right way to go about handling the opening of the app? By the way, I'm building it completely programmatically, which means I'm not using storyboards so segues can't be used (unless I'm totally wrong there). Thanks guys
This is the right approach to implementing the login process. In short Up on login, you check for the token, if it is there and valid you present the MainViewController. Else you present the LoginViewController.
PS(Thumbs up for developing it programatically! I like that way as well)
What is the best approach to create an iOS app login screen that slides in (by presenting it modally) whenever the user isn't logged in?
A user will be "logged out" in the following circumstances:
he has never logged into the app (i.e. first use)
he has explicitly logged out of the app
he is logged out on the server (e.g. his security token has expired
on the server, and that is communicated to the app)
My app consists of a UINavigationController that is set as the app's RootViewController, and each screen (other than the Login screen), is pushed onto the NavigationController as the user navigates the app. One of the screens a user can navigate to (i.e. that is pushed onto the stack) is the Logout screen (where the user can log out of the app). The Login screen should appear modally when needed, and the logic and presentation should happen from one centralized place. I am using Storyboards.
What I have tried is to subclass UINavigationController (for the RootViewController), and in its viewDidAppear method I check whether the user is logged in (I store a flag in NSUserDefaults). If he is logged in, the app's first screen is (programmatically) pushed onto the stack; if he isn't logged in, the Login screen is (programmatically) presented modally.
This approach has the following two issues:
you cannot set a background image for the subclassed
UINavigationController, so a blank screen appears for a short while
the subclassed UINavigationController's viewDidAppear is not
called when popping to its RootViewController (specifically when
popped from the Logout screen)
Ideally I want one central place to check whether a user is logged in (I was hoping the subclassed UINavigationController's viewDidAppear method would be this spot) to check the user's logged-in state, and present the modal Login screen if needed.
I have looked at Login Screen using Storyboard and Example for login screen modally based on storyboard (and others referenced in these) but none of them address the issue of presenting the modal Login screen from a central point.
First you should note, that as per Apples developer notes, you are not supposed to subclass UINavigationController.
Second, this is fairly opinion based, however I suggest you should use your application delegate class as the pillar point for checking login status, it's a singleton as UINavigationController, effectively is.
I would suggest posting an NSNotification that your AppDelegate can listen for so that the AppDelegate takes responsibility for presenting your modal login view.
Your communication layer can be responsible for posting the notification whenever the user logs out or the server responds saying that the token has expired.
Try presenting the UINavigationController modally from a "Login ViewController":
On app launch, the LoginVC is shown, requiring the credentials. If the login succeeds, push the UINavigationController.
When the login is invalidated (logout, cookie expires, 401 from server, ...), dismiss the UINavigationController and return to the LoginVC.
Note that on returning to the LoginVC all application state is lost, which may or may not be what you want.
Your AppDelegate should retain a reference to the LoginVC, through which you can call the 'dismiss', e.g.
[((YourAppDelegate*)[[UIApplication sharedApplication] delegate]) fallbackToLoginVC]
OK so here is what I ended up doing:
As pointed out by John Woods, don't subclass UINavigationController. Instead, I created a base UIViewController, which in its viewWillAppear checks whether the user is logged in; if not, modally present the Login screen. All View Controllers that need to check the logged-in state inherit from this base View Controller, so it becomes the "central point" for checking the logged-in state (I don't like using the AppDelegate for this kind of logic).
I like Mike Pollard's suggestion of using notifications to notify when the user's token expires (since this may happen well before viewWillAppear is called). Subscribing to this notification can then also be done in the base View Controller.
Hendrik Demmer's solution is probably the most straightforward, but I don't like having a Login screen "lurking" around at the bottom of the view controller stack - or is this just nitpicking?
I've been reading several posts dealing with switching the rootViewController of the window, and sometimes it is said that keeping a same rootViewController throughout the app's lifecycle is better than switching it, and other posts are related to changing the rootViewController at some point of the app. I'm confused about this, and I couldn't find any recommendation regarding this point in Apple's docs.
What should be the best practice when you are likely to have different content view controllers in an app? For example, let's say you have a tabbed app (UITabBarController) that you want first to show a welcome view for signing in or signing up (UINavigationController):
1) Approach with a fixed rootViewController. I think you should set the UITabBarController as the rootViewController, as the core of the app is tabbed, but if user credentials are needed, then set a UINavigationController within the UITabBarController, hiding the tab bar, to push the welcome view and navigate to Sign In/Sign Up view controllers.
2) Approach switching rootViewController. Firstly, if user credentials needed, set the UINavigationController as the rootViewController to show the welcome view and navigate to Sign In/Sign Up, and, once user credentials successfully provided or account successfully created, switch the rootViewController to the UITabBarController to show the core tabbed app.
In case the best general approach is switching rootViewController as needed, what are the memory management considerations you'd take into account? Should I need to have two strong properties in AppDelegate (UITabBarController and UINavigationController)? And what about the transition between the view controllers when switching the rootViewController: will it be smooth, or should you animate it?
Thanks in advance
The approach of switching rootViewController suits good . Firstly, if user credentials needed, set the UINavigationController as the rootViewController to show the welcome view and navigate to Sign In/Sign Up, and, once user credentials successfully provided or account successfully created, switch the rootViewController to the UITabBarController to show the core tabbed app
If you would like to store User credentials inside tabbarcontroller . Store them in NSUserDefaults or into plist in your main bundle or created in document directory .
I am making a new application there i have 5 tabs in it, its a user based application so i have to get the credentials of users login and passwords .
I am using storyboards with Arc , since its a Tabbed application so my initial view controller is my tab view controller , I wish to add a login screen also (probably as Modal View or an).
I am not able to think the perfect way to add a login screen in the tabbed application .
Should i call it from app delegate or in view will appear or some of these methods . I tried some of the code but ended up with warning like unbalanced call etc.
Need your valuable suggestions :)
Thanks in advance !!!
Just two options (of course there are more):
Use one UIViewController as rootViewController in the beginning of your App. Once the login is successful it will switch the window's rootViewController.
Put the UITabBarController as rootViewController, on viewWillAppear, check if the login has been made, if not, just show the Login's UIViewController
I've been struggling with this a long time now, so I finally gave up on trying to find the answer and decided to ask it right away.
On my app I have a user log in page. The app has a TabBarViewController that has some NavigationControllers in it's items.
My last effort was to put the login screen embedded on a NavigationBarController and make it the Initial View Controller, as in the picture below.
When the app is launched, if the user is logged in the LogInViewController 'segues' to the TabBarController and everything is fine. When the user logs out in the ProfileViewController, there's a segue in this ViewController 'segueing' to the initial view controller.
In the other hand, if no user is logged in, the LogInViewController presents a view so that the user can insert username and password. If credentials are correct the LogInViewController 'segues' to the TabBarController. The problem is that at this point, even if the app is still working good, i get the following warning:
Warning: Attempt to present TabBarViewController: 0xa19a670 on UINavigationController: 0xa526370 while a presentation is in progress!
So I assume this is not the best way to handle all this LogIn/LogOut process.
My question is, where should I put the LogInScreen in the hierarchy?
If by any chance my layout/hierarchy is correct, how to make the warning go away?
You should make your Home screen as your rootViewController and in once your application starts or become active, you can check if user is logged in or not, if not then present the LoginScreen Modally, it will avoid the mess with other NavigationController or TabBarController
Alternate could be to put all the ViewControllers in a MutbaleArray and set the current Index of TabBarController according to the view you want to show? if you don't want to show the LoginScreen after user Logged in, just remove it from your MutableArray, check my answer here, it might help your cause