How to hide a Storyboard/ViewControllers forever? - ios

I am working on an app that has two Storyboards: One for my app's onboarding controllers and the other for my main app. My onboarding view controller has a skip button that when pressed directs the user to the main storyboard. I only want to show the onboarding view as long as the user hasn't hit the skip button. Once the skip button is hit, the corresponding storyboard should disappear forever.
I thought I could fix this problem by only making showing onboarding storyboard when the app is first opened, and I found some code online that seemed helpful, however it doesn't work...here is the code that is in my app's AppDelegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
FIRApp.configure()
Fabric.with([Crashlytics.self])
let defaults = NSUserDefaults.standardUserDefaults()
if (defaults.objectForKey("userAsLoggedInBefore") != nil) {
print("Functions")
//here you insert the code to go to the main screen
var mainView: UIStoryboard!
mainView = UIStoryboard(name: "Functions", bundle: nil)
let viewcontroller : UIViewController = mainView.instantiateViewControllerWithIdentifier("functions") as UIViewController
self.window!.rootViewController = viewcontroller
} else {
//this means the user hasn't logged in before and you can redirect him to the onboarding page
print("Onboarding")
var mainView: UIStoryboard!
mainView = UIStoryboard(name: "Main", bundle: nil)
let viewcontroller : UIViewController = mainView.instantiateViewControllerWithIdentifier("onboarding") as UIViewController
self.window!.rootViewController = viewcontroller
}
}
Note in my code "Main" is the onboarding storyboard, while "Functions" is my main app code.
However this code is not working as intended as my same problem still arises. If anybody could take a look at my code and see if I am doing anything wrong, that would be greatly appreciated.
Thanks in advance!

You are never writing to NSUser defaults. So it will stay nil..
Your missing some line like this:
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setValue("Did watch", forKey: "userAsLoggedInBefore")
Put it at the end of if - else statement

Related

How to handle showing different view controllers when the app starts based on whether the user has logged in or not?

In an app that I am developing, I need to show different view controllers based on whether the user has logged in or not when the app starts
If the user has logged in, I need to show HomeViewController
If the user has not logged in, I need to show LoginViewController
Presently, I have coded in the following way-
the rootViewController of window of AppDelegate is always LoginViewController embedded inside a UINavigationController.
In the viewDidAppear method of LoginViewController, I check if the user has logged in. If yes, then I push HomeViewController.
In the HomeViewController, when the user logs out, I pop the HomeViewController to show LoginViewController
Is there a better way to do this?
Which is the better way is totally depends on your requirement and self-satisfaction.
Now think about your approach:
If you're going with this approach then login screen will display for fraction of time. If it is okay for you then you can for
it.
Now think about my approach:
I think the better way is to check the same condition in didFinishLaunchingWithOptions instead of loginVC
If you are going this approach then your HomeVC is directly displayed after your splash screen. It is the main advantage of this
approach
Code:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let isLoggedIn = true
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let navigationController = storyboard.instantiateViewController(withIdentifier: "NavigationController") as! UINavigationController
var viewController:UIViewController
if !isLoggedIn{
viewController = storyboard.instantiateViewController(withIdentifier: "LoginVC")
}else{
viewController = storyboard.instantiateViewController(withIdentifier: "HomeVC")
}
navigationController.viewControllers = [viewController]
window?.rootViewController = navigationController
return true
}
Download sample code
Update for Anbu.Karthik:
Just define the method in AppDelegate.swift the file then calls it when required. Also, the same method is implemented in the sample code too.
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.logoutFromApp()
Code to change the root view controller:
func logoutFromApp(){
guard let rootNavigationController = window?.rootViewController as? UINavigationController else {
return
}
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "LoginVC")
rootNavigationController.viewControllers = [viewController]
}
Sometimes I use a custom root view controller, that can change a child view controller based on business logic (the root controller is a subclass of UIViewController not of UINavigationControler or UITabBarController). For example, the root controller can subscribe to notification about login/logout of user and show required view controller as a child view controller.
To support this, the root controller should be a custom container view controller (part with title Implementing a Container View Controller).
This solution allows to divide the interface to different story boards and save memory because you can destruct invisible view controllers.
I think the better and easier approach will be to check is user signed in in AppDelegate (the best approach to create separate class for this logic).
You need to store user token in keychain and in AppDelegate check is user signed in. If it's require API request, while request is executing, replace Splash screen with spinner and depends from the API response, display necessary controller

