How to present different view controllers from appdelegate in ios - ios

Actually I am having navigationcontroller as root controller it is embed in main.storyboard,i am having two screens one screen login and another one home as per login credentials i need to skip login screen and i need to show home screen.From appdelegate i am doing this skipping it is not working properly
Unbalanced calls to begin/end appearance transitions for <UINavigationController: 0x7fadf384c600>.
let storyboard=UIStoryboard.init(name: "Main", bundle: nil)
let navigationController=storyboard.instantiateInitialViewController()
let username=UserDefaultUtil.getString(key: AppConstants.PREF_USERID)
print(username!)
if username != ""
{
window?.rootViewController=navigationController
let sectionController=SectionController(nibName: "SectionController" , bundle: nil)
navigationController?.present(sectionController, animated: true, completion: nil)
}

I guess you are trying to present your sectionController in navigationController, its not really how it works, try this code:
let navigationController = self.storyboard?.instantiateInitialViewController() as! UINavigationController
and replace the present with this:
navigationController.setViewControllers([sectionController], animated: false)
or just drop the navigationController instantiate and create it with code and set it as window?.rootViewController:
let sectionController=SectionController(nibName: "SectionController" , bundle: nil)
let nav = UINavigationController(rootViewController: sectionController)
window?.rootViewController = nav

First, check the user credentials in the login page. Then use:
if hasCredentials {
let vc:AnyObject! = self.storyboard?.instantiateViewController(withIdentifier: "someViewController")
self.show(vc as! UIViewController, sender: vc)
}
Sidenote: Personally, I do this from the login page because it simplifies the process and I do not like having weight sitting in my AppDelegate. If you were thinking you did not want people seeing your login screen who are already members, you can do it from an AppDelegate, but take into account the user experience might be diminished during the loading process if this is the route you decide to take.

Related

Bar item not show when I use segue programmatically

