PARSE user still active after log-out - swift

I'm learning to use Parse Server to manage users and passwords. I have an app that lets the user log-in and store information in the server (age, weight, gender and name). But I'm having a problem, if the user logs out, the app takes them to the login page and, if after that they use a non-existing user or incorrect password, after showing the alert "Incorrect user or password", the app takes them to the main page with the previous user information. I'm not sure what is going on; the code I'm using to log in is this:
#IBAction func signInButton(_ sender: Any) {
let acl = PFACL()
acl.getPublicWriteAccess = true
PFUser().acl = acl
if userTextField.text == "" || passwordTextField.text == "" {
createAlert(title: "Error", message: "Both fields should be filled")
} else {
activityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
activityIndicator.center = self.view.center
activityIndicator.hidesWhenStopped = true
activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
view.addSubview(activityIndicator)
activityIndicator.startAnimating()
UIApplication.shared.beginIgnoringInteractionEvents() // UIApplication.shared() is now UIApplication.shared
PFUser.logInWithUsername(inBackground: userTextField.text!, password: passwordTextField.text!, block: { (user, error) in
self.activityIndicator.stopAnimating()
UIApplication.shared.endIgnoringInteractionEvents() // UIApplication.shared() is now UIApplication.shared
if error != nil {
var displayErrorMessage = "Please try again later."
let error = error as NSError?
if let errorMessage = error?.userInfo["error"] as? String {
displayErrorMessage = errorMessage
}
self.createAlert(title: "Login Error", message: displayErrorMessage)
} else {
print("Logged in")
let usuario = PFUser.current()
let dataUse = self.queryDownload(usuario: usuario!)
for items in dataUse {
let dataMeassure: GlucoseEvents = NSEntityDescription.insertNewObject(forEntityName: "GlucoseEvents", into: DatabaseController.getContext()) as! GlucoseEvents
dataMeassure.allAtributes = items
}
DatabaseController.saveContext()
self.performSegue(withIdentifier: "goToMain", sender: self)
}
})
}
}
Now, this problem only happens after the logout, so I'm guessing the error in that function. The code of the logout is:
#IBAction func logOut(_ sender: Any) {
let refreshAlert = UIAlertController(title: "Confirm", message: "Really LogOut?", preferredStyle: UIAlertControllerStyle.alert)
refreshAlert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action: UIAlertAction!) in
PFUser.logOutInBackground { (error) in
if error != nil {
self.createAlert(title: "Error", message: "Error")
}
}
self.performSegue(withIdentifier: "closedProf", sender: self)
}))
refreshAlert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action: UIAlertAction!) in
print("Handle Cancel Logic here")
}))
present(refreshAlert, animated: true, completion: nil)
}
Is there another way to logout and avoid this problem? Thanks
More info
So I tried to see if the problem was the server not returning a nil user. And added a print statement to the logout. Turns out that it's nil so I don't know why it is being stored (or where) but the LogOut returns a nil user.

Related

How can i fix attempt to present UIAlertController on ViewController whose view is not in the window hierarchy

Im trying to work on alerts as a way to show potential errors while logging in to my app.
I have a couple of alerts in different parts of my code. None of them work. They don't show and print:
Attempt to present on whose view is not in the window hierarchy!
I see a lot of different posts with the same problem, they all get fixed by doing something with the viewDidAppear. I've tried that, but since its not only one alert and since I use a function to call each alert, that help didn't work.
Here is some code:
func alerts(title: String, message: String) {
let alert = UIAlertController(title:"Oops...", message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title:"Ok", style: UIAlertActionStyle.default, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
}
#IBAction func loginPressed(_ sender: Any) {
if emailField.text == "", pwField.text == "" {
alerts(title: "Oops...", message: "Please fill out the fields.")
accessGiven = false
} else {
accessGiven = true
}
// if emailField.text == "", pwField.text != "" || emailField.text != "", pwField.text == "" {
// alerts(title: "Oops...", message: "Please fill out all fields.")
// accessGiven = false
// } else {
// accessGiven = true
// }
Auth.auth().fetchProviders(forEmail: self.emailField.text!, completion: { (stringArray, error) in
if error != nil {
print(error!)
self.alerts(title: "Oops...", message: "Something went wrong, please try again")
self.accessGiven = false
} else {
if stringArray == nil {
print("No active account using this email.")
self.alerts(title: "Oops...", message: "There is no active account using this E-Mail.")
self.accessGiven = false
//self.performSegue(withIdentifier: "segue.SignUp.toTabBar", sender: nil)
} else {
print("There is an active account using this E-mail")
self.accessGiven = true
}
}
})
Auth.auth().signIn(withEmail: emailField.text!, password: pwField.text!) { (user, error) in
if let error = error {
print(error.localizedDescription)
self.alerts(title: "Oops...", message: "Something went wrong, please try again")
self.accessGiven = false
} else {
self.accessGiven = true
}
if let user = user {
}
}
if accessGiven == true {
self.performSegue(withIdentifier: "segue.LogIn.toTabBar", sender: nil)
}
}
If your accessGiven value is true, you are performing segue to a different viewController. And so your current viewController is no longer a part of the window hierarchy. And then you are trying to present alert controllers from the completionBlocks of your fetchProviders and signIn function using self.present(alert, animated: true, completion: nil) where self is your viewController which is no longer a part of window hierarchy. That's why error is being thrown. To make sure, just comment your performSegue and try the same code. Hope it helps.

