show() View Controller doesn't work - ios

I try to authenticate the user of my app if they are not. I first read the value saved in UserDefaults to know if they are logged in, and if not, I want to show the ViewController to allow them to log in.
But anyway, the LogInViewController won't show, this is my code :
import UIKit
import MapKit
class MainViewController: UIViewController, CLLocationManagerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if(!UserDefaults.standard.bool(forKey: "userIsConnected")){
let LogVC = self.storyboard?.instantiateViewController(withIdentifier: "LogInViewController") as! LogInViewController
show(LogVC, sender: self)
}
}
I also tried without the 'if' condition, and it doesn't work anymore
EDIT :
I also tried presentVC() and I had the error "Attempt to present on whose view is not in the window hierarchy"
Thanks for help

You are trying to login in the MainViewController. You should avoid when possible managing your login in a view controller. The login is more effective if managed in your AppDelegate.
Add this code to didFinishLaunchingWithOptions in AppDelegate.swift
if !UserDefaults.standard.bool(forKey: "userIsConnected") {
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let loginViewController = storyboard.instantiateViewController(withIdentifier: "LogInViewController")
window?.makeKeyAndVisible()
window?.rootViewController?.present(loginViewController, animated: true, completion: nil
}
Make sure you can access UserDefaults.

Related

How to unwind through progrmmatically set rootVC (tabViewController) from child tab to different VC that was not instantiated

Swift 5 / xCode 11.5
When the app begins, App Delegate will check for a saved auth token on the users phone. If it is not present it will display what is the InitialViewController (instantiating it, setting it as rootViewController, and displaying it in the Window). If token is present it will instantiate and set the MainTabController as the rootVC and display it. The hierarchy looks like this:
RegisterViewController <-> (No nav controllers between) InitialViewController <->
LoginViewController All above controllers push to MainTabController if successful login or registration, assuming auth token for user is not present on phone (no nav controllers between any of these 'views', just pushing/dismissing.).MainTabController has three tabs that are UIViewControllers connected through NavControllers.
Since the rootVC is instantiated and displayed through Window object programmatically in the AppDelegate, the InitialViewController is not available to unwind to from a tab on the MainTabController (which was set as the rootVC), assuming the user does have a saved auth token.
My work around was deleting the auth token then calling a function in AppDelegate that would switch the rootViewController and have the Window display it (back to InitialViewController). I worry about all the other VC's that were instantiated though and the memory pile up this may cause. Are all the MainTabController's children de-allocated when I do this? Here is code for logout action and switching rootVC:
logout
#IBAction func logout(_ sender: Any) {
UserServices.logoutUser() {
responseResult in
if responseResult == .Success {
CommonUtils.clearAuthToken()
CommonUtils.clearAllUserDefaultData()
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.switchRootViewController()
} else {
self.showAlert(title: "Error", message: responseResult.rawValue)
}
}
}
switchRootViewController in AppDelegate
func switchRootViewController() -> Void {
let rootController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "InitialViewController")
self.window?.rootViewController = rootController
}
What I've tried with the unwind segue already (inside logout action)
let initialController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "InitialViewController")
initialController.addChild(self) // self is tab VC I'm trying to unwind from
let children = initialController.children
self.performSegue(withIdentifier: "unwindToHomeScreen", sender: self)
The InitialViewControllers unwind segue code
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
return true
}
override func canPerformUnwindSegueAction(_ action: Selector, from fromViewController: UIViewController, sender: Any?) -> Bool {
return true
}
#IBAction func unwindToInitialViewController(sender: UIStoryboardSegue) {}
I don't think this unwind is working because no reference exists for the InitialViewController. I'd display a picture of the layout but the project is secret in nature until product launch... Please let me know if you need more info or I was not clear in my description of the issue.
If you try this:
func ToSomeScreen() {
let homeViewController = storyboard?.instantiateViewController(identifier: "Storyboard ID") as? ScreenCustummerViewController //file name for next screen
view.window?.rootViewController = homeViewController
view.window?.makeKeyAndVisible()
}

Why does pushViewController doesn't work after navigating through Dynamic Link?

I'm building an app that can receive Firebase's Dynamic Link and redirects it into a certain UIViewController after clicking the link. So based on this question that I asked, I have the code in AppDelegate.swift that navigates the app to the UIViewController like this:
func goToDynamicLinkVC() { // This function is in AppDelegate.swift
let destinationStoryboard: UIStoryboard = UIStoryboard(name: "DynamicLink", bundle: nil)
let destinationVC = destinationStoryboard.instantiateViewController(withIdentifier: "DynamicLinkView") as? DynamicLinkVC
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = destinationVC
self.window?.makeKeyAndVisible()
}
And in that UIViewController I also have a function that moves to another UIViewController if a user pressed a button like this:
func routeToDynamicForm() { // This function is in DynamicLinkVC class
let destinationVC = UIStoryboard(name: "DynamicLink", bundle: nil).instantiateViewController(withIdentifier: "DynamicFormView") as! DynamicFormVC
self.navigationController?.pushViewController(destinationVC, animated: true)
}
But weirdly enough now that I pressed the button it doesn't move me to the next View Controller (in this case, DynamicFormVC). I've tried to debug the button whether it's working or not by doing this:
print("button tapped")
It shows the message on the debug area, but it still doesn't redirects to the next view controller. So what can I do to resolve this? If you need more information feel free to ask and I will provide it to you. Thank you.
I changed the goToDynamicLinkVC function to something like this:
func goToDynamicLinkVC() { // This function is in AppDelegate.swift
let destinationStoryboard: UIStoryboard = UIStoryboard(name: "DynamicLink", bundle: nil)
let destinationVC = destinationStoryboard.instantiateViewController(withIdentifier: "DynamicLinkView")
let navController = UINavigationController()
navController.setViewControllers([destinationVC], animated: true)
self.window?.rootViewController = navController
}