I’m building an app which has a main vc that only existing user can log in to. The main page is the initial vc so I embed in the navigation bar to it. When I open the app in the first time I can see the bar items, but when I sign out, and log in again I can’t see the bar items, someone know why? Should I add some code? or maybe change some definitions?
I’m using segue programmatically like this:
private var handle: AuthStateDidChangeListenerHandle?
handle = Auth.auth().addStateDidChangeListener({ (auth, user) in
if user == nil{
if let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Home") as? MainVC
{
self.present(vc, animated: true, completion: nil)
}
}else{
//keep going with the code...
})
In the simulator it starts with the main vc then I sign out (pic 1), then I log in (pic 2) and get back to the main vc but now I can’t see the bar items (pic 3).
link to the app pictures
you must present your main vc in navigationcontroller
private var handle: AuthStateDidChangeListenerHandle?
handle = Auth.auth().addStateDidChangeListener({ (auth, user) in
if user == nil{
if let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Home") as? MainVC
{
let navVc = UINavigationController(rootViewController: vc)
self.present(navVc, animated: true, completion: nil)
}
}else{
//keep going with the code...
})

How to present a view controller from appdelegate xcode 11 swift 5

I have been searching all day on how to present a view controller from within the appdelegate. It appears that in xcode 11 the window property was moved to the scenedelegate which has been confusing me. I want to present a view controller from within the appdelegate from the didReceiveRemoteNotification function so when the user receives a notification it takes them to a separate view controller with information. I have tried to do:
self.window?.rootViewController?.present(LoginViewController(), animated: false, completion: nil)
within the appdelegate which used to work in a previous application of mine but it does not seem to work anymore. Any help would be much appreciated.
I was able to solve this issue by using shared windows to get the window from scenedelegate to present the view controller on.
UIApplication.shared.windows.first?.rootViewController?.present(vc, animated: false, completion: nil)
Best approach to present view controller through app delegate is without falling for hierarchy like below:
if let vc = UIStoryboard(name: "YOURSTORYBOARD", bundle: nil).instantiateViewController(withIdentifier: "YOURVIEWCONTROLLER") as? YOURVIEWCONTROLLER {
if let window = self.window, let rootViewController = window.rootViewController {
var currentController = rootViewController
while let presentController = currentController.presentedViewController {
currentController = presentController
}
currentController.present(vc, animated: true, completion: nil)
}
}

How to move login page when session expired

I have multiple screens and api's, if session expired each api get session expired message. Based in that I'm moving from current page to login page.
My code :
//If session expaired move to login page
if message == "Session Expired" {
//Session Expired
DispatchQueue.main.async {
let lpc = self.storyboard?.instantiateViewController(withIdentifier: "LVC")
//Set the user login key false
UserDefaults.standard.set(false, forKey: "isUserLoggedIn")
//Clear user defaults
SharedClass.sharedInstance.clearDataFromUserDefaults()
self.navigationController?.pushViewController(lpc!, animated: false)
}
}
Here I'm using ** pushViewController** to navigate back. Every thing working fine, but when i'm in Nth VC is session expired it's navigating N times to login page. Means if I'm navigating from 1st VC to 2nd VC, 2nd VC to 33rd VC, 3rd VC to 4th, is session expired in 4th VC it's navigating to login page around 3times. How to resolve this issue....
You can try this
let vc = self.storyboard?.instantiateViewController(withIdentifier: "LVC")
self.navigationController?.setViewControllers([vc], animated: true)
Since you didn't provide your full code so I am not sure why that issue happens. However, here are 2 solutions to achieve your requirement.
First implementation: Image for 1st flow
If the user logged in:
navigationController?.viewControllers = [loggedInVC]
If the user doesn't login yet or when he/she logout:
navigationController?.viewControllers = [logInVC]
2nd implementation: Image for 2nd flow
LoginVC is the root view controller and acts as a loading screen, and only show the login form when the session is invalid. With this implementation, every time the session is invalid. You can call:
navigationController?.popToRootViewController(animated: true)
to navigate the user back to the login screen.
First create the global variable to access the AppDelegate, because we are putting redirection function here.
let appDelegate = UIApplication.shared.delegate as! AppDelegate
Now put below function in AppDelegate to set login screen whenever your session is expired.
func configureWindow(_ viewController: UIViewController) {
if let window = window {
window.rootViewController = viewController
window.makeKeyAndVisible()
}
}
now set your login UIViewController as like below.
let loginVC = LoginViewController()
appDelegate.configureWindow(loginVC)
use the following:
self.navigationController?.popToRootViewController(animated: false)
if login is not root,then you can use the following:
if let controllers = self.navigationController?.viewControllers, controllers.count > 0{
for vc in controllers{
if vc is LoginViewController{
self.navigationController?.popToViewController(vc, animated: false)
}
}
}
I don't know is this right or wrong. But i'm getting one more issue
1) First i cleared all navigations from navigation stack
2) Then i made my LoginVC as Root VC
3) Finally i called popToRootViewController
//If session expaired move to login page
if message == "Session Expired" {
DispatchQueue.main.async {
//Check navigation stacks
let navigationArray = self.navigationController?.viewControllers //To get all UIViewController stack as Array
print(navigationArray!)//Prints navigation stacks
//Remove all
self.navigationController!.viewControllers.removeAll()
//Check navigation stacks
let navigationArray2 = self.navigationController?.viewControllers //To get all UIViewController stack as Array
print(navigationArray2 as Any)//Prints nill
//Check whether the user logined or not
UserDefaults.standard.set(false, forKey: "isUserLoggedIn")
//Clear user defaults
SharedClass.sharedInstance.clearDataFromUserDefaults()
let lvc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LVC") as! LoginViewController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = lvc
}
}
//Call alert function
self.showAlert(title: "", msg: message)
I don't know is this right or wrong. But i'm getting one more issue
Warning: Attempt to present <UIAlertController: 0x7fb840800600> on <******: 0x7fb840069800> whose view is not in the window hierarchy!

How to connect Universal Links to UIViewController?

