PerformSegue in StoryBoard - iphone

I am working for the first time on storyboard using Swift.
In the first page of the app user will login and when login is done successfully, user will navigate to next page.
But When button is clicked firstly navigation occurs then after web service gets called, but what I want is to authenticate the Login Web service first then navigate with login data to next page.
I have put the identifier on the login button i.e, "login_success" on the button in storyboard and called self.performSegueWithIdentifier, when login is successfull.
Please guide me. Thanks.

You can add segue in the identifier in storyboard. Set the segue in the storyboard from File Owner. And when we get the response from the server, then using,
dispatch_async(dispatch_get_main_queue(), {
if self.loginInfo.userEmail.length > 0{
self.performSegueWithIdentifier("login_success", sender: self)
}
})
Here, I am checking whether response string has value or not.
Then call the method of segue to navigate to next page.
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!)
{
if segue.identifier == "login_success"
{
var mainVC: MainViewController!
mainVC = segue.destinationViewController as MainViewController
mainVC.mainLoginInfo = loginInfo
}
}
MainViewController is the page to which I want to move after login is done successfully.

Related

Application crashing without information about crash when transitioning to another view controller

I have two view controllers, LoginViewController and TermsAndPrivacyViewController.
On first view controller, there is a button that has IBAction, which opens TermsAndPrivacyViewController. On TermsAndPrivacyViewController I have web view, that loads url I am passing from LoginViewController. So, this is the code(Login view controller):
#IBAction func tosAction(_ sender: Any) {
if let vc = UIStoryboard(name: "Login", bundle: nil).instantiateViewController(withIdentifier: kTOSViewControllerIdentifier) as? TermsAndPrivacyViewController{
vc.url = URL(string: kTOSUrl)
self.navigationController?.pushViewController(vc, animated: true)
}
}
On TermsAndPrivacyViewController, I have this:
override func viewDidLoad() {
super.viewDidLoad()
webView.navigationDelegate = self
if let `url` = url {
webView.load(URLRequest(url: url))
webView.allowsBackForwardNavigationGestures = true
}
}
So, when I hit a button, app crashes SIGABRT message, and nothing else. Also, I don't get any crash report on Firebase Crashlytics (I have detached an app from a debugger first, and produced a crash).
Now, the strange part :/ ... If I put a breakpoint on a first line of tosAction() method, and go step by step (or just let it continue), everything works normally. No crash???
Am I missing something obvious here? What is the reason of crashing? Also, I tried to put Exception Breakpoint but nothing changes. I only get this:
and a console:
so, kinda no information about crash at all.
EDIT
Ok, now the even more strange part :) I just tried app on different phones.
App crashes on iPhone 6s+, iOS 12.1(16B5059d), but it works normally on iPhone 6, iOS 12.0 (16A366)
maybe you use library or framework that not supported by those device.
you must see the error
did you try this?
when the app crashing, in the console press cmd+F and search exception.
now you can see your error
hope to helpful.
Maybe you can use segue methods inside your LoginViewController.
#IBAction func tosAction(_ sender: Any) {
performSegue(withIdentifier: "GoToWeb", sender: nil)
}
}
And call prepareForSegue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "GoToWeb") {
let vc = segue.destination as! TermsAndPrivacyViewController
vc.url = "YOUR_URL"
}
And the code inside TermsAndPrivacyViewController don't change
Edit:
Maybe your not binding well your UIButton, can you verify in the right panel, in the section "Show the connection inspector" if your button is call only once.
To get further information on this type of crashes, open the Breakpoints menu (or press CMD+8), click the plus icon in the bottom left corner and press Exception Breakpoint.... Right click, then edit. Add an action of the type Debugger Command and enter the following:
Reproduce the crash again, this time the console will output a more useful error message.

How to pass data between two screens while both are elements in NavigationController in Swift?