passing data from 2 view controllers without segue

I have a mainviewcontroller and a popup view controller which opens without a segue.
the popup viewcontroller recive data from Firebase, and then i need to append this data to an array in the mainviewcontroller.
How can i do that?
(i tried to create a property of the popupviewcontroller in the mainviewcontroller, but in crashes the app)
this is the code that opens the popup:
#IBAction func showPopUp(_ sender: Any) {
let popOverVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "sbPopUp") as! PopUpViewController
self.addChild(popOverVC)
popOverVC.view.frame = self.view.frame
self.view.addSubview(popOverVC.view)
popOverVC.didMove(toParent: self)
You need to connect the classes so that the popup class knows what to do with the data once it has been received. Here's a sample structure that works in a playground that you should be able to apply to your real classes.
class MainClass {
func showPopUp() {
let popOverVC = PopUpClass()
popOverVC.update = addToArray
popOverVC.receivedData()
}
func addToArray() {
print("Adding")
}
}
class PopUpClass {
var update: (()->())?
func receivedData() {
if let updateFunction = update {
updateFunction()
}
}
}
let main = MainClass()
main.showPopUp()
Or you could create a global variable so it can be accessed anywhere. ... It is better to pass data but I just thought it would be easier in this instance, so the variable can be accessed anywhere in your entire project.
if it is just a notification, you can show it in an alert, but if you don't want to use an alert my offer to present another view controller is not like this , try my code :
//if you have navigation controller ->
let vc = self.storyboard?.instantiateViewController(
withIdentifier: "storyboadrID") as! yourViewController
self.navigationController?.pushViewController(vc, animated: true)
//if you don't use navigation controller ->
let VC1 = self.storyboard!.instantiateViewController(withIdentifier: "storyboadrID") as! yourViewController
self.present(VC1, animated:true, completion: nil)
also if you want to pass any data or parameter to destination view controller you can put it like this before presenting your view controller :
VC1.textView.text = "test"

iOS Swift Navigate to certain ViewController programmatically from push notification

I use push notifications to inform the user about different types of events happening in the app. A certain type of push notification should open a special viewcontroller (f.e a notification about a new chat message does navigate to the chat on click)
To achieve this, i tried the following code inside my app delegate:
func applicationDidBecomeActive(_ application: UIApplication) {
if var topController = UIApplication.shared.keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
topController.tabBarController?.selectedIndex = 3
}
}
It does not move anywhere. What am I missing here?
I wrote a simple class to navigate to any view controller in the view hierarchy from anywhere in one line of code by just passing the class type, so the code you'll write will be also decoupled from the view hierarchy itself, for instance:
Navigator.find(MyViewController.self)?.doSomethingSync()
Navigator.navigate(to: MyViewController.self)?.doSomethingSync()
..or you can execute methods asynchronously on the main thread also:
Navigator.navigate(to: MyViewController.self) { (MyViewControllerContainer, MyViewControllerInstance) in
MyViewControllerInstance?.doSomethingAsync()
}
Here's the GitHub project link: https://github.com/oblq/Navigator
This is what you need.
Use your navigation controller to push your notification view controller
let mainStoryBoard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let navigationController : UINavigationController = mainStoryBoard.instantiateViewController(withIdentifier: "MainNavigationController") as! UINavigationController
let notifcontrol : UIViewController = (mainStoryBoard.instantiateViewController(withIdentifier: "NotificationViewController") as? NotificationViewController)!
navigationController.pushViewController(notifcontrol, animated: true)
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
The topController was already my TabBarController in this case!

Swift opening a new view controller programmatically

I'm working on an workout app. I finished everything now I'm stuck at the details.
So when my workout finishes it needs to open a new view controller which will tell the user that he finished the workout.
I have tried to do it with this code:
var storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
//var vc: UINavigationController = storyboard.instantiateViewControllerWithIdentifier("newViewController") as! UINavigationController
var vc: EndOfWorkout = storyboard.instantiateViewControllerWithIdentifier("newView") as! EndOfWorkout
self.presentViewController(vc, animated: true, completion: nil)
But it opens sometimes and sometimes not. Also when it opens it closes after a short period of time.
I need also at the end that the user is able to go back to the main menu but none of the examples that I tried are working.
Your vc variable might be destroyed by ARC, try setting this instance in a property of your controller.
Something like that :
class MainViewController: UIViewController{
private var vc: EndOfWorkout?
override func viewDidLoad() {
...
vc = storyboard.instantiateViewControllerWithIdentifier("newView") as! EndOfWorkout
}
}

Resources