Select storyboard to launch on launchScreen completion - swift

After completion of launchScreeen(Splash) I want to be able to select which storyboard to launch. For example launch login.storyboard if user is not logged in or launch dashboard.storyboard if user is logged in. Currently Main.storyboard is launched after launchScreen. in appdelegate I have a code for checking the login status as follows:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
var launchDashBoard = false
let isUserLoggedIn = UserDefaults.standard.object(forKey: TAG_IS_USER_LOGGEDIN) as? Bool
if isUserLoggedIn != nil {
launchDashBoard = isUserLoggedIn!
}
if launchDashBoard {
self.loadDashBoard()
}else{
self.loadIntro()
}
return true
}
func loadHome(){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let home = storyboard.instantiateViewController(withIdentifier: "dashboard") as! Dashboard
let navigationController = UINavigationController(rootViewController: home)
self.window?.rootViewController = navigationController
}
func loadLogin(_ viewController: UIViewController?){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let home = storyboard.instantiateViewController(withIdentifier: "signIn") as! SignInVC
home.previousViewController = viewController
let navigationController = UINavigationController(rootViewController: home)
self.window?.rootViewController = navigationController
}
If I run the app with this code it crashes with following log:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Pushing a navigation controller is not supported'
*** First throw call stack:
Is there a way to include a function that determines which storyboard to launch on launchScreen loading is finished or is there something wrong with my code?

You can create a new Navigation.storyboard. That storyboard can have an initial view controller(Call it StartUpNavigationViewController), that will be opened when Launch screen has been shown.
In that StartUpNavigationViewController, check for the logic if the user is logged in or not, based on that you can navigate to login or dashboard storyboard.

Related

AppDelegate problem when i close Onboarding and run application for second time

I make onboarding with UIPageViewController its work but when I run the App for Second time the Xib not working
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//////OnBoarding
let lunchbefor = UserDefaults.standard.bool(forKey: "haslunched")
self.window = UIWindow(frame: UIScreen.main.bounds)
let lunchstoryboard = UIStoryboard(name: "onboarding", bundle: nil)
let mainstoryboard = UIStoryboard(name: "Main", bundle: nil)
var vc: UIViewController
if lunchbefor{
window?.rootViewController = MainVC()
vc = mainstoryboard.instantiateInitialViewController()!
}else{
vc = lunchstoryboard.instantiateViewController(withIdentifier: "start")
}
UserDefaults.standard.set(true, forKey: "haslunched")
if lunchbefor == false{
self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()
}else{
window?.makeKeyAndVisible()
window?.rootViewController = MainVC()
}
return true
}
error:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value"
on this line:
vc = mainstoryboard.instantiateInitialViewController()!
I find the answer
I did this:
var vc: UIViewController
if lunchbefor{
window?.rootViewController = MainVC()
// vc = mainstoryboard.instantiateInitialViewController()!
vc = MainVC()
}else{
vc = lunchstoryboard.instantiateViewController(withIdentifier: "start")
}
If you want to use instantiateInitialViewController you need to select Is Initial View Controller in a storyboard (red arrow on the screen), and you will see an indicator that the view controller is the initial one (green arrow)
Also, it is a good idea not to use force unwrap (!) as it will crash your app when a variable is nil.

show the second ViewController when app launch the second time

so when i launch my app the first time there is a welcome ViewController. How can i set a function, that shows the second ViewController when the user launches the app the second time.
Add a Boolean in the user default in your application to check whether the application is launch first time or not. Based on this Boolean value load another ViewController in your app delegate class.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let isFirst = UserDefaults.standard.bool(forKey: “isLaunched”) // edited this after rmaddy's comment
var viewControllerWithIdentifier = "SecondViewController"
if !isFirst {
UserDefaults.standard.set(true, forKey: “isLaunched”)
viewControllerWithIdentifier = "FirstViewController"
}
let mainStoryboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController : UIViewController = mainStoryboard.instantiateViewControllerWithIdentifier(viewControllerWithIdentifier) as UIViewController
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
return true
}

Whose view is not in the window hierarchy only in First Launch