*Disclaimer: I've only been coding in iOS/XCode/Swift for a couple of weeks
I have Universal Links working in that clicking a link outside my app opens up the app and I catching the Url in the AppDelegate class.
So that's all good.
My question is... how do I then redirect to the correct UIViewController AND pass the controller some info from my URL? All Universal Link tutorials stop before they get to that part.
In particular I'm confused about the lifecycles of AppDelegate and how it relates to UIViewController.
My app had two UIViewController sitting under (is this right?) a UINavigationController.
What I've tried
I have tried handling the url event in AppDelegate, and setting a public property, and then in my ViewController getting access to the AppDelegate. HOWEVER, after the Universal Link is clicked, both viewDidLoad and viewWillAppear don't get called again :-/
What's the best way to redirect to a ViewController from AppDelegate? My goal is simply to load the root view controller BUT I need to pass in some data from the URL. How?
First, Read your URL.
Get your parameters from URL
Initiate your target controller
Set your parameter to that controller
Present controller on root view controller
let urlString = url.absoluteString
let queryArray = urlString.componentsSeparatedByString("/")
if queryArray[2].lowercaseString == "yourQuery" {
let queryId = Int(queryArray[3])
if self.window?.rootViewController?.presentedViewController != nil {
self.window?.rootViewController?.dismissViewControllerAnimated(false, completion: nil)
}
let queryVC = self.window?.rootViewController?.storyboard?.instantiateViewControllerWithIdentifier(QUERY_SCENE) as? QueryViewController
queryVC?.urlQueryId = queryId!
self.window?.rootViewController?.presentViewController(queryVC!, animated: true, completion: nil)
}
Edit:
Push a controller say 'PresentedViewController' on navigation controller and if rootViewController is also navigation controller
And on back press on controller 'OnBackPressViewController' present controller 'PresentedViewController'
if self.window?.rootViewController?.presentedViewController != nil {
self.window?.rootViewController?.dismissViewControllerAnimated(false, completion: nil)
}
let navController = self.window?.rootViewController?.storyboard?.instantiateInitialViewController() as? UINavigationController
let presentedVC = self.window?.rootViewController?.storyboard?.instantiateViewControllerWithIdentifier(PRESENTED_SCENE) as? PresentedViewController
//Pass parameters to PresentedViewController
let onBackPressViewController = self.window?.rootViewController?.storyboard?.instantiateViewControllerWithIdentifier(ON_BACK_PRESS_SCENE) as? OnBackPressViewController
navController!.pushViewController(onBackPressViewController!, animated: true)
navController!.pushViewController(presentedVC!, animated: true)
self.window?.rootViewController?.presentViewController(navController!, animated: true, completion: nil)
You can update your code according to that. Always remember that you have to present any view controller on rootViewController.
It depends on your app architecture. You can definitely assume that the AppDelegate has the reference to the rootViewController (window?.rootViewController). You should know what's the type of that controller. Then you can access to the other and so on. This question is, anyway, too much generic in my opinion.

revealViewController() always returns nil

I'm having some troubles with revealViewController in Xcode 7.2 and iOS 9.2.
My app starts with a view controller embedded in a navigation controller to perform a login.
After login, or if the login token is present, I jump to another view controller embedded in a navigation controller with the following code:
let homePage = self.storyboard?.instantiateViewControllerWithIdentifier("HomeViewController") as! HomeViewController
let homePageNav = UINavigationController(rootViewController: homePage)
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window?.rootViewController = homePageNav
In this home view controller I would like to have a left navigation menu with SWRealViewController.
I had the SWRealViewController view linked with sw_front to my home navigation controller, and the following code:
if (self.revealViewController() != nil) {
self.menuButton.target = self.revealViewController()
self.menuButton.action = "revealToggle:"
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
But self.revealViewController() always returns nil, so it does not work.
I think I lost the revealViewController somewhere (maybe when I jump from the first navigation controller to the second) but I do not know what to do.
The most convenient to be a reason for the revealViewController to be nil
is you didn't connect segues correctly in stroyboard.
See this tutorial it's quite easy to follow.
Update
If in your case you just need to open a login vc if the user is not logged in you may do like this:
in AppDelegate
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
var rootVCStoryboardId = userIsLoggedin ? "SWRevealViewController" : "LoginViewController"
self.window?.rootViewController = UIStoryboard(name: Storyboards.main, bundle: NSBundle.mainBundle()).instantiateViewControllerWithIdentifier(rootVCStoryboardId)
Where SWRevealViewController is the stroyboard id for SWRevealViewController and LoginViewController is the storyboard id for your login view controller(or its navigation controller if exists).
In case someone is wondering how to do a manual segue, this is what worked for me at the end.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let sw = storyboard.instantiateViewControllerWithIdentifier("SWRevealViewController") as! SWRevealViewController
self.view.window?.rootViewController = sw
let destinationController = self.storyboard?.instantiateViewControllerWithIdentifier("StoryboardID") as! NameOfViewController
let navigationController = UINavigationController(rootViewController: destinationController)
sw.pushFrontViewController(navigationController, animated: true)
Incase you are skipping login scene based on the current user information, then make sure to instantiate the storyboard with SWRevealViewController. See below code for reference:
if User.currentUser != nil {
//There is a current user
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("SWRevealViewController")
window?.rootViewController = vc
}
else{
//No current user
}

Resources