I have a set of storyboards under the UINavigation Controller. One of these screens contains a table view and I need to send the data of the clicked row to the next screen. I am aware that doing so would require me to use segue. But then how do I get the Navigation Bar and the Navigation Button
For better understanding I have uploaded the screen shot of my first screen. Clicking on the detail button is going to send the challenge object to next screen. However, I am looking forward to having the same navigation bar on the top of the next scree. Any idea how can I accomplish such objective?
Currently if I press on details button, I'll get the details of that challenge in the terminal.
#IBAction func CAStartChallenge(_ sender: UIButton) {
let tag = NSNumber(value: sender.tag) as! Int
print("challenge name to be printed : \(challengeTableConent[tag].display()) ")
}
In your storyboard you want to drag a segue between the view controllers, like so:
Storyboard segue dragging
Use either Show or Push (deprecated) options for the segue. Give this segue an appropriate identifier, such as Details.
Then in your button function you can add these:
#IBAction func CAStartChallenge(_ sender: UIButton) {
let tag = NSNumber(value: sender.tag) as! Int
performSegue(withIdentifier: "Details", sender: challengeTableConent[tag])
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Details",
let destination = segue.destination as? DestinationViewController,
let model = sender as? ChallengeTableContentType {
// pass through model to destination view controller
destination.model = model
}
}
You can then configure the destination view controller using its specific model when it appears.
First of all
Passing data through screens using segues is simple. Just check this answer
Pass data through segue
It will explain exactly how to do it
Second
Check the bug on your button I think you mean "Details"
Third
Check the type of segue, you might not used push or show detail. Also select the details view and make sure that "Simulated Metrics -> Topbar" is set to inferred
Some prints to help
This will ensure that the navigation bar comes from the previous controller
This is how your segue should look, do not forget to give an identifier to your segue

is there an easy way to logout when clicking the back nav item on a navigation controller in swift 4?

I've got a login screen that takes an email and password. I'm using firebase Auth.
The login screen is embedded in a navigation controller. From the login screen it goes to a UserDetailsController. There is a "back" nav item in the navbar that comes with the nav controller. I can't actually drag this to be an outlet.
I was wondering if there is an easy way for when the "back" is clicked and the user is returned to the login page to logout the user. The code for logout is relatively simple with firebase Auth. The issue i'm having is working out in LoginController if I returned to here from the UserDetailsController.
I've read up about using self.presentingcontroller to determine which controller i came back from but I keep getting nil. And I wasn't sure if this is the best/only option to determine from which controller I have navigated back from.
Thanks.
Looks pretty straightforward. Hopefully this code will help.
In viewDidLoad add a function like this:
override func viewDidLoad() {
super.viewDidLoad()
setupLogOut()
}
Then set up the function like this with a selector handler method like this:
fileprivate func setupLogOut() {
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Back", style: .plain, target: self, action: #selector(handleLogOut))
}
#objc func handleLogOut() {
do {
try Auth.auth().signOut()
// Present the login controller
let loginController = LoginController()
let navController = UINavigationController(rootViewController: loginController)
self.present(navController, animated: true, completion: nil)
} catch let signOutErr {
print("Failed to sign out:", signOutErr)
}
}
If you don't want to add method to back button. you can also use below method to logout:
override func viewWillDisappear(animated: Bool) {
// Your sign out logic here..
}
Above method get's called when you pop back to login controller.

How i can go away from closure in viewcontroller?