I have an Onboarding screen that I'm showing to the new users when they first open the app.
In my appDelegate I check whether is the first launch or not.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.main.bounds)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var initialViewController = storyboard.instantiateViewController(withIdentifier: "OnBoarding")
let userDefaults = UserDefaults.standard
if userDefaults.bool(forKey: "onBoardingComplete") {
initialViewController = storyboard.instantiateViewController(withIdentifier: "MainApp")
}
window?.rootViewController = initialViewController
window?.makeKeyAndVisible()
}
Also I have a collectionViewCell that I have some buttons and when I click them I get an Alert with informations.
Example of one button
#IBAction func guide3Btn(_ sender: Any) {
let infoVC = infoService.info(title: "Title", body: "Information")
self.window?.rootViewController?.present(infoVC, animated: true, completion: nil)
}
When the user first launches the app if he clicks the info button gets this:
Warning: Attempt to present <MyApp.InfoViewController: 0x7f91db45cfb0> on <MyApp.OnbBoardViewController: 0x7f91db506af0> whose view is not in the window hierarchy!
If the user reopens the app everything is ok. I know that when we have first launch we have onBoarding as root controller but I can't understand how to fix this.
Update
This is the infoService class. I use a new storyboard to create the alert.
class InfoService {
func info(title: String, body: String) -> InfoViewController {
let storyboard = UIStoryboard(name: "InfoStoryboard", bundle: .main)
let infoVC = storyboard.instantiateViewController(withIdentifier: "InfoVC") as! InfoViewController
infoVC.infoBody = body
infoVC.infoTitle = title
return infoVC
}
}
You can try add your storyboard instantiate code blocks to main thread using DispatchQueue.main.async like below:
I solved almost all of my whose view is not in the window hierarchy! problems.
DispatchQueue.main.async {
let infoVC = storyboard.instantiateViewController(withIdentifier: "InfoVC") as! InfoViewController
infoVC.infoBody = body
infoVC.infoTitle = title
}
return infoVC
Referenced from : https://stackoverflow.com/a/45126338/4442254

How to put an onboarding screen on the screen even though the navigation controler is the rootView

I am making an app that includes an onboarding screen inn swift. In the AppDelegate.swift I set the navigationController as the root view controller. But I have an onboarding screen and need to add that to the code also. Whenever I run the app I get Thread 1: SIGNAL SIBAGRT. Here is my code:
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [ .
UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
let sb = UIStoryboard(name: "Onboarding", bundle: nil)
let initialViewController = sb.instantiateInitialViewController()
window?.rootViewController = initialViewController
window?.makeKeyAndVisible()
let navigationController = window!.rootViewController as! UINavigationController
let controller = navigationController.viewControllers[0] as! AllListsViewController
controller.dataModel = dataModel
Try that:
window = UIWindow(frame: UIScreen.main.bounds)
let storyboard = UIStoryboard(named:"Onboarding",bundle: nil)
let nvc = storyboard.instantiateInitialViewController() as? UINavigationController // since your storyboard start with a navigationController
let topVc = nvc.topViewController as? AllListsViewController // then we get the first controller associated to that navigationController
topVc?.dataModel = dataModel
window?.rootViewController = nvc // still set your navigationController as the root of your application
window?.makeKeyAndVisible()

Have ViewController appear only on first launch

I would like to have my viewController named "DoctorInformationController" appear only when the user launches the app for the first time. Ive seen some people's answers, however i would like to know how to do this in swift please!
Thank you
This code I use that will launch a viewcontoller based on first or second launch of the app.
This in the AppDelegate.
var defaults: NSUserDefaults
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let firstLaunch = defaults.boolForKey("FirstLaunch")
if firstLaunch {
print("Not first launch.")
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyboard.instantiateViewControllerWithIdentifier("navController") as! UINavigationController
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
} else {
print("First launch, setting NSUserDefault.")
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyboard.instantiateViewControllerWithIdentifier("firstrunVCstoryboard") as UIViewController
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
}
return true
}
You would need to adapt it to fit your storyboard and viewcontrollers names.
EDIT:
This how I set the value in the first view controller to indicate that first run has completed. So that it will not show again.
self.defaults.setBool(true, forKey: "FirstLaunch")
There is no way to tell if it's the first launch built into the UIKit framework, however you can do it another way.
In AppDelegate.swift, go to applicationDidFinishLaunchingWithOptions method (the first one, usually). Now, add this:
//Check for key "first_launch" in User Defaults
if let _ = NSUserDefaults.standardUserDefaults().objectForKey("first_launch") {
//Set your own global variable to be true. Then, when your ViewController
//loads, do a popup window with that DoctorViewController thingy if the
//variable is true
//Example:
isFirstLaunch = true
//Then, set "first_launch" to be a value so your app will never call this block again
NSUserDefaults.standardUserDefaults().setObject("", forKey: "first_launch")
}