navigation controller doesn't work

Signup UIViewController design that validates email, password and confirm password.
Signup is embedded in NavigationController
func showAlert(ttl:String,msg:String){
let alert = UIAlertController(title: "\(ttl)", message: "\(msg)", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
#IBAction func signUp(_ sender: UIButton) {
if (email.text != nil && password.text != nil && confrimPassword.text != nil){
if !(isValidEmail(testStr: email.text!)){
showAlert(ttl: "Invaild Email-ID", msg: "Please enter an valid Email-ID")
}
else if !(isValidPassword(testStr: password.text)){
showAlert(ttl: "Invalid Password", msg: "Password must have at least one uppercase,one digit,one lowercase and minimum 8 characters")
}
else if !(passwordMatch(password: password.text!, confirmPassword: confrimPassword.text!)){
showAlert(ttl: "Passwords doesn't Match", msg: "Please re-enter your password")
}
else{
if Connectivity.isConnectedToInternet {
Authentication.Signup(for: email.text!, password: password.text!,finished: { resdata in do{ let res = try JSONSerialization.jsonObject(with: resdata) as! Dictionary<String, AnyObject>
print(res)
DispatchQueue.main.async {
let view: Verification = self.storyboard?.instantiateViewController(withIdentifier: "Verify") as! Verification
view.email = self.email.text!
//doesnot work
self.navigationController?.pushViewController(view, animated: true)
}
}
catch{
print("Error")
}
})
}
else{
showAlert(ttl: "No Internet", msg: "Please check your internet connection")
}
}
}
else{
self.showAlert(ttl: "Enter all Credentials", msg: " ")
}
}
}
Tried using segue also that doesn't suit my requirements
#IBAction func signUp(_ sender: UIButton) {
Authentication.Signup(for: email.text!, password: password.text!,finished: { resdata in do{
let res = try JSONSerialization.jsonObject(with: resdata) as! Dictionary<String, AnyObject>
print(res)
DispatchQueue.main.async {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "Verify") as! Verification
controller.email = self.email.text!
self.present(controller, animated: true, completion: nil)
}
}

How to show enter password and try again alert view

So I want to make a touchid authentication but if the touchid does not match there will be an alert to enter the password and try again
I am using XCode 9.0.0
#IBAction func ac(_ sender: Any) {
let context:LAContext=LAContext()
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil){
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "use your touch id", reply: {(wasCorrect,error) in
if wasCorrect {
print("correct")
}else{
print("incorrect")
}
})
}else{
}
}
The best way (from the users point of view) would be to enable / show a "Re-Authenticate" button if the authentication fails. Then, the user has to actively tap that button to re-run the autentication loop.
If you would automatically start re-authentication instead, the user might get annoyed by autentication dialogs popping up all the time, wich also might look a little weired because sometimes the animations overlap and the screen seems to flicker.
this will work, updated for swift 3 and 4
func authenticationWithTouchID() {
let localAuthenticationContext = LAContext()
localAuthenticationContext.localizedFallbackTitle = "Use Passcode"
var authError: NSError?
let reasonString = "To access the secure data"
if localAuthenticationContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) {
localAuthenticationContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reasonString) { success, evaluateError in
if success {
//TODO: User authenticated successfully, take appropriate action
} else {
//TODO: User did not authenticate successfully, look at error and take appropriate action
guard let error = evaluateError else {
return
}
let message = self.evaluateAuthenticationPolicyMessageForLA(errorCode: error._code)
let alert = UIAlertController(title: "Alert", message: message, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
} else {
guard let error = authError else {
return
}
let message = self.evaluateAuthenticationPolicyMessageForLA(errorCode: error._code)
let alert = UIAlertController(title: "Alert", message: message, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
and methods to get the error messages
func evaluatePolicyFailErrorMessageForLA(errorCode: Int) -> String {
var message = ""
if #available(iOS 11.0, macOS 10.13, *) {
switch errorCode {
case LAError.biometryNotAvailable.rawValue:
message = "Authentication could not start because the device does not support biometric authentication."
case LAError.biometryLockout.rawValue:
message = "Authentication could not continue because the user has been locked out of biometric authentication, due to failing authentication too many times."
case LAError.biometryNotEnrolled.rawValue:
message = "Authentication could not start because the user has not enrolled in biometric authentication."
default:
message = "Did not find error code on LAError object"
}
} else {
switch errorCode {
case LAError.touchIDLockout.rawValue:
message = "Too many failed attempts."
case LAError.touchIDNotAvailable.rawValue:
message = "TouchID is not available on the device"
case LAError.touchIDNotEnrolled.rawValue:
message = "TouchID is not enrolled on the device"
default:
message = "Did not find error code on LAError object"
}
}
return message;
}
func evaluateAuthenticationPolicyMessageForLA(errorCode: Int) -> String {
var message = ""
switch errorCode {
case LAError.authenticationFailed.rawValue:
message = "The user failed to provide valid credentials"
case LAError.appCancel.rawValue:
message = "Authentication was cancelled by application"
case LAError.invalidContext.rawValue:
message = "The context is invalid"
case LAError.notInteractive.rawValue:
message = "Not interactive"
case LAError.passcodeNotSet.rawValue:
message = "Passcode is not set on the device"
case LAError.systemCancel.rawValue:
message = "Authentication was cancelled by the system"
case LAError.userCancel.rawValue:
message = "The user did cancel"
case LAError.userFallback.rawValue:
message = "The user chose to use the fallback"
default:
message = evaluatePolicyFailErrorMessageForLA(errorCode: errorCode)
}
return message
}
Hope this will help:
func authenticateUser() {
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
let reason = "Identify yourself!"
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) {
[unowned self] success, authenticationError in
DispatchQueue.main.async {
if success {
self.runSecretCode()
} else {
let ac = UIAlertController(title: "Authentication failed", message: "Sorry!", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
self.present(ac, animated: true)
}
}
}
} else {
let ac = UIAlertController(title: "Touch ID not available", message: "Your device is not configured for Touch ID.", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
present(ac, animated: true)
}
}
For more understanding: https://www.hackingwithswift.com/example-code/system/how-to-use-touch-id-to-authenticate-users-by-fingerprint
#IBAction func ac(_ sender: Any) {
let context:LAContext=LAContext()
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil){
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "use your touch id", reply: {(wasCorrect,error) in
if wasCorrect {
}else{
let myalert = UIAlertController(title: " Password Title", message: "Message ", preferredStyle: UIAlertControllerStyle.alert)
myalert.addAction(UIAlertAction(title: "Retry", style: .default) { (action:UIAlertAction!) in
print("retry")
})
self.present(myalert, animated: true)
}
})
}else{
// If You Wanna Show Alert
let myalert = UIAlertController(title: "Title", message: "Message ", preferredStyle: UIAlertControllerStyle.alert)
myalert.addAction(UIAlertAction(title: "Retry", style: .default) { (action:UIAlertAction!) in
print("retry")
})
self.present(myalert, animated: true)
}
}

