I want to set a view controller as initial view controller - swift4

When user is already logged In then the app should be open from home page and he/she should not go to the login page again.
I have tried this code it shows no error but it is not working.
let token = UserDefaults.standard.string(forKey: "token") ?? ""
if !token.isEmpty
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let firstVC =
storyboard.instantiateViewController(withIdentifier:
"initController") as! ViewController
self.window?.rootViewController = firstVC
self.window?.makeKeyAndVisible()
}
I expect that when user is already logged In then when she again open the app he/she must directly go the home page and should not go again to the login page.

let token = UserDefaults.standard.string(forKey: "token") ?? ""
if !token.isEmpty
{
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window = UIWindow(frame: UIScreen.main.bounds)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let yourVC = mainStoryboard.instantiateViewController(withIdentifier:
"initController") as! HomeController
appDelegate.window?.rootViewController = yourVC
appDelegate.window?.makeKeyAndVisible()
super.viewDidLoad()
}

let rootViewController = self.storyboard?.instantiateViewController(withIdentifier: "rootVcIdentifier") as! UINavigationController
self.view.window?.rootViewController = rootViewController

Related

How to change rootViewController in swift?

I am working on an application in which there is no login logout feature. When user opens the application he sees main screen. Now there is an option in setting page named as Pin Lock application. When user select this option a PinScreen comes(like OTP Screen) then user sets the Pin. After that When user closes the application the Pin lock screen comes first asking user to enter pin.
So how can i do that?
if UserDefaults.standard.bool(forKey: "LoggedIn") == false {
let story = UIStoryboard(name: "Main", bundle:nil)
let vc = story.instantiateViewController(withIdentifier: "EnterPin") as! UINavigationController
UIApplication.shared.windows.first?.rootViewController = vc
UIApplication.shared.windows.first?.makeKeyAndVisible()
let enterPinViewController = self.storyboard.instantiateViewController(identifier: "EnterPinViewController") as! EnterPinViewController
self.navigationController.present(enterPinViewController, animated: true, completion: nil)
} else if UserDefaults.standard.bool(forKey: "LoggedIn") == true {
let story = UIStoryboard(name: "Main", bundle:nil)
let vc = story.instantiateViewController(withIdentifier: "MainController") as! UINavigationController
UIApplication.shared.windows.first?.rootViewController = vc
UIApplication.shared.windows.first?.makeKeyAndVisible()
let mainViewController = self.storyboard.instantiateViewController(identifier: "ViewController") as! ViewController
self.navigationController.present(mainViewController, animated: true, completion: nil)
}
Currently i am setting UserDefaults value as true and false and saving Pin in UserDefaults itself. I have tried changing rootViewController but nothing happening means every time main screen is coming instead of Pin Screen.
First you need to get the Window from SceneDelegate and use that window for rootViewController.
Suggestion: Whenever we are using rootViewController then no need to do present and push thing it will automatically set as a main home viewcontroller(Intial viewController)
let window = (UIApplication.shared.connectedScenes.first!.delegate as! SceneDelegate).window
let storyboard = UIStoryboard(name: "Main", bundle:nil)
if UserDefaults.standard.bool(forKey: "LoggedIn") == false {
let vc =storyboard.instantiateViewController(withIdentifier: "EnterPin") as! UINavigationController
window?.rootViewController = vc
window?.makeKeyAndVisible()
} else if UserDefaults.standard.bool(forKey: "LoggedIn") == true {
let vc =storyboard.instantiateViewController(withIdentifier: "MainController") as! UINavigationController
window?.rootViewController = vc
window?.makeKeyAndVisible()
}
I suggest you do all this things in SceneDelegate file
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let scene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: scene)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if UserDefaults.standard.bool(forKey: "LoggedIn") == false {
let vc = storyboard.instantiateViewController(withIdentifier: "EnterPin") as! UINavigationController
self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()
} else if UserDefaults.standard.bool(forKey: "LoggedIn") == true {
let vc = storyboard.instantiateViewController(withIdentifier: "MainController") as! UINavigationController
self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()
}
}
I Hope it is helpful for you!
let viewController = MainViewController()
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
guard let window = appDelegate.window else {
return
}
window.rootViewController = viewController

