Accessing a modal viewController from Appdelegate - ios

I have a situation where if the user is not logged in to Firebase, when they launch my app, a modal viewcontroller is presented asking them to log in.
I am also using UIActivityViewController to send data between devices, but want to notify the user with an alert if he is not logged in.
I figure I can do this from the AppDelegate in the open url application function:
func application(_ app: UIApplication, open url: URL, ...
I just have to figure out how to navigate to that viewcontroller to call the function that will present my alert.
Within this function, I have this:
if status == .notLoggedIn {
guard
let rootVC = window?.rootViewController as? UITabBarController,
let collectionSplitVC = rootVC.viewControllers?.first as? UISplitViewController,
let navVC = collectionSplitVC.viewControllers.first as? UINavigationController,
let MyBookshelfCollectionVC = navVC.children.first as? MyBookshelfCollectionVC
// here is where I want to get access to the modal viewController presented on this MyBookshelfCollectionVC.
// it is called LoginVC
else { return true }
LoginVC.showAlertFromAppDelegate(status: status)
}
So the question his, how do I access this modal viewcontroller?

Related

I am having a problem with checking if my users are signed in to Firebase and changing the the initial view controller

My goal is to check if my users have signed in to my app in Firebase before. Then change the initial view controller from my Navigation controller to my TabBarController. I'd like to do this for a better user experience so they don't have to log in every time.
Also where is the best place to put this code? my first View controller or my app Delegate?
if Auth.auth().currentUser != nil {
// User is signed in.
func transitionToTab() {
let tabBarController =
storyboard?.instantiateViewController(identifier: Constants.Storyboard.TabBarController) as? TabBarController
view.window?.rootViewController = tabBarController
view.window?.makeKeyAndVisible()
}
} else {
// No user is signed in.
func tranitionToView() {
_ =
storyboard?.instantiateViewController(identifier: Constants.Storyboard.HomeViewController) as? ViewController
view.window?.makeKeyAndVisible()
}
}
Your problem is you don't call the functions within the if/else statement.
I would recommend creating an additional slash screen that replicates the one within launchscreen.storyboard to initiate your authentication process. Your code should look similar to this;
if Auth.auth().currentUser != nil {
// User is signed in.
let tabBarController =
storyboard?.instantiateViewController(identifier: Constants.Storyboard.TabBarController) as? TabBarController
view.window?.rootViewController = tabBarController
view.window?.makeKeyAndVisible()
} else {
// No user is signed in.
storyboard?.instantiateViewController(identifier: Constants.Storyboard.HomeViewController) as? ViewController
view.window?.makeKeyAndVisible()
}
you can use this function from app delegate method
"didFinishLaunchingWithOptions"
and make an userdefaults to store current user object or any check that you
will true once user login into the app and in Appdelegate "didFinishLaunchingWithOptions"
check if it's true than set initial page
tabbarcontroller else show login screen.

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

How to call ViewControllers in regular sequence at AppDelegate.swift

now I am developing messenger
MessengerBox is tableViewController, when user tap the one of the cell, then chatRoomViewcontroller is presented.
if the app is not running and message arrived, then push notifications show. And user Tap the notification, App shows chatRoomViewcontroller directly.
Initially, I implemented this code by using window.rootViewController
But the problem happened. when I tap Back Button of chatRoomviewController, change is not happened because this view controller is rootview and its presentingViewController is empty!
so I fixed it like below
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
///some code for notification..
let mVC //this is MessengerBoxViewcontroller
let crVC //this is ChatRoomViewController
CRVC?.sender = "asdf"
do{
self.window?.rootViewController = mVC
self.window?.makeKeyAndVisible()
defer{
mVC?.presentChatRoomVC()
}
}
/// some code...
}
It works! But I'd like to know better way.
And Also I think I should study How window and viewcontrollers works.
please recommend me the better way, and reference documents.
Thank you.
You need to handle back button action programatically in ChatRoomViewControllerto solve your problem :
On back button Click :
Check for MessengerBoxViewcontroller is present in navigationController stack.
if MessengerBoxViewcontroller is in navigationController stack then pop to move back to MessengerBoxViewcontroller.
Else present MessengerBoxViewcontroller.
if let viewControllers = self.navigationController?.viewControllers {
for viewController in viewControllers {
if viewController.isKindOfClass(MessengerBoxViewcontroller) {
print("Your controller exist")
navigationController?.popViewController(animated: true)
}
}
}
else {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "messengerBoxViewcontroller") as! MessengerBoxViewcontroller
self.present(vc, animated: true, completion: nil)
}

AddingViewcontroller manually into the stack swift 3

I am coding the sign out button in my app.This is the flow of my app.
SplashvwController -> secretCodeViewController ->
LoginviewController -> DashboardViewController
in this DashboardViewController I have the signOut button.
My app has single sign in facility once user logged, next time when he opens the app,
SplashvwController -> DashboardViewController
I want to prompt the user to Loginviewcontroller whenever he clicks the sign out button.
Question
When user going through path 1 I can simply do popviewcontroller to go back to the previous viewcontroller. But when user go though the 2nd path,
how can I add the Loginviewcontroller manually into my
viewcontrollers stack to perform the same operation?
How can I check whether the LoginviewController exists in my current Viewcontrollers stack?
Please help me
I think, the below code helps you,
for (var i = 0; i < self.navigationController?.viewControllers.count; i++) {
if(self.navigationController?.viewControllers[i].isKindOfClass(Loginviewcontroller) == true) {
self.navigationController?.popToViewController(self.navigationController!.viewControllers[i], animated: true)
break;
}
}
To add a ViewController Manually , check the code below..
let navigation = mainStoryboard.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
let nav = UINavigationController(rootViewController: navigation)
appdelegate.window!.rootViewController = nav
Set the storyboard reference identifier for a LoginViewController on the Main.Storyboard file.
Whenever you want to show the LoginViewController just call the function
func launchLoginView() {
if let loginVC: LoginViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("LoginStoryboardID") as? LoginViewController {
// .instantiatViewControllerWithIdentifier() returns AnyObject! this must be downcast to utilize it
// LoginStoryboardID is the reference id for login view controller.
self.presentViewController(loginVC, animated: true, completion: nil).
// OR
//UIApplication.shared.keyWindow?.rootViewController = loginVC
}
}
Landing screen based on the User login status.
In AppDelegte.swift
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let isLoggedAlready = //Get the login status
if isLoggedAlready == true {
if let dashBoardVC: DashboardViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("DashboardStoryboardID") as? DashboardViewController {
//Already logged in then directly launch dashboard vc.
//change the code based on your needs
window?.rootViewController = dasbBoardVC
}
}
// Otherwise let it go as flow 1
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.

Resources