try to find some info about it, but i can't.
So i got this code when i clicking on login.
#IBAction func checkLogin(_ sender: UIButton) {
let email = userEmail.text!
let password = userPassword.text!
if email == "" || password == "" || !email.isValidEMail {
userEmail.shake()
userPassword.shake()
return
}
Auth.auth().signIn(withEmail: email, password: password, completion: {(user, error) in
var collectError = ("","")
if error != nil {
collectError = ("Error", (error?.localizedDescription)!)
self.alertMsg(title: collectError.0, message: collectError.1)
return
}
if !(user?.isEmailVerified)! {
collectError = ("Error", "Your account is not verified! Confirm your account from email!")
return
}
print("OK, User Authentificated: \(Auth.auth().currentUser?.email)")
// DispatchQueue.main.async { }
//return
self.navigationController?.popViewController(animated: true)
//self.dismiss(animated: true, completion: nil)
})
i have first view controller which gives a user a login or registration options. So i choose for example login. try to login and it returns me back to first view controller there i got verification firebase listener if user loggen in it hides first view controller but when i click to logout in profile.storyboard it immeditialy show me first login controller. and when i try to login again again nothing happens why i dont know
i understand that i need use for navigation return back function
self.navigationController?.popViewController(animated: true)
and for buttons if i want hide viewcontrol
self.dismiss(animated: true, completion: nil)
So, when your root controller of the main.storyboard is loaded or will/did appear, you are trying to check the authorization, if user is not authorized, you will present the login controller, right?
In your case you need to have at least two separated navigation stacks (let's call them the main stack and the login stack)
It means that your login view controller should be presented (not pushed) when user is not authorized and dismissed (not popped) after user will be logged successfully or the reg will be canceled.
I have almost the same reg process in my project. I will try to explain it in the example code below
class MainController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
//detecting the firebase, just a custom bool var
if !isAuthorized {
//call the method to present login controller
presentLoginController()
}
}
func presentLoginController(){
let segue = UIStoryboard(name: "Login", bundle: nil)
if let loginNavigationController = segue.instantiateInitialViewController() {
self.present(loginNavigationController, animated: true, completion: nil)
}
}
}
Now you will see the login controller. So this will make a second navigation stack (login stack). This will be a separated navigation, where the root controller is a login navigation controller (initial controller in you Login.storyboard). For any controller in this stack if you will call navigationController?.popToRootViewController it will bring you to the root controller in the login stack. Don't use self.dismiss(animated: true, completion: nil) here, likely it will break your navigation
So when user is logged in or authorization was finished or canceled, you need to show a main controller (the main stack), where you can make that check for isAuthorized again. This is how you can make it:
func navigateToTheRoot(){
//this code will dismiss all modal controllers that was presented
UIApplication.shared.keyWindow?.rootViewController?.dismiss(animated: false, completion: nil)
//accessing to the appDelegate
let appDelegate = UIApplication.shared.delegate as? AppDelegate
//now this will return your app to the main controller
if let navigationController = appDelegate?.window?.rootViewController as? UINavigationController {
navigationController.popToRootViewController(animated: true)
}
}
Call this method in any controller that is presented in separated navigation stack (like to cancel the registration, call this after clicking the "X" button in your login stack).
If your profile controller has a navigation controller as an initial of the Profile.storyboard (third stack - the profile stack), then you need to present it in the same way as I've used for the login controller. In this case, to make the logout, you need to call method navigateToTheRoot() from any controller in the profile stack to show the main controller.
Otherwise, if your profile controller is just a single controller without additional navigation and it was pushed from any other controller from your main stack, then you just need to call this:
self.navigationController?.popToRootViewController(animated: true)
You will see the root controller of your main.stack
Short rules to get to the root in the Main.storyboard from any other controller no matter how many controllers had been presented or pushed before:
if you controller is presented (not in the main stack) and it is in an additional navigation stack, use navigateToTheRoot() method I've described
if your controller was pushed (push segues or navigationController?.pushViewController for programmatically usage) use navigationController?.popToRootViewController
if your controller is presented (is Modal) and it is just single controller without a navigation, just call self.dismiss(animated: true, completion: nil)
Hope it will help you. You can ask me if you need
P.S. In your registration controller (showing when user clicks "Зарегистрироваться") there is a small mistake "Зарегестрироваться". Удачи! ;)

Swift programmatically set segues

I have a piece of parse code that checks to see if a user is logged in:
PFUser.logInWithUsernameInBackground("myname", password:"mypass") {
(user: PFUser!, error: NSError!) -> Void in
if user != nil {
// Do stuff after successful login.
} else {
// The login failed. Check error to see why.
}
}
After the successful login I want to send the user to a new segue. I can't do this in the storyboard because I only want the segue to occur if the conditions are met. Is there anyway to create a segue programmatically?
You can use:
performSegueWithIdentifier("yourSegueIdentifier", sender: nil)
You dont need to "create" a segue programmatically, just connect your ViewControllers and create a Segue. With performSegueWithIdentifier you can call your segue manually if needed.