How do I show the data retrieved from Firebase onto a view controller that is inside a tab bar controller?

I have a ViewController that I would like to show the activity indicator while fetching username data from firebase and changing the username on the MenuViewController. After that, it would show the MenuViewController. But the problem I'm facing is the "menuVC.greetText = "Hi " + username" line doesn't work. There is no username being shown on the ViewController. Any advice is appreciated. Thanks in advance.
class LoadingViewController: UIViewController {
#IBOutlet var activityIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
startLoading {
switchToMenu()
}
// Do any additional setup after loading the view.
}
func startLoading(finished: () -> Void) {
activityIndicator.startAnimating()
authenticateUserAndConfigureView()
finished()
activityIndicator.stopAnimating()
}
func switchToMenu() {
DispatchQueue.main.async {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let mainViewController = storyboard.instantiateViewController(identifier: "MainVC")
self.view.window?.rootViewController = mainViewController
self.view.window?.makeKeyAndVisible()
}
}
func authenticateUserAndConfigureView() {
if Auth.auth().currentUser == nil {
DispatchQueue.main.async {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginController = storyboard.instantiateViewController(identifier: "LoginViewController")
self.view.window?.rootViewController = loginController
self.view.window?.makeKeyAndVisible()
}
}
else {
//let tabBar = segue.destination as! UITabBarController
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let tabBar = storyboard.instantiateViewController(identifier: "MainVC") as! UITabBarController
let menuVC = tabBar.viewControllers?[0] as! MenuViewController
guard let uid = Auth.auth().currentUser?.uid else {return}
Database.database().reference().child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let username = value?["Username"] as? String ?? ""
menuVC.greetText = "Hi " + username
}
)
}
}
}
You are dealing with asynchronous function here and you are also inside closure, so meaning that as soon as you leave the closure, the assigned values inside closure will not be available. You need completionHandler to deal with it. You can make a separate function of it and then call it inside authenticateUserAndConfigureView.
func getName(completion:#escaping((String)->()) {
Database.database().reference().child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let username = value?["Username"] as? String ?? ""
completion(name)
}
And then you can call this function like this.
getName{(nameOfUser) in
print("Name is \(nameOfUser)")
}
In your case:
Calling inside authenticateUserAndConfigureView
func authenticateUserAndConfigureView() {
if (......) {
//All your code
}
else {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let tabBar = storyboard.instantiateViewController(identifier: "MainVC") as! UITabBarController
let menuVC = tabBar.viewControllers?[0] as! MenuViewController
self.getName{(name) in
menuVC.greetText = "Hi" + name
}
}
}
You may need to use DispatchQueue here for possible timing issue, but I am not sure about that. If you do, let me know.

Perform segue in AppDelegate

