Prevent Segue Transition in Login Swift/Parse - ios

I have a login view controller where it should prevent the user from transition to the next view controller via a segue called "toMasterTab". I think the logic might be wrong - if the user entered the correct credentials and is not empty, it transitions fine, but if the user entered no credentials (nil) and entered the wrong credentials, then it should prevent the segue. So far, I can only get the UIAlertView to pop up, but other than that, I can't solve this...
#IBAction func loginButton(sender: AnyObject) {
let RedPlanetUser = RPUsername.text
let RedPlanetUserPassword = RPUserPassword.text
PFUser.logInWithUsernameInBackground(RedPlanetUser!, password: RedPlanetUserPassword!) {
(user: PFUser?, error: NSError?) -> Void in
if user != nil {
// Do stuff after successful login
self.performSegueWithIdentifier("toMasterTab", sender: self)
print("User logged in successfully")
} else {
// Login failed
print("User log in failed")
// Present alert
var alert = UIAlertView(title: "Login Failed",
message: "The username and password do not match.",
delegate: self,
cancelButtonTitle: "Try Again")
alert.show()
func shouldPerformSegueWithIdentifier(identifier: String!, object: AnyObject) -> Bool {
let identifier = "toMasterTab"
// prevent segue
return false
}
}
}
}

I believe you should be overriding the
override func shouldPerformSegueWithIdentifier
The problem was that the segue was connected to the button, so it automatically performed the segue even when the conditions were NOT met. I connected the segue from VC1 to VC2 and used the following code when the conditions were met, and didn't call the segue when the conditions were erroneous:
self.performSegueWithIdentifier("toMasterTab", sender: self)

Related

How do I stop a segue from going through programmatically in Swift?

Here is the code:
#IBAction func loginTapped(_ sender: Any) {
let email = emailTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let password = passwordTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
Auth.auth().signIn(withEmail: email, password: password) { (result, error) in
if error != nil {
self.errorLabel.text = error!.localizedDescription
self.errorLabel.alpha = 1
print(error!.localizedDescription)
}
else {
self.performSegue(withIdentifier: "loginSegue", sender: nil)
print("User is signed in with Firebase.")
}
}
}
I have a segue, loginSegue, connected from the login button to the homeViewController. Within in the if error statement I would like to stop the segue from going through because the user has not signed in. The goal here is not allow the user to go forward if they get an error. Is there an "opposite" to the performSegue(withIdentifier: String, sender: Any?) ?
First , there is no "opposite" to performSegue(withIdentifier: String, sender: Any?).
But the issue is not about this. I think you wired the segue from the login button and gave it an identifier. If you wire a segue from a button directly the button is always going to execute that segue. Doing some operations in the button's action does not effect.
You need to wire a segue from FirstVc to SecondVc (not from the button) and then give the segue an identifier. Then, from the button's action you can check if there is no error and call performSegue(withIdentifier: String, sender:) passing your segue's identifier.
I think your button is connected to perform segue in storyboard. So your button has two actions - one from storyboard to perform segue and second in your code. Just remove the connection from storyboard and connect only UIViewControllers not with your button.
You could override the shouldPerformSegue(withIdentifier:,sender:) method and return false if the login fails and you don't want to perform the segue. Here's an example
override func shouldPerformSegue(withIdentifier identifier: String?, sender: Any?) -> Bool {
if let ident = identifier {
if ident == "YourIdentifier" {
if loginSuccess != true {
return false
}
}
}
return true
}

Disable "Save Password" action sheet when exiting UIViewController?

