How to dismiss current view controller from app delegate in swift? - ios

I want to pop current view controller on some condition from appDelegate but I don't know how to do so, if any idea please help me out...................................................................................
import UIKit
import IQKeyboardManagerSwift
let kSharedAppDelegate = UIApplication.shared.delegate as? AppDelegate
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
self.window = UIWindow(frame: UIScreen.main.bounds)
IQKeyboardManager.shared.enable = true
IQKeyboardManager.shared.shouldResignOnTouchOutside = true
IQKeyboardManager.shared.enableAutoToolbar = false
//IQKeyboardManager.shared.toolbarTintColor = .white
//IQKeyboardManager.shared.toolbarBarTintColor = ColorSet.appTheamColor
kSharedAppDelegate?.moveToSplashVC()
return true
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Check we can access the application window
guard let window = UIApplication.shared.windows.first else {
return
}
// Check we can access the root viewController
guard let vc = window.rootViewController else {
return
}
// Check the root vc is the type that we want to dismiss
if vc is NoInternetPopUpViewController {
vc.dismiss(animated: true, completion: nil)
}
}
//MARK:- Show No Internet connection VC
func showNoInterNetVC() {
guard let controller = UIStoryboard(name: Storyboards.kNoInternet, bundle: nil).instantiateViewController(withIdentifier: Identifiers.kNoInternetPopUpViewController) as? NoInternetPopUpViewController else {return}
controller.modalPresentationStyle = .overFullScreen
controller.modalTransitionStyle = .crossDissolve
kSharedAppDelegate?.window?.rootViewController?.present(controller, animated: true, completion: nil)
//window.present(controller , animated: true)
}
}

I think pop is the wrong terminology here unless you are using a navigation controller.
If you want to dismiss the currently presented viewController you could check the rootViewController of the applications Window like this.
// Check we can access the application window
guard let window = UIApplication.shared.windows.first else {
return
}
// Check we can access the root viewController
guard let vc = window.rootViewController else {
return
}
// Check the root vc is the type that we want to dismiss
if vc is NoInternetPopUpViewController {
vc.dismiss(animated: true, completion: nil)
}
I also just noticed that you may not need to access the application singleton via the shared property, as applicationDidBecomeActive(_ application: UIApplication) is passing you the Application already - that line would become:
guard let window = application.windows.first else {

There is another way to do this, in newer iOS versions you directly have access to topViewController() so you access it like UIApplication.topViewController() the only downside of it is that you need yo wrap it into an if let statement to check if it is not null. FYI, It won’t be null in most cases if you have let your didFinishLaunching() method run at least once in your app delegate. So that it can stack a view controller to be a top view controller. This won’t be a problem for you since all of the other methods will fail as well if this is the case.
Todo a pop view controller now all you need to do it use top view controller and perform pop on its navigation view controller, or you can dismiss it in case there is no navigation view controller.

Related

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)
}

Swift Firebase check authentication more smoothly