When a Firebase token is invalid, I want that the user is redirect to my app loginVC. This is my code:
func userErrorLogout() {
print("user error logout")
let storyboard = UIStoryboard(name: "Main", bundle: nil);
let viewController: ViewController = storyboard.instantiateViewController(withIdentifier: "loginVC") as! ViewController;
let rootViewController = self.window!.rootViewController as! UINavigationController;
rootViewController.pushViewController(viewController, animated: true);
}
I get an error on "let rootViewController:.... = 2019-11-27 13:03:49.364864+0100 Geo[25133:2193886] Fatal error: Unexpectedly found nil while unwrapping an Optional value: file
Since SWIFT is new to me, I am not able to debug this message. How do I perform the redirection?
you need to use the below code :
let storyboard = UIStoryboard(name: "Main", bundle: nil);
let viewController: ViewController = storyboard.instantiateViewController(withIdentifier: "loginVC") as! ViewController;
let window = (UIApplication.shared.delegate as! AppDelegate).window!
DispatchQueue.main.async {
window.rootViewController = viewController
}
You need to assign the value for your rootViewController. Try this:
func userErrorLogout() {
print("user error logout")
let storyboard = UIStoryboard(name: "Main", bundle: nil);
let viewController: ViewController = storyboard.instantiateViewController(withIdentifier: "loginVC") as! ViewController;
let nav = UINavigationController(rootViewController: viewController)
self.window?.rootViewController = nav
}
You need to set the viewcontroller to window. You can do like below. This code also has transition animation it gives fade to transition.
// MARK: WINDOW SET
func gotoIntro() {
UIView.transition(with: window!, duration: 0.5, options: .transitionCrossDissolve, animations: {
UIView.performWithoutAnimation({
let introStoryboard = UIStoryboard(name: "Intro", bundle: nil)
let navigationController = introStoryboard.instantiateInitialViewController() as! UINavigationController
self.window?.rootViewController = navigationController
})
}, completion: nil)
}
As of iOS 13, pushing a VC at the start of a launch is not possible from AppDelegate anymore (Not 100% sure).
You must use SceneDelegate if you wish to present the view from your root.
To do so, simply do this inside your SceneDelegate file. I have tested this approach on my own app and is working 100%
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: windowScene)
// check if the token is valid
if valid {
let vc = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "yourVC")
self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()
}
else {
// token is invalid
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let rootVC = storyboard.instantiateViewController(identifier: "invalidVC")
let rootNC = UINavigationController(rootViewController: rootVC)
self.window?.rootViewController = rootNC
self.window?.makeKeyAndVisible()
}
}

Check if user already exists Firebase swift 3.0