My app has a "Create Account" view controller (shown below) that prompts the user to enter a username and password. Whenever I segue to another view controller, I get a pop-up action sheet prompting to save the password in the keychain.
This is a nifty little freebie IF the user successfully creates the new account. But I get this same pop-up if the user hits the cancel (back) button in the navigation bar, if they select the option to use Facebook login instead of creating an account, or any other means for leaving this view controller (see figures below).
How can I get this popup to ONLY show up when the user successfully creates a new account?
EDIT: Per request, here is the code that is related to the segues that result in the appearance of the "Save Password" action sheet.
from CreateAccountViewController.swift:
class CreateAccountViewController : UIViewController
{
// ... bunch of irrelevant code deleted ...
// bound to "Connect with Facebook" button (see image below)
#IBAction func switchToFacebook(_ sender : UIButton)
{
performSegue(.SwitchToFacebookLogin, sender: sender)
}
// ... bunch of irrelevant code deleted ...
}
extension CreateAccountViewController : GameServerAlertObserver
{
// callback based on response from GameCenter after
// submitting a "create new user" request
func handleConnectionResponse(_ response:GameServerResponse )
{
switch response
{
// ... other response cases removed ...
case .UserCreated:
self.removeSpinner()
performSegue(.CreateAccountToStartup, sender: self)
default:
response.displayAlert(over: self, observer: self)
self.removeSpinner()
}
}
// Functions defined in the GameServerAlertObserver protocol
// to handle user response to "User Exists Popup" (figure below)
func ok()
{
// user chose to enter new password... clear the existing username field
usernameTextField.text = ""
}
func cancel()
{
// segue back to the startup view controller
performSegue(.CreateAccountToStartup, sender: self)
}
func goToLogin()
{
// segue to the login view controller
performSegue(.SwitchToAccountLogin, sender:self)
}
}
from UIViewController_Segues:
enum SegueIdentifier : String
{
case LoserBoard = "loserBoard"
case CreateAccount = "createAccount"
case AccountLogin = "accountLogin"
case FacebookLogin = "facebookLogin"
case SwitchToFacebookLogin = "switchToFacebookLogin"
case SwitchToAccountLogin = "switchToAccountLogin"
case CreateAccountToStartup = "createAccountToStartup"
case AccountLoginToStartup = "accountLoginToStartup"
case FacebookLoginToStartup = "facebookLoginToStartup"
case UnwindToStartup = "unwindToStartup"
}
extension UIViewController
{
func performSegue(_ target:SegueIdentifier, sender:Any?)
{
performSegue(withIdentifier: target.rawValue, sender: sender)
}
}
from GameServerAlert.swift:
protocol GameServerAlertObserver
{
func ok()
func cancel()
func goToLogin()
}
extension GameServerResponse
{
func displayAlert(over controller:UIViewController, observer:GameServerAlertObserver? = nil)
{
var title : String
var message : String
var actions : [UIAlertAction]
switch self
{
// ... deleted cases/default which don't lead to segue ...
case .UserAlreadyExists:
title = "User already exists"
message = "\nIf this is you, please use the login page to reconnect.\n\nIf this is not you, you will need to select a different username."
actions = [
UIAlertAction(title: "Go to Login page", style: .default, handler: { _ in observer?.goToLogin() } ),
UIAlertAction(title: "Enter new username", style: .default, handler: { _ in observer?.ok() } ),
UIAlertAction(title: "Cancel", style: .cancel, handler: { _ in observer?.cancel() } )
]
}
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
actions.forEach { (action) in alert.addAction(action) }
controller.present(alert,animated:true)
}
}
Examples from the simulator:
Create Account - (user enters username and password for new account here.)
Facebook Login
If user decides to use Facebook to log in rather than creating a user account, they are taken to this view (which I still haven't fleshed out). Note that the "Save Password" action sheet has popped up.
User Exists Popup
If user attempts to create an account with a username that already exists, they will be presented with this popup. If they select Cancel, they are taken back to the startup screen (see below). If they select Enter new username, they are kept on the same screen with the username cleared out. If they select Login, they are taken to the login screen.
Startup Screen
If the user selects Cancel above, they are brought back here. Again, note that the "Save Password" action sheet has popped up.
What I do to avoid the automatic Password saving action sheet when the user :
dismiss the login view controller ;
pop the view controller ;
use interactive pop gesture.
=>
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
passwordTextField.textContentType = .password
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if isMovingFromParent || isBeingDismissed || parent?.isBeingDismissed == true {
passwordTextField.textContentType = nil
}
}
Sorry about the short answer, I don't usually post on this site. This is the password Autofill that is happening on your device when the create user screen is dismissed.
Apple Documentation: https://developer.apple.com/documentation/security/password_autofill
Here is a link to a site that goes over all the requirements very well: https://developerinsider.co/ios12-password-autofill-automatic-strong-password-and-security-code-autofill/
Add a condition before running the code block which shows the action sheet. You can do this simply with an if statement. This statement must check if the account has been successfully created or not. Code block which shows action sheet must run only when the condition is true.

Swift - password protected view controller

I'm trying to make a password protected view controller.
so far -
Created storyboard -
on viewcontroller - created hard coded log in -
prints to console if successful or not.
textfields etc...
#IBOutlet weak var untext: UITextField!
#IBOutlet weak var pwtext: UITextField!
let username = "admin"
let password = "adminpw"
override func viewDidLoad() {
super.viewDidLoad()
pwtext.isSecureTextEntry = true
}
#IBAction func loginbtn(_ sender: Any) {
if untext.text == username && pwtext.text == password
{
print("log in succesful")
} else {
print("log in failed")
}
}
The issue I have, once I press the login button, it takes me to the admin page if successful or not.
How can I print a notification - on screen - if unsuccessful and remain on the current view controller, and if successful, take me to admin view controller?
You can either use a segue or instantiateViewController. But in this example I'll use instantiateViewController (Images). (But commented how to use a segue)
Add a class and an identifier to your secondary ViewController
Choose between my Segue or Instantiate. (Check my comments in the code)
If login is succeeded, either perform the segue or navigate using instantiate.
Happy coding. :D
But first off, let's take a look at the code you provided.
#IBAction func loginbtn(_ sender: Any)
{
if untext.text == username && pwtext.text == password
{
print("login succeeded")
//1. using instantiateViewController
if let storyboard = storyboard
{
//Check my image below how to set Identifier etc.
// withIdentifier = Storyboard ID & "ViewController" = Class
let vc = storyboard.instantiateViewController(withIdentifier: "ViewController") as! ViewController
self.present(vc, animated: false, completion: nil)
}
//2. Use segue (I'll wrap this with a comment incase you copy)
//self.performSegue(withIdentifier: "SegueID", sender: self)
}
else
{
//Setting up an "AlertController"
let alert = UIAlertController(title: "Login failed", message: "Wrong username / password", preferredStyle: UIAlertController.Style.alert)
//Adding a button to close the alert with title "Try again"
alert.addAction(UIAlertAction(title: "Try again", style: UIAlertAction.Style.default, handler: nil))
//Presentating the Alert
self.present(alert, animated: true, completion: nil)
}
}
Click on the yellow dot on your ViewController (On the ViewController where you want the login-page to take you)
Click on the icon like I've. (Which is blue) and set a Class + Storyboard ID.
NOTE! IF you wanna use a segue, make SURE you have a connection between ViewController(Login) and ViewController1
Assuming you use segues for navigation, you can put a "general purpose" segue (drag from your controller, instead of any controls in it) and assign it an ID (Identifier in attribute inspector of the segue in Storyboard). After that you can conditionally invoke segue from the parent controller class with your code:
if passwordCorrect {
performSegue(withIdentifier: "SegueID", sender: nil)
}