User Login & Register Problems

Trying to set up Login and Register Controllers but I keep getting the same errors and all the solutions online have been no help. The Errors appear on lines: "displayMyAlertMessage" and UserDefaults.
#IBAction func signUpButton(_ sender: Any) {
let userName = userFirstName.text;
let userLast = userLastName.text;
if(userName!.isEmpty || userLast!.isEmpty)
{
displayMYAlertMessage("All Fields are RequireD");
return;
}
// Store data
UserDefaults.standardUserDefaults().setObject("userName", forKey: "userName")
UserDefaults.standardUserDefaults().setObject("userLast", forKey: "userLast")
let userName = UserDefaults.standardUserDefaults().stringForKey("userName")
let userLast = UserDefaults.standardUserDefaults().stringForKey("userLast")
// Display alert message with confirmation.
var myAlert = UIAlertController(title:"Alert", message:"Registration is successful. Thank you!", preferredStyle: UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title:"Ok", style:UIAlertActionStyle.default){ action in
self.dismiss(animated: true, completion:nil);
}
myAlert.addAction(okAction);
self.present(myAlert, animated:true, completion:nil);
}
}

Duplicate email Alert Swift + Parse

I'm trying to have it so when a user creates an account... if their email hasn't been used before an Alert box appears saying "Account created" and if the email is already in created (on Parse) then an alert should appear notifying the user.
I can't seem to get my code to do both..only display one message. What am I doing wrong here?
Thanks!
func createNewUser() {
let newUser = PFUser()
newUser.email = emailSignUp.text
newUser.username = emailSignUp.text
newUser.password = passwordSignUp.text
newUser.signUpInBackgroundWithBlock { ( success: Bool, error: NSError?) -> Void in
if newUser.username != nil {
let alert: UIAlertController = UIAlertController(title: "Account created", message: "Please confirm your email", preferredStyle: .Alert)
let okButton = UIAlertAction(title: "OK", style: .Default) { action -> Void in
}
alert.addAction(okButton)
self.presentViewController(alert, animated: true, completion: nil)
}
else {
let alert: UIAlertController = UIAlertController(title: "Email already registered", message: "Please enter a different email", preferredStyle: .Alert)
let okButton = UIAlertAction(title: "OK", style: .Default, handler: nil)
alert.addAction(okButton)
self.presentViewController(alert, animated: true, completion: nil)
}
}
}
If memory serves the error that's returned if a user already exists is a different string than a generic error. You can try to match the error string and then display an alert if it matches or a different one if it's just an error (like the error string itself).
newUser.signUpInBackgroundWithBlock { ( success: Bool, error: NSError?) -> Void in
if error != nil {
if let errorString = error.userInfo?[“error”] as? String {
if errorString == “username \(emailSignUp.text) already taken” {
// Create alert that address is taken
} else {
// Create other case alert, though it may make sense to just display the error string in an alert box
}
}
} else {
// Do your usual stuff
}