Segue from LaunchScreen to a viewController in Main.Storyboard?

I got some logic I want to do in my LaunchScreen, and if the check is alright, I want to segue to a viewController and if not I want to segue to another, is that possible?
I got some logic I want to do in my LaunchScreen
Now that I understand the question, I can answer it: don't. Do your time-consuming logic later. Your job is to launch fast. You need to get out of applicationDidFinishLaunchingWithOptions, get out of viewDidLoad, and launch.
What you show at that point is up to you; if you have time-consuming stuff (in your case, it sounds like you're networking or doing something else that takes time while you load up the data source for a table) and you want to show a special view controller that covers the time with a spinning activity view or something, fine. But during actual launch is not the time to do that.
No you cant code for launchScreen.Storyboard, The reason why :- when your launchScreen.storyboard shows the app is still loading.
Simply put: You cant access your app when it is displaying launchScreen.storyboard, all you can do is make a UI/UX for that not execute any code for it.
Alternative:- Make a viewController that appears as a first viewController check your logic there and do things from there accordingly!
Reference : - https://stackoverflow.com/a/27642160/6297658
Your LaunchScreen is shown while your app is loading. Go to your AppDelgate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
window.rootViewController = //your root view controller that you have figured out with logic
return true
}
Run your check in didFinishLaunchingWithOptions() and use that to "jump" directly to a specific vc. Here's an example using userDefaults, but of course you can replace that with whatever check you're running.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Do some logic
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let welcomeVC = storyboard.instantiateViewControllerWithIdentifier("WelcomeNavController")
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = welcomeVC
self.window?.makeKeyAndVisible()
}
}
Add this function into the AppDelegate:
func initialVC(storyboardID: String) {
let mainStoryboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController : UIViewController = mainStoryboard.instantiateViewControllerWithIdentifier("\(storyboardID)") as UIViewController
self.window?.makeKeyAndVisible()
if storyboardID == "tabBarVC" {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = initialViewController
} else {
let navController = UINavigationController(rootViewController: initialViewController)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = navController
}
}
In the didFinishLaunchingWithOptions method inside of the AppDelegate, you can add this:
if currentUser != nil {
initialVC("tabBarVC")
} else {
initialVC("loginVC")
}
You can see in my example, I am either loading the main app VC or the Login VC depending on if the user is logged in. In your case, You can use an if - else statement and do the logic within the initialVC function.
Note: When I call for the loginVC to be loaded, I have to load the navigationController because the loginVC is embedded in a navigationController. For the tabBarVC, I don't embed the navController because it isn't needed.

Prevent Login Screen from showing when auto login

I have implemented the auto login within my app into the viewcontroller linked to the Login View Controller. However, given below, the storyboard navigates to the Login screen first after the launcher screen. When there are credentials found in the keystore, the app will auto login, but the Login screen will still flash before then moving on.
How can I ensure the login screen will not appear if the user credentials are already in the keystore and navigate directly to the Reveal View Controller?
Complement to the #Kerby Jean answer. First remove the entry point in the storyBoard, assign the point by code in your app delegate at didFinishLaunchingWithOptions method, something like this:
UIWindow * window
var storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
//Slect initial viewController
self.window = UIWindow(frame: UIScreen.mainScreen.bounds)
UIViewController * viewController
var preferences: NSUserDefaults = NSUserDefaults.standardUserDefaults()
var userId: String = preferences["userId"]
if userId != nil {
viewController = storyboard.instantiateViewControllerWithIdentifier("NavigationController")
}
else {
viewController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController")
}
self.window.rootViewController = viewController
self.window.makeKeyAndVisible()
Store the user details in your loginScreen
NSUserDefaults.standardUserDefaults().setObject("\(userNameTxtField.text!)", forKey: "userName")
NSUserDefaults.standardUserDefaults().synchronize()
Put this function in your appDelegate
func userIsLoggedIn() {
let userName = NSUserDefaults.standardUserDefaults().stringForKey("userName")
if userName != nil {
//Navigate to Reveal View Controller
}
}
Then call the function in ViewDidFinishLauchingWithOption(In AppDelegate)
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
userIsLoggedIn()
return true
}