ShouldPerformSegue Fires Twice

I have currently come across a weird problem. I have a storyboard which has viewController A with a button. From that button, I have created a segue in storyboard to viewController B. When the button is clicked, a segue is fired.
The button is a Login button, so I need to validate the login details before the segue is performed.
In ViewController A, when the button is pressed, I have following code:
#IBAction func SignInButtonPressed(_ sender: Any) {
guard let email = username.text , username.text != "" else {
return self.successLogin = false
}
guard let pass = password.text , password.text != "" else {
return self.successLogin = false
}
AuthService.instance.loginUser(email: email, password: pass) { (success) in
if success {
self.successLogin = true
} else {
self.displayAlertView(title: USER_LOGIN_FAILED_TITLE, message: USER_LOGIN_FAILED_MESSAGE)
}
}
}
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if successLogin != true && identifier == "homeSegue" {
self.displayAlertView(title: USER_LOGIN_FAILED_TITLE, message: USER_LOGIN_FAILED_MESSAGE)
return false
} else {
return true
}
}
But with this, the first click shows the alert in shouldPerformSegue, and then a second click allows the login.
Any suggestions would be helpful on how can I fix this.
Thanks
You should perform the segue inside your AuthService success closure callback, you should also remove the unwind segue from the button, the button must only execute the login logic, if the login went well then the segue must be performed but not before
AuthService.instance.loginUser(email: email, password: pass) { (success) in
if success {
self.successLogin = true
self.performSegue(withIdentifier: "homeSegue", sender: self)
} else {
self.displayAlertView(title: USER_LOGIN_FAILED_TITLE, message: USER_LOGIN_FAILED_MESSAGE)
}
}
should work now
You can run segue programmatically.
performSegue(withIdentifier: "homeSegue", sender: self)
If you need to perform custom logic, moreover postpone navigation, it is better to bind button to the action, and trigger segue you need from it.

Facebook Sign In Segue Loads and Then Returns to Sign In Screen

So I have created a Sign In / Sign Up function using Parse and Swift.
After successful Facebook login the view controller I want to see pops up briefly but then the SignIn view controller comes back onto the screen. I want the user to be logged in and have access to the App. Why is this?
Below is my code for both view controllers. I have a storyboard with segues setup as shown below.
fbSignIn - Facebook Sign In Segue
goSignIn - Sign In Page Segue
Main View Controller (The View Controller I want to go to after Facebook Login)
class MainViewController: UIViewController {
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(true)
if PFUser.currentUser() != nil {
} else {
// take user to SignInViewController through a custom segue
self.performSegueWithIdentifier("goSignIn", sender: self)
}
}
#IBAction func logOutUser(sender: UIButton) {
PFUser.logOut()
self.performSegueWithIdentifier("goSignIn", sender: self)
}
Sign In View Controller (Sign In and Facebook Sign In Button)
#IBAction func didTapFacebookConnect(sender: AnyObject) {
let permissions = [ "public_profile", "email", "user_friends" ]
PFFacebookUtils.logInInBackgroundWithReadPermissions(permissions) {
(user: PFUser?, error: NSError?) -> Void in
if let user = user {
if user.isNew {
print ("User signed up and logged in through Facebook!")
self.performSegueWithIdentifier("fbSignIn", sender: self)
} else {
print ("User logged in through Facebook!")
self.performSegueWithIdentifier("fbSignIn", sender: self)
}
} else {
print ("Uh oh. The user cancelled the Facebook login.")
self.performSegueWithIdentifier("fbSignIn", sender: self)
}
You Segue to your Facebook login page on a viewDidAppear method in your main viewcontroller. The behaviour you describe suggests that PFUser.currentUser() is nil so self.performSegueWithIdentifier("goSignIn", sender: self) is executed in viewDidAppear when your main viewcontroller appears and you immediately end up back in the sign in page.

Resources