I have a registration process where I have an entry point with a Login/register with Facebook (Connected to Parse). If the user has never registered with their Facebook account, then they are sent to a send page where a user registers a username, email and password. I have a function setup that if a user leaves any of the text fields blank for the user registration, then a alert message appears with an error stating the field is blank. This functionality works correctly, but when I click "OK" to dismiss the message, the registration view controller dismisses itself and the entry point (login screen) view controller is displayed. This should not be happening and I don't have a segue setup to go from registration screen to login screen. Any thoughts?
One thing that pops out to me is the error in the console log, which I believe is actually associated with the Parse if/else statement, and not with the field == nil statement.
Console Log:
2015-04-14 10:42:56.293 tappery[574:142525] [Error]: missing username (Code: 200, Version: 1.6.3)
Login Screen View Controller:
import UIKit
class LoginViewController: UIViewController {
#IBOutlet var loginCancelledLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var currentUser = PFUser.currentUser()
if currentUser != nil {
println("User is Logged in")
} else {
println("User is not logged in")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func facebookLoginButton(sender: AnyObject) {
var permissions = ["public_profile", "email", "user_friends"]
self.loginCancelledLabel.alpha = 0
PFFacebookUtils.logInWithPermissions(permissions, {
(user: PFUser!, error: NSError!) -> Void in
if let user = user {
if user.isNew {
println("User signed up and logged in through Facebook!")
self.performSegueWithIdentifier("registerUser", sender: self)
} else {
println("User logged in through Facebook!")
self.performSegueWithIdentifier("loginSuccessful", sender: self)
}
} else {
println("Uh oh. The user cancelled the Facebook login.")
self.loginCancelledLabel.alpha = 1
}
})
}
}
Registration View Controller:
import UIKit
class UserRegistrationViewController: UIViewController {
func displayAlert(title:String, error:String) {
var alert = UIAlertController(title: title, message: error, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: {
action in
self.dismissViewControllerAnimated(true, completion: nil)
}))
self.presentViewController(alert, animated: true, completion: nil)
}
#IBOutlet var usernameTextField: UITextField!
#IBOutlet var emailTextField: UITextField!
#IBOutlet var passwordTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func registerUser(sender: AnyObject) {
var error = ""
if usernameTextField.text == nil || emailTextField.text == nil || passwordTextField.text == nil {
error = "Please enter a username, email and password"
}
if error != "" {
displayAlert("Error In Form", error: error)
} else {
var user = PFUser.currentUser()
user.username = usernameTextField.text
user.password = passwordTextField.text
user.email = emailTextField.text
user.saveInBackgroundWithBlock {
(succeeded: Bool!, signupError: NSError!) -> Void in
if signupError == nil {
println(user.username)
println(user.password)
println(user.email)
self.performSegueWithIdentifier("successfulRegistration", sender: self)
// Hooray! Let them use the app now.
} else {
if let errorString = signupError.userInfo?["error"] as? NSString {
error = errorString
} else {
error = "Please try again later."
}
self.displayAlert("Could Not Sign Up", error: error)
}
}
}
}
Remove self.dismissViewControllerAnimated(true, completion: nil) from your OK button UIAlertAction's handler. Alert is dismissed automatically upon OK button click and you're dismissing registration controller with this call.
Related
When I create a new user it does everything it should do and saves the users detail and goes back to the login page waiting for the email to be verified before allowing it to be used. The coding works so it doesn't allowing you to proceed until email has been verified but I've realised when I slide the app to close it and then reopen it (before verifying the email), it goes straight to the homepage bypassing the login page even if the email hasn't been verified?
import UIKit
import Firebase
import SwiftKeychainWrapper
class ViewController: UIViewController {
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var passwordField: UITextField!
var userUid: String!
override func viewDidLoad(){
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidAppear(_ animated: Bool) {
func Keychain() {
KeychainWrapper.standard.set(userUid, forKey: "uid")
}
if let _ = KeychainWrapper.standard.string(forKey: "uid"){
LoggedIn()
}
}
func goToCreateUserVC() {
performSegue(withIdentifier: "CreateAProfile", sender: nil)
}
func LoggedIn() {
performSegue(withIdentifier: "LoginSuccessful", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "CreateAProfile" {
if let destination = segue.destination as? CreatUsers {
if userUid != nil {
destination.userUid = userUid
}
if emailField.text != nil {
destination.emailField = emailField.text
}
if passwordField.text != nil {
destination.passwordField = passwordField.text
}
}
}
}
func DisplayAlertMessage(MessageToDisplay: String) {
let alertController = UIAlertController(title: "Alert", message: MessageToDisplay, preferredStyle: .alert)
let OKAction = UIAlertAction(title: "OK", style: .default) { (action:UIAlertAction!) in
// Code in this block will trigger when OK button tapped.
print("Ok button tapped");
}
alertController.addAction(OKAction)
self.present(alertController, animated: true, completion:nil)
}
#IBAction func signIntapped(_ sender: Any) {
if let email = emailField.text, let password = passwordField.text {
Auth.auth().signIn(withEmail: email, password: password, completion:
{(user,error) in
if let user = Auth.auth().currentUser {
if user.isEmailVerified {
self.userUid = user.uid
print("Email Verified")
self.LoggedIn()
} else {
self.DisplayAlertMessage(MessageToDisplay: "Need To Verify Email Address")
}
} else {
self.DisplayAlertMessage(MessageToDisplay: "Incorrect Username/Password")
}
});
}
}
#IBAction func NotaMemberisTapped(_ sender: Any) {
self.goToCreateUserVC()
}
}
Only happens when I close the app and reopen it to find it cheats its way through - trying to figure out how to prevent it from happening.
You’re calling your LoggedIn() function based on whether or not there is a value in the keychain. I’m assuming you’re saving that information to the keychain whether or not validation has been performed.
Is there a better way to make a user automatically login to the app as long as they have logged in in the past; other than saving their login details directly to the storage which may be insecure?
Thanks
PS: I'm using Firebase and Swift for iOS
EDIT: Here's the code
import UIKit
import Firebase
class loginVC: UIViewController {
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var errorLabel: UILabel!
override func viewDidAppear(animated: Bool) {
automaticLogin()
}
#IBAction func loginTapped(sender: AnyObject) {
errorLabel.text = ""
FIRAuth.auth()?.createUserWithEmail(emailTextField.text!, password: passwordTextField.text!, completion: {
user, error in
if error != nil {
FIRAuth.auth()?.signInWithEmail(self.emailTextField.text!, password: self.passwordTextField.text!, completion: { (user, error) in
if error == nil {
self.login()
} else if error != nil {
self.errorLabel.text = ("Invalid email address or password")
print(error)
}
})
} else {
print("user created")
self.login()
print(user?.displayName)
}
})
}
#IBAction func forgotPasswordTapped(sender: AnyObject) {
let email = emailTextField.text
errorLabel.text = ""
FIRAuth.auth()?.sendPasswordResetWithEmail(email!) { error in
if error != nil {
self.errorLabel.text = ("Invalid email address")
print(error)
} else {
self.errorLabel.text = ("Password reset email successfully sent")
print("password reset email sent")
}
}
}
func login() {
FIRAuth.auth()?.signInWithEmail(emailTextField.text!, password: passwordTextField.text!, completion: {
user, error in
if error != nil {
self.errorLabel.text = ("Invalid email address or password")
print(error)
} else {
print("login successful")
self.checkIfUserIsNew()
}
})
}
func checkIfUserIsNew() {
if FIRAuth.auth()?.currentUser?.displayName != nil || FIRAuth.auth()?.currentUser?.displayName == "" {
self.performSegueWithIdentifier("showChatVC", sender: self)
} else {
self.performSegueWithIdentifier("showOptionsVC", sender: self)
}
}
func automaticLogin() {
FIRAuth.auth()?.addAuthStateDidChangeListener { auth, user in
if user == user {
print("still signed in")
} else {
print("not signed in")
}
}
}
I also used this to logout the user:
#IBAction func logOutTapped(sender: AnyObject) {
try! FIRAuth.auth()?.signOut()
performSegueWithIdentifier("showLoginVC", sender: self)
print(FIRAuth.auth()?.currentUser?.displayName)
}
You might want to use NSUserDefaults. To do this in your login() function you might want to save your user's data once they signed in or create an account. Inside your login function you can start by creating new variable to grab your user's email / password.
let userDefaults = UserDefaults.standard
userDefaults.setValue(self.emailField.text!, forKey: "email")
userDefaults.setValue(self.passField.text!, forKey: "password")
Next, you also need to implement a new function called viewWillAppear, which will be called before the view controller. And you might also put some arguments inside your login function.
override func viewWillAppear(_ animated: Bool) {
let userDefaults = UserDefaults.standard
if userDefaults.string(forKey: "email") != nil {
let email = userDefaults.string(forKey: "email")
let password = userDefaults.string(forKey: "password")
login(email: email!, password: password!)
}
}
the if statement will check wether your user already registered their email account or not yet. Lastly you need to update your login function to have two parameters, to pass in the email and password of the user.
Been smashing my face against the wall all day trying to upgrade my app to the Firebase 3.x code.
I was having a ton of trouble with updating my original userAuth code and decided to just start from scratch. I haven't really been able to test it though because when I run the app it is calling the segue immediately upon loading the initial VC. Obviously I don't want it to do this and I don't know what is causing it.
I've tried deleting the app from the simulator and when I load it back up I get the same result.
Here is my code for the VC:
import UIKit
import FirebaseAuth
class SignInViewController: UIViewController {
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var passwordField: UITextField!
override func viewDidAppear(animated: Bool) {
if let user = FIRAuth.auth()?.currentUser {
self.signedIn(user)
}
}
#IBAction func didTapSignIn(sender: AnyObject) {
// Sign In with credentials.
let email = emailField.text
let password = passwordField.text
FIRAuth.auth()?.signInWithEmail(email!, password: password!) { (user, error) in
if let error = error {
print(error.localizedDescription)
return
}
self.signedIn(user!)
}
}
#IBAction func didTapSignUp(sender: AnyObject) {
let email = emailField.text
let password = passwordField.text
FIRAuth.auth()?.createUserWithEmail(email!, password: password!) { (user, error) in
if let error = error {
print(error.localizedDescription)
return
}
self.setDisplayName(user!)
}
}
func setDisplayName(user: FIRUser) {
let changeRequest = user.profileChangeRequest()
changeRequest.displayName = user.email!.componentsSeparatedByString("#")[0]
changeRequest.commitChangesWithCompletion(){ (error) in
if let error = error {
print(error.localizedDescription)
return
}
self.signedIn(FIRAuth.auth()?.currentUser)
}
}
#IBAction func didRequestPasswordReset(sender: AnyObject) {
let prompt = UIAlertController.init(title: nil, message: "Email:", preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction.init(title: "OK", style: UIAlertActionStyle.Default) { (action) in
let userInput = prompt.textFields![0].text
if (userInput!.isEmpty) {
return
}
FIRAuth.auth()?.sendPasswordResetWithEmail(userInput!) { (error) in
if let error = error {
print(error.localizedDescription)
return
}
}
}
prompt.addTextFieldWithConfigurationHandler(nil)
prompt.addAction(okAction)
presentViewController(prompt, animated: true, completion: nil);
}
func signedIn(user: FIRUser?) {
MeasurementHelper.sendLoginEvent()
AppState.sharedInstance.displayName = user?.displayName ?? user?.email
AppState.sharedInstance.photoUrl = user?.photoURL
AppState.sharedInstance.signedIn = true
NSNotificationCenter.defaultCenter().postNotificationName(Constants.NotificationKeys.SignedIn, object: nil, userInfo: nil)
performSegueWithIdentifier(Constants.Segues.SignInToFp, sender: nil)
}
}
Can someone please help? Thank you in advance.
After the user logs in SUCCESSFULLY, I need to switch view controllers to the timeline section of my app, this is also going to be a tab bar view controller with 5 different tabs at the bottom. Here is the code I have so far, it works and is connected to the parse database I have setup.
import UIKit
import Parse
class LoginViewController: UIViewController {
#IBOutlet var usernameField: UITextField!
#IBOutlet var passwordField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func loginTapped(sender: AnyObject) {
let username = usernameField.text
let password = passwordField.text
PFUser.logInWithUsernameInBackground(username, password:password) {
(user: PFUser?, error: NSError?) -> Void in
if user != nil {
println("Success")
} else {
var loginError:UIAlertView = UIAlertView(title: "Invalid Login", message: "I did not recognize your credentials. Try again?", delegate: self, cancelButtonTitle: "Dismiss")
loginError.show()
}
}
}
#IBAction func closeTapped(sender: AnyObject) {
dismissViewControllerAnimated(true, completion: nil)
}
}
In the line println("sucessful"), instead of printing out the sucessful login I need to switch to their timeline home (the tab bar view controller).
Use a segue to transition to new view controller if the login was successful:
if user != nil {
self.performSegueWithIdentifier("successfulLoginPage", sender: self)
}
I am using Xcode, Swift, and Parse. When I try and logout a PFUser, i never get a return of nil.
In this part of the app, the viewController is simply showing a few buttons one logs in. One sends the user to signup. One sends the user to change details, and one is a simple logout.
The code for the two that matter on logout is;
#IBAction func logout(sender: AnyObject) {
PFUser.logOut()
var currentUser = PFUser.currentUser()
self.displayAlert("You are now logged out", error: "")
println(currentUser!)
}
#IBAction func changeDetails(sender: AnyObject) {
var currentUser = PFUser.currentUser()
println(currentUser!)
if currentUser != nil {
let nextView30 = self.storyboard?.instantiateViewControllerWithIdentifier("changeDetails") as! changeUserDetailsViewController
self.navigationController?.pushViewController(nextView30, animated: true)
} else {
self.displayAlert("Please log in", error: "")
}
}
Once the code runs and I logout, wherever the currentUser gets read I get the following type of response, not nil. The next ViewController is actioned, and this shouldn't happen without a usr logged in.
PFUser: 0x37018fbc0, objectId: new, localId: local_3b5eb7453f9af5ed {
}
Am I doing something wrong or is this standard?
If it is correct, how do I return no user logged in?
Thanks
if PFUser.currentUser()!.username != nil
{
self.performSegueWithIdentifier("loginSegue", sender: self)
}
The above code worked for the login issue. But i still have the logout issue. After I call PFUser.logout(), PFUser.currentUser() is not becoming nil. Any help?
Thanks
I've been struggling with logging out for a little while and I believe I have finally cracked it!
No matter what I did, when I used "PFUser.logOut()" would never set "PFUser.currentUser()" to nil, but it would set "PFUser.currentUser()!.username" to nil...
Because of this I used
var currentUser = PFUser.currentUser()!.username
as a global variable to track is a user is logged in.
On my login/first page I added
override func viewDidAppear(animated: Bool) {
if currentUser != nil {
self.performSegueWithIdentifier("login", sender: self)
}
}
and finally on my logout button i used
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "logout" {
PFUser.logOut() //Log user out
currentUser = PFUser.currentUser()!.username //Reset currentUser variable to nil
}
}
I hope that helps!
Try commenting out the following line of code in your AppDelegate.swift file -
PFUser.enableAutomaticUser()
enableAutomaticUser() will log in an anonymous user once you call PFUser.logOut(), and the username for an anonymous user is nil.
override func viewDidLoad(animated: Bool) {
var currentUser = PFUser.currentUser()?.username
if(currentUser != nil){
var loginAlert: UIAlertController = UIAlertController(title: "Signup/ Login", message:"Please Signup or Login" , preferredStyle: UIAlertControllerStyle.Alert)
loginAlert.addTextFieldWithConfigurationHandler({
textfield in
textfield.placeholder = "Your Username"
})
loginAlert.addTextFieldWithConfigurationHandler({
textfield in
textfield.placeholder = "Your Password"
textfield.secureTextEntry = true
})
loginAlert.addAction(UIAlertAction(title: "Login", style: UIAlertActionStyle.Default, handler: {alertAction in
let textFields: NSArray = loginAlert.textFields! as NSArray
let usernameTextFields: UITextField = textFields.objectAtIndex(0) as! UITextField
let passwordTextFields: UITextField = textFields.objectAtIndex(1) as! UITextField
PFUser.logInWithUsernameInBackground(usernameTextFields.text as String!, password: passwordTextFields.text as String!){
(loggedInuser: PFUser?, signupError: NSError?) -> Void in
if((loggedInuser) != nil){
println("User logged in successfully")
}else{
println("User login failed")
}
}
}))
loginAlert.addAction(UIAlertAction(title: "SignUp", style: UIAlertActionStyle.Default, handler: {alertAction in
let textFields: NSArray = loginAlert.textFields! as NSArray
let usernameTextFields: UITextField = textFields.objectAtIndex(0) as! UITextField
let passwordTextFields: UITextField = textFields.objectAtIndex(1) as! UITextField
var sweeter : PFUser = PFUser()
sweeter.username = usernameTextFields.text
sweeter.password = passwordTextFields.text
sweeter.signUpInBackgroundWithBlock {(sucess,error) -> Void in
if !(error != nil){
println("sign up success")
}else
{
println("constant error string\(error)")
}
}
}))
self.presentViewController(loginAlert, animated: true, completion: nil)
}
}