Logout from TabBar

What is the best way to log out from a TabBar. I have an app which in one of its tabs has a log out option in a row. When I tab this row I want to go to my log in screen. The app launch the log in screen if you are not log in or the main screen (is a tab bar controller) if you are already log gin. I manage this in the appDelegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let defaults = NSUserDefaults.standardUserDefaults()
let notFisrtRun = defaults.boolForKey("notFirstRun")
// Chooses between login view and my list view if the user is already authenticated when the app is launched
if AuthToken.sharedInstance.isAuthenticated() && notFisrtRun {
let tabBarViewController = mainStoryboard.instantiateViewControllerWithIdentifier("TabBarViewController") as? UITabBarController
self.window?.rootViewController = tabBarViewController
} else {
let loginViewController = mainStoryboard.instantiateViewControllerWithIdentifier("LoginViewController") as? LoginViewController
self.window?.rootViewController = loginViewController
}
self.window?.makeKeyAndVisible()
return true
}
If I try a show segue to my log in screen, it appear with the navigation bar and tab bar.
If I try an unwind segue, it work in the case you are not already log in and the app start with log in screen.But in the cases the app start with the tab bar controller because you are already log in, it doesn't work.
It is pretty normal for apps to show login screens modally. Upon logout, Present loginViewController modally. If the user logs back in, dismiss the loginViewController.
You could also create a custom segue that replaces the rootViewController, This question can get you started on that.

Check for user in session in AppDelegate and trigger UIViewController if not

I have a Swift application, and what I'd like to do is that every time the app becomes active, I'd like to check for a user in session. If not found, I'd like to show a login view controller that I designed in my Storyboard. If found, I need everything to just resume as usual.
Where is the best way to trigger this check? Is AppDelegate's applicationDidBecomeActive the right place for this?
If yes, how do I actually instantiate the login view controller and show it? I have another home view controller, which is set to be my initial view controller. How do I manage this controller if and when I do end up successfully pushing the login view controller from the app delegate in case there is no user found in session? I don't want the home view controller to show up if a user is not found.
Any advise is greatly appreciated!
Hope this answer can help!
Put this in your AppDelegate.swift file. With the if you can check the value saved in local memory.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
var userLoggedIn: Bool = NSUserDefaults.standardUserDefaults().boolForKey("isUserLoggedIn")
if (userLoggedIn){
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var homeViewController = mainStoryboard.instantiateViewControllerWithIdentifier("HomeViewController") as!
HomeViewController
window!.rootViewController = homeViewController
}
return true
}
If let's say you store a token in your usersession, we go look if there is a token set or not. If it's already set (so not null) then we go to your other viewcontroller
let prefs = NSUserDefaults.standardUserDefaults()
let checkfortoken = prefs.stringForKey("token")
if(checkfortoken != ""){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("identifierofyourview") as UIViewController
self.presentViewController(vc, animated: true, completion: nil)
}
Now you want to check this when you start your app so we gonna put the code into appdelegate in the first function (didFinishLaunchingWithOptions):
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
UIApplication.sharedApplication().setStatusBarStyle(UIStatusBarStyle.LightContent, animated: true)
//so here ...
return true
}

Resources