I'm setting my window's root view controller as the login controller right off the bat, and the login controller checks user authentication from Firebase. If the user is logged in, the controller changes the root view controller to be the feed controller. Otherwise, the login controller proceeds as itself.
class LoginController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let uid = Auth.auth().currentUser?.uid {
Database.database().reference().child("users").observeSingleEvent(of: .value, with: { snapshot in
if snapshot.hasChild(uid) {
AppDelegate.launchApplication()
}
else {
self.setUpUI()
}
})
}
else {
self.setUpUI()
}
}
...
}
where launchApplication is
class func launchApplication() {
guard let window = UIApplication.shared.keyWindow else { return }
window.rootViewController = UINavigationController(rootViewController: FeedController())
}
In addition to if let uid = Auth.auth().currentUser?.uid, I'm checking whether the uid (if it isn't nil) exists in my database, because I have had the situation where a deleted user still wasn't nil.
The problem is that after the launch screen finishes, there is a moment when the login controller, though blank, is visible. Sometimes this moment lasts a few seconds. How can I check authentication such that the login controller isn't visible at all—so that the app decides how to proceed immediately after the launch screen disappears? Thanks.
1) use this following code in case if you want to set a rootController from app delegate itself . use a check if your currentUser.uid is not nil and matched with values in database then perform following code in DidFinishLaunchingWithOptions of Appdelegate . Used by me
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
if let uid = Auth.auth().currentUser?.uid {
Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value, with: { snapshot in
if snapshot.exist() {
let storyBoard = UIStoryboard.init(name: "Main", bundle: nil)
let vc = storyBoard.instantiateViewController(withIdentifier: "feedController") as! FeedController
self.window?.rootViewController=vc
self.window?.makeKeyAndVisible()
}
else {
//Present your loginController here
}
})
}
return true
}
2) Method: when you had initialised your logincontroller instead of calling a function from Appdelegate or code in LaunchApplication. make a function in login class and write the following code when required parameters are matched
var transition: UIViewAnimationOptions = .transitionFlipFromLeft
let rootviewcontroller: UIWindow = ((UIApplication.shared.delegate?.window)!)!
rootviewcontroller.rootViewController = self.storyboard?.instantiateViewController(withIdentifier: "rootnav")//rootnav is Storyboard id of my naviagtionController attached to the DestinationController [FeedController]
let mainwindow = (UIApplication.shared.delegate?.window!)!
mainwindow.backgroundColor = UIColor(hue: 0.6477, saturation: 0.6314, brightness: 0.6077, alpha: 0.8)
UIView.transition(with: mainwindow, duration: 0.55001, options: transition, animations: { () -> Void in
}) { (finished) -> Void in
}
As a quick fix, you could try checking just the desired "users" child node (and not the complete branch). For instance:
Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value, with: { snapshot in
if snapshot.exists() { ...
This could reduce your delay considerably if your database contains many users.
If that isn't enough, you might consider moving this logic to you AppDelegate class as well and show your LoginController from there (and maybe holding off your launch screen a little longer until you find out if an user is available).
Instead of changing any code, I simply set the background of the login controller to match the launch screen:
class LoginController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// here
let background_image_view = UIImageView(image: #imageLiteral(resourceName: "launch_screen_background"))
background_image_view.frame = self.view.frame
self.view.addSubview(background_image_view)
if let uid = Auth.auth().currentUser?.uid {
Database.database().reference().child("users").observeSingleEvent(of: .value, with: { snapshot in
if snapshot.hasChild(uid) {
AppDelegate.launchApplication()
}
else {
self.setUpUI()
}
})
}
else {
self.setUpUI()
}
}
...
}
There is no noticeable transition from the launch screen to the login controller.

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
}
}

Checking if ViewController is already in background or not

I have an app which starts with different VCs depending whether the user is already logged in or not.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window = UIWindow.init(frame: UIScreen.mainScreen().bounds)
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let initialViewController: UIViewController
if DataManager.getInstance().getUserInfo() == nil {
initialViewController = storyboard.instantiateViewControllerWithIdentifier("authenticationViewController")
} else {
initialViewController = storyboard.instantiateViewControllerWithIdentifier("locationsNavigationViewController")
}
window!.rootViewController = initialViewController;
window!.makeKeyAndVisible();
return true
}
If the user is not logged in, the app starts with AuthenticationViewController, otherwise it starts with LocationsNavigationViewController, which is a NavigationViewController
In the latter VC, the is a button for logout. The problem is when the user taps on that button, I don't know if I have to dismiss the LocationsNavigationViewController (because AuthenticationViewController is in background) or if I have to dismiss LocationsNavigationViewController and perform a segue for opening the AuthenticationViewController.
So far, I have just covered the first use case. So in LocationsNavigationViewController I call this function
func showAuthentication() {
dismissViewControllerAnimated(true, completion: nil)
}
But when the app starts with LocationsNavigationViewController dismiss the VC is not enough of course, because the the AuthenticationViewController has never been instantiated.
How can I solve this please?
self.navigationController?.viewControllers
This is an array which will contain all your previous view controller. You can enumerate it and check whether your view controller is exist or not.
If you are using UINavigationController then you can check any UIViewController present or not!
let rootViewController = application.windows[0].rootViewController as! UINavigationController
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if !rootViewController.viewControllers.contains(UIViewController_Class()){
let notificationVC = mainStoryboard.instantiateViewControllerWithIdentifier(constInstance.notificationsIdentifier) as! UIViewController_Class_Name
rootViewController.pushViewController(notificationVC, animated: false)
}
Hope this helps!
Thanks to Sohil's answer, I change the showAuthentication function in this way
func showAuthentication() {
//Since the app can start with different VC, I have to check which is the window root VC
if UIApplication.sharedApplication().windows[0].rootViewController is AuthenticationViewController {
dismissViewControllerAnimated(true, completion: nil)
} else {
performSegueWithIdentifier("authenticationSegue", sender: self)
}
}
And added a segue from the NavigationViewController to the AuthenticationViewController, called authenticationSegue
Pretty simple solution.
You can create one function in appdelegate for logout
and in logout function chanege rootviewcontroller of window like
func logoutUser()
{
var login: UIViewController?
login = LoginViewController(nibName : "LoginViewController", bundle : nil)
let nav = UINavigationController(rootViewController: login!)
self.window?.rootViewController = nav
}

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