let database = FIRDatabase.database().reference()
database.child("Users").queryOrderedByKey().observe(.childAdded, with: { (snapshot) in
print(snapshot)
if let value = snapshot.value as? [String: AnyObject] {
let ui = value["id"] as! String
if ui != FIRAuth.auth()!.currentUser!.uid
{
var storyboard = UIStoryboard(name: "Main", bundle: nil)
if s == true
{
storyboard = UIStoryboard(name: "Main", bundle: nil)
}
else if p == true
{
storyboard = UIStoryboard(name: "big", bundle: nil)
}
else if se == true
{
storyboard = UIStoryboard(name: "se", bundle: nil)
}
else if os == true
{
storyboard = UIStoryboard(name: "4s", bundle: nil)
}
else if ip1 == true
{
storyboard = UIStoryboard(name: "ipad1", bundle: nil)
}
else if ipb == true
{
storyboard = UIStoryboard(name: "ipadbig", bundle: nil)
}
let naviVC = storyboard.instantiateViewController(withIdentifier: "eula")as! UIViewController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = naviVC
}
else
{
var storyboard = UIStoryboard(name: "Main", bundle: nil)
if s == true
{
storyboard = UIStoryboard(name: "Main", bundle: nil)
}
else if p == true{
storyboard = UIStoryboard(name: "big", bundle: nil)
}
else if se == true
{
storyboard = UIStoryboard(name: "se", bundle: nil)
}
else if os == true{
storyboard = UIStoryboard(name: "4s", bundle: nil)
}
else if ip1 == true
{
storyboard = UIStoryboard(name: "ipad1", bundle: nil)
}
else if ipb == true
{
storyboard = UIStoryboard(name: "ipadbig", bundle: nil)
}
let naviVC = storyboard.instantiateViewController(withIdentifier: "yo")as! UIViewController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = naviVC
}
}
})
I have a firebase app, and I want to check if a user exists in my databse, If the don't i want to display a licenses agreement, but if they do i don't have too. My current method only works if there are two users. Thank you for your help in advance.
I figured it out
let database = FIRDatabase.database().reference()
database.child("Users").observeSingleEvent(of: FIRDataEventType.value, with: { (snapshot) in
print(snapshot)
if snapshot.hasChild( FIRAuth.auth()!.currentUser!.uid)
{
All you need to do is change .ChildAdded to .value. This will make sure its reading the data when you start.
With that code I think it will only execute when a child is added. I can't check it out right now but try with
database.child("Users").queryOrderedByKey().observe(.Value, with:{ (snapshot) in
...
})
As I said I can't check it so answer me if you have any problem. Good luck!

How to detect a first app launching and display an instruction screen?

I am developing a walkthrough screen which should appear only the first time when the user opens the app. So far I created the walkthrough page and PageViewController.
See the picture:
I read lots of similar questions here and I understood that I have to use
UserDefaults()
inside the AppDelegate, but I didn't understand how to use the class and storyboard names inside the code.
Basically, when the app is opened for the first time, the PageViewController should appear on the screen and when the user clicks on the start button, which is on the WalkThroughScreen it will dismiss the tutorial page and the app will start.
I have tried this code:
if let isFirstStart = UserDefaults.standard.value(forKey: "isFirstLaunch") as? Bool {
if defaults.bool(forKey: "isFirstLaunch") {
defaults.set(false, forKey: "isFirstLaunch")
let mainStoryboard = UIStoryboard(name: "WalkThroughScreen", bundle: Bundle.main)
let vc : WalkThroughScreen = mainStoryboard.instantiateViewController(withIdentifier: "PageViewController") as! WalkThroughScreen
self.present(vc, animated: true, completion: nil)
}
I am pretty sure that it is a complete mess, because I didn't understand it very good and I haven't used the TutorialPage, so I will be very thankful if someone leave me hints or example how to do it correctly
Yes you are right, you have to use userDefaults to achieve this. And you have to do it inside appDelegate()
Roel Koops answer should do it but you can try it like this also:
let launchedBefore = UserDefaults.standard.bool(forKey: "launchedBefore")
if launchedBefore {
print("This is not first launch.")
} else {
print("This is first launch.")
UserDefaults.standard.set(true, forKey: "launchedBefore")
UserDefaults.standard.synchronize()
let mainStoryboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let vc : WalkThroughScreen = mainStoryboard.instantiateViewController(withIdentifier: "WalkThroughScreen") as! WalkThroughScreen
self.present(vc, animated: true, completion: nil)
}
And make sure to declare: let userDefaults = UserDefaults.standard and use it inside didFinishLaunchingWithOptions.
There are even more solutions so I give you one more:
let userDefaults = UserDefaults.standard
if !userDefaults.bool(forKey: "launchedBefore") {
let mainStoryboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let vc : WelcomeViewController = mainStoryboard.instantiateViewController(withIdentifier: "WalkThroughScreen") as! WelcomeViewController
self.window?.makeKeyAndVisible()
self.window?.rootViewController?.present(vc, animated: false, completion: nil)
userDefaults.set(true, forKey: "launchedBefore")
userDefaults.synchronize()
}
You can even declare all the storyboard thing in one line:
self.window?.rootViewController = self.storyboard?.instantiateViewController(withIdentifier: "WalkThroughScreen")
But this assumes that you declare 2 variables:
var window: UIWindow?
var storyboard: UIStoryboard?
If it didn't work just tell me, I am gonna try to help.
The code in your if-block is never executed if the key "isFirstLaunch" doesn't exist.
Try this:
if let isFirstStart = UserDefaults.standard.value(forKey: "isFirstLaunch") as? Bool {
print("this is not the first launch")
} else {
print("this is the first launch")
UserDefaults.standard.set(false, forKey: "isFirstLaunch")
UserDefaults.standard.synchronize()
let mainStoryboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let vc : WalkThroughScreen = mainStoryboard.instantiateViewController(withIdentifier: "WalkThroughScreen") as! WalkThroughScreen
self.present(vc, animated: true, completion: nil)
}