In AWS iOS SDK, how do I handle FORCE_CHANGE_PASSWORD User Status - ios

I have followed the sample here
https://github.com/awslabs/aws-sdk-ios-samples/tree/master/CognitoYourUserPools-Sample
To integrate interactive cognito login to my iOS app. This is all working well, but when a new user is created in the pool, they initially have a FORCE_CHANGE_PASSWORD status.
For android you can follow the procedure below
http://docs.aws.amazon.com/cognito/latest/developerguide/using-amazon-cognito-user-identity-pools-android-sdk-authenticate-admin-created-user.html
But for iOS I can't find out how to do this. Using the sample, if I attempt to login with a user in FORCE_CHANGE_PASSWORD status, I see the following output in the console logs (with some values removed for brevity):
{"ChallengeName":"NEW_PASSWORD_REQUIRED","ChallengeParameters":{"requiredAttributes":"[]","userAttributes":"{\"email_verified\":\"true\",\"custom:autoconfirm\":\"Y\","Session":"xyz"}
The following is the code from the SignInViewController from the sample detailed above.
import Foundation
import AWSCognitoIdentityProvider
class SignInViewController: UIViewController {
#IBOutlet weak var username: UITextField!
#IBOutlet weak var password: UITextField!
var passwordAuthenticationCompletion: AWSTaskCompletionSource<AWSCognitoIdentityPasswordAuthenticationDetails>?
var usernameText: String?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.password.text = nil
self.username.text = usernameText
self.navigationController?.setNavigationBarHidden(true, animated: false)
}
#IBAction func signInPressed(_ sender: AnyObject) {
if (self.username.text != nil && self.password.text != nil) {
let authDetails = AWSCognitoIdentityPasswordAuthenticationDetails(username: self.username.text!, password: self.password.text! )
self.passwordAuthenticationCompletion?.set(result: authDetails)
} else {
let alertController = UIAlertController(title: "Missing information",
message: "Please enter a valid user name and password",
preferredStyle: .alert)
let retryAction = UIAlertAction(title: "Retry", style: .default, handler: nil)
alertController.addAction(retryAction)
}
}
}
extension SignInViewController: AWSCognitoIdentityPasswordAuthentication {
public func getDetails(_ authenticationInput: AWSCognitoIdentityPasswordAuthenticationInput, passwordAuthenticationCompletionSource: AWSTaskCompletionSource<AWSCognitoIdentityPasswordAuthenticationDetails>) {
self.passwordAuthenticationCompletion = passwordAuthenticationCompletionSource
DispatchQueue.main.async {
if (self.usernameText == nil) {
self.usernameText = authenticationInput.lastKnownUsername
}
}
}
public func didCompleteStepWithError(_ error: Error?) {
DispatchQueue.main.async {
if let error = error as? NSError {
let alertController = UIAlertController(title: error.userInfo["__type"] as? String,
message: error.userInfo["message"] as? String,
preferredStyle: .alert)
let retryAction = UIAlertAction(title: "Retry", style: .default, handler: nil)
alertController.addAction(retryAction)
self.present(alertController, animated: true, completion: nil)
} else {
self.username.text = nil
self.dismiss(animated: true, completion: nil)
}
}
}
}
When didCompleteStepWithError executes, error is null where I would expect it to indicate something to tell us that the user is required to change password.
My question is really how to catch the "ChallengeName":"NEW_PASSWORD_REQUIRED" json that is output to the console?

Sorted this now. Posting it here in case it helps anyone else.
First, you need to create a view controller that will allow the user to specify the new password. The ViewController should have a AWSTaskCompletionSource<AWSCognitoIdentityNewPasswordRequiredDetails> as a property:
var newPasswordCompletion: AWSTaskCompletionSource<AWSCognitoIdentityNewPasswordRequiredDetails>?
Following the pattern in the sample referred to in the question, I then implemented AWSCognitoIdentityNewPasswordRequired as an extension to the view controller as follows:
extension NewPasswordRequiredViewController: AWSCognitoIdentityNewPasswordRequired {
func getNewPasswordDetails(_ newPasswordRequiredInput: AWSCognitoIdentityNewPasswordRequiredInput, newPasswordRequiredCompletionSource:
AWSTaskCompletionSource<AWSCognitoIdentityNewPasswordRequiredDetails>) {
self.newPasswordCompletion = newPasswordRequiredCompletionSource
}
func didCompleteNewPasswordStepWithError(_ error: Error?) {
if let error = error as? NSError {
// Handle error
} else {
// Handle success, in my case simply dismiss the view controller
self.dismiss(animated: true, completion: nil)
}
}
}
Again following the design of the sample detailed in the question, add a function to the AWSCognitoIdentityInteractiveAuthenticationDelegate extension to AppDelegate:
extension AppDelegate: AWSCognitoIdentityInteractiveAuthenticationDelegate {
func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication {
//Existing implementation
}
func startNewPasswordRequired() -> AWSCognitoIdentityNewPasswordRequired {
// Your code to handle how the NewPasswordRequiredViewController is created / presented, the view controller should be returned
return self.newPasswordRequiredViewController!
}
}

The perfect example to implement the ResetPassword when u create the user in Cognito Console.
But, Integrating this ResetPassword mechanism to your existing app is your responsibility.
https://github.com/davidtucker/CognitoSampleApplication/tree/article1/CognitoApplication

Related

Google signing is signing in automatically, also right after log out. swift 4

After writing the log in with mail, i'm integrating social logins to my app and starting with Google signin. It now logs in automatically at app start, not if I press the Google login button. If I cancel it at pop up window it throws an Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional valueon the token line in AppDelegate`. Also logging out doesn't prevent to log in automatically again.
It was quite confusing following instruction from Firebase manual for Google login so I sure made some obvious mistake.
Here's the code so far:
AppDelegate:
// start google sign in methods
#available(iOS 9.0, *)
func application(_ application: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any])
-> Bool {
return GIDSignIn.sharedInstance().handle(url,
sourceApplication:options[UIApplicationOpenURLOptionsKey.sourceApplication] as? String,
annotation: options[UIApplicationOpenURLOptionsKey.annotation])
}
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
// ...
print("User successfully signed in with Google",user)
guard let idToken = user.authentication.idToken else {
return
}
guard let accessToken = user.authentication.accessToken else {return}
let credentials = GoogleAuthProvider.credential(withIDToken: idToken, accessToken: accessToken)
Auth.auth().signInAndRetrieveData(with: credentials) { (user, error) in
if let error = error {
print("Failed to create user with Google account", error)
return
}
print("Succesfully created new user in Firebase with Google account")
}
if let error = error {
// ...
print("User failed to sign in with Google", error)
return
}
guard let authentication = user.authentication else { return }
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
accessToken: authentication.accessToken)
// let storyboard = UIStoryboard(name: "Main", bundle: nil)
// var mainVC = self.window?.visibleViewController as? MainNavigationController
// mainVC = storyboard.instantiateViewController(withIdentifier: "MainNavigationController") as? MainNavigationController
}
func sign(_ signIn: GIDSignIn!, didDisconnectWith user: GIDGoogleUser!, withError error: Error!) {
// Perform any operations when the user disconnects from app here.
// ...
}
// end of google sign in
The Google signin should perform a segue to main menu but it doesn't.
Only at first sign in it gets to the desired vc.
Here's the Login class:
import UIKit
import Firebase
import GoogleSignIn
class LoginViewController: UIViewController, GIDSignInUIDelegate {
// outlets
#IBOutlet weak var backGroundImage: UIImageView!
#IBOutlet weak var userNameTextField: UITextField!
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var confirmPasswordTextField: UITextField!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if Auth.auth().currentUser != nil {
self.performSegue(withIdentifier: "skipSegue", sender: nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
setGoogleButton()
setFacebookButton()
}
// dismiss keyboard on touch outside textfields
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for txt in self.view.subviews {
if txt.isKind(of: UITextField.self) && txt.isFirstResponder {
txt.resignFirstResponder()
}
}
}
private func setGoogleButton() {
GIDSignIn.sharedInstance().uiDelegate = self
GIDSignIn.sharedInstance().signIn()
}
private func setFacebookButton() {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Actions
#IBAction func newUserRegisterButton(_ sender: Any) {
if passwordTextField.text != confirmPasswordTextField.text{
let alertController = UIAlertController(title: "Password Incorrect", message: "Please re-type password", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertController.addAction(defaultAction)
self.present(alertController, animated: true, completion: nil)
}
else{
Auth.auth().createUser(withEmail: emailTextField.text!, password: passwordTextField.text!){ (user, error) in
if error == nil {
self.performSegue(withIdentifier: "skipSegue", sender: self)
}
else{
let alertController = UIAlertController(title: "Error", message: error?.localizedDescription, preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertController.addAction(defaultAction)
self.present(alertController, animated: true, completion: nil)
}
}
}
}
#IBAction func mailLogin(_ sender: UIButton) {
Auth.auth().signIn(withEmail: emailTextField.text!, password: passwordTextField.text!) { (user, error) in
if error == nil{
self.performSegue(withIdentifier: "skipSegue", sender: self)
}
else{
let alertController = UIAlertController(title: "Error", message: error?.localizedDescription, preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertController.addAction(defaultAction)
self.present(alertController, animated: true, completion: nil)
}
}
}
#IBAction func facebookLogin(_ sender: UIButton) {
}
#IBOutlet weak var signInButton: GIDSignInButton!
#IBAction func googleSignInButton(_ sender: GIDSignInButton) {
performSegue(withIdentifier: "skipSegue", sender: self)
}
#IBAction func logoutButton(_ sender: UIButton) {
}
#IBAction func skipButton(_ sender: UIButton) {
performSegue(withIdentifier: "skipSegue", sender: self)
}
}
The sign out:
#IBAction func logOutButton(_ sender: UIButton) {
// firebase auth sign out
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
} catch let signOutError as NSError {
print ("Error signing out: %#", signOutError)
}
// GSI log out
GIDSignIn.sharedInstance().signOut()
print("User successfully logged out Firebase with Google account")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initial = storyboard.instantiateInitialViewController()
UIApplication.shared.keyWindow?.rootViewController = initial
}
func signOutOverride() {
do {
GIDSignIn.sharedInstance().signOut()
try GIDSignIn.sharedInstance()?.disconnect()
// Set the view to the login screen after signing out
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initial = storyboard.instantiateInitialViewController()
UIApplication.shared.keyWindow?.rootViewController = initial
// let storyboard = UIStoryboard(name: "SignIn", bundle: nil)
// let loginVC = storyboard.instantiateViewControllerWithIdentifier("SignInVC") as! SignInViewController
// let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
// appDelegate.window?.rootViewController = loginVC
} catch let signOutError as NSError {
print ("Error signing out: \(signOutError)")
}
}
I read many posts about revoking the tokens and disconnect the user from the app, but couldn't implement those solutions as some are in obj-c and others in older swift syntax.
Anyone having same problem as me?
Thank as usual.
After many tries and error,thanks to Google's sarcastically useful documentation, I found out what the problem was. They suggest to put the sign in inside viewDidLoad()' and that obviously get's called every time the VC gets instantiated. I so Moved into the Google sign in button and left the delegate only inviewDidLoad()
It all now works as expected.
override func viewDidLoad() {
super.viewDidLoad()
GIDSignIn.sharedInstance().uiDelegate = self
setFacebookButton()
}
#IBOutlet weak var signInButton: GIDSignInButton!
#IBAction func googleSignInButton(_ sender: GIDSignInButton) {
GIDSignIn.sharedInstance()?.signIn()
performSegue(withIdentifier: "skipSegue", sender: self)
}

Swift - login in VC, allows anyone to enter not verifying username and passwords match or saved

With using firebase as well. My signup is working well and it registers users. But my login was working fine at start but now it allows anyone to go to the next view(login).
Here's my code:
SignInVC..
import UIKit
import Firebase
class SignInVC: UIViewController {
#IBOutlet weak var emailTF: UITextField!
#IBOutlet weak var passwordTF: UITextField!
#IBAction func onSignInTapped(_ sender: Any) {
guard let email = emailTF.text,
email != "",
let password = passwordTF.text,
password != ""
else {
AlertController.showAlert(self, title: "Missing Information", message: "Please fill in username and password")
return
}
Auth.auth().signIn(withEmail: email, password: password) { (user, error) in
guard error == nil
else {
AlertController.showAlert(self, title: "Oops!", message: error!.localizedDescription)
return
}
guard let user = user else {return}
print(user.email ?? "Missing email address")
print(user.displayName ?? "Missing name")
print(user.uid)
self.performSegue(withIdentifier: "signInSegue", sender: nil)
}
}
//remove keyboard
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
}
AlerControllerVC
import UIKit
import Firebase
class AlertController {
static func showAlert(_ inViewController: UIViewController, title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
let action = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(action)
inViewController.present(alert, animated: true, completion: nil)
}
}
I tried many was and rewrote it again but wont work correct again, when executed it says:
".SignInVC: 0x7fd99c208280> whose view is not in the window hierarchy!"
Please check if your segue is from button to ViewController or from ViewController to ViewController. if it is from button to VC then it does not depend upon the code you wrote

Sending data (label and date picker) from my app to the Reminders app

I've created an appointment reminder section in my app but it seems the first use of the app doesn't get stored? When I click the create reminder button I get my popup alert saying it was successfully created followed by the would like to access your reminders popup. Because of this people's first appointments aren't being stored, and I can't figure out what I have done wrong?
Here is my code:
import UIKit
import EventKit
class FirstViewController: UIViewController {
#IBOutlet weak var reminderText: UITextField!
#IBOutlet weak var myDatePicker: UIDatePicker!
let appDelegate = UIApplication.shared.delegate
as! AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func setReminder(_ sender: AnyObject) {
if reminderText.text == "" {
// Create the alert controller
let alertController = UIAlertController(title: "Information Needed", message: "Please type in your treatment and select the correct date and time you wish to be reminded about before pressing the Create Appointment Reminder button.", preferredStyle: .alert)
// Create the actions
let okAction = UIAlertAction(title: "Got It", style: UIAlertActionStyle.default) {
UIAlertAction in
NSLog("OK Pressed")
}
// Add the actions
alertController.addAction(okAction)
// Present the controller
self.present(alertController, animated: true, completion: nil)
} else {
if appDelegate.eventStore == nil {
appDelegate.eventStore = EKEventStore()
appDelegate.eventStore?.requestAccess(
to: EKEntityType.reminder, completion: {(granted, error) in
if !granted {
print("Access to store not granted")
print(error!.localizedDescription)
} else {
print("Access granted")
}
})
}
if (appDelegate.eventStore != nil) {
self.createReminder()
}
}
self.reminderText.resignFirstResponder()
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func createReminder() {
let reminder = EKReminder(eventStore: appDelegate.eventStore!)
reminder.title = reminderText.text! + " " + "(Pose Beauty Salon)"
reminder.calendar =
appDelegate.eventStore!.defaultCalendarForNewReminders()
let date = myDatePicker.date
let alarm = EKAlarm(absoluteDate: date)
reminder.addAlarm(alarm)
do {
try appDelegate.eventStore?.save(reminder,
commit: true)
} catch let error {
print("Reminder failed with error \(error.localizedDescription)")
}
// Create the alert controller
let alertController = UIAlertController(title: "Reminder Created Successfully", message: "Your \(reminderText.text!) appointment reminder at Pose Beauty Salon has been successfully created in your iPhone Reminders app. Thank You! ", preferredStyle: .alert)
// Create the actions
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default) {
UIAlertAction in
NSLog("OK Pressed")
}
// Add the actions
alertController.addAction(okAction)
// Present the controller
self.present(alertController, animated: true, completion: nil)
reminderText.text = ""
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
reminderText.endEditing(true)
}
}
Your logic is much more complicated than it needs to be. You can take advantage of the fact that when you request access to the event store after access has already been granted (or denied) then those permissions are used without prompting the user.
It is also poor form to initialise a property of the AppDelegate from some other class. You probably don't even need the EventStore on the AppDelegate - you can just create an instance when you need it.
You can use something like:
} else {
let eventStore = EKEventStore()
eventStore.requestAccess(
to: EKEntityType.reminder, completion: {(granted, error) in
if !granted {
print("Access to store not granted")
print(error!.localizedDescription)
} else {
print("Access granted")
self.createReminder(in: eventStore)
}
})
}
func createReminder(in eventStore: EKEventStore) {
....
}

UIAlertController Not Within The Window Hierarchy

First of all please do not mark duplicate on this question Ive done my research on this topic, and not only have the most recent answers been from over a year ago, but they have also been in C#. Mine also differs from those because I am trying to present my UIView from what I assume to be a child of a child view. But I'm not 100% sure about this. So, here is what my code dump looks like after the suggestions.
import UIKit
import Firebase
class LoginViewController: UIViewController, UITextFieldDelegate{
#IBOutlet weak var usernameTxt: UITextField!
#IBOutlet weak var emailTxt: UITextField!
#IBOutlet weak var passwordTxt: UITextField!
#IBOutlet weak var confirmPassTxt: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func nextScreen(sender: UIButton) {
if(emailTxt.text == "" || passwordTxt.text == "" || confirmPassTxt.text == "" || usernameTxt.text == ""){
let alertController = UIAlertController(title: "Wait!", message: "You didn't fill out the required fields, please do so and try again. ", preferredStyle: .Alert)
let defaultAction = UIKit.UIAlertAction(title: "OK", style: .Default, handler: nil)
alertController.addAction(defaultAction)
self.presentViewController(alertController, animated: true, completion: nil)
}else{
if(validateEmail(emailTxt.text!)){
emailCheck(emailTxt.text!){isValid in if isValid{self.secondRound({ (goodMail, goodPassL, goodPassTxt, nameGood) in
if (goodMail && goodPassL && goodPassTxt && !nameGood){
print("good")
}else{
self.showAlert("Else", description: "Got it?") }
})}else{let alertController=UIAlertController(title: "Whoops!", message: "That email address has already been taken, please try another one", preferredStyle: .Alert)
let defaultAction = UIKit.UIAlertAction(title: "OK", style: .Default, handler: nil)
alertController.addAction(defaultAction)
alertController.parentViewController
self.presentViewController(alertController, animated: true, completion: nil)
}}
}else{
let alertController = UIAlertController(title: "Whoops!", message: "That doesnt appear to be a valid email address, please check your information and try again!", preferredStyle: .Alert)
let defaultAction = UIKit.UIAlertAction(title: "OK", style: .Default, handler: nil)
alertController.addAction(defaultAction)
alertController.parentViewController
presentViewController(alertController, animated: true, completion: nil)
}
}
}
func showAlert(title: String, description: String){
let alertController: UIAlertController = UIAlertController(title: title, message: description, preferredStyle: .Alert)
let defaultAction = UIKit.UIAlertAction(title: "OK", style: .Default, handler: nil)
alertController.addAction(defaultAction)
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alertController, animated: true, completion: nil)
self.presentViewController(alertController, animated: true, completion: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let DestinationVC : Login2VC = segue.destinationViewController as! Login2VC
DestinationVC.prepareEmail = emailTxt.text!
DestinationVC.preparePass = passwordTxt.text!
}
func validateEmail(canidate: String) -> Bool {
let emailRegex = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}"
return NSPredicate(format: "SELF MATCHES %#", emailRegex).evaluateWithObject(canidate)
}
func nameFilter(input : String)-> Bool{
var profanity : Bool = true
let dataRef = FIRDatabase.database().reference()
dataRef.child("Profanity").observeSingleEventOfType(.Value) { (snap: FIRDataSnapshot) in
if(snap.exists()){
if(snap.value! as! NSArray).containsObject(input){
print("our ears!")
profanity = true
}else{
profanity = false
}
}
}
return profanity
}
func emailCheck(input: String, callback: (isValid: Bool) -> Void) {
FIRAuth.auth()?.signInWithEmail(input, password: " ") { (user, error) in
var canRegister = false
if error != nil {
if (error?.code == 17009) {
canRegister = false
} else if(error?.code == 17011) {
//email doesn't exist
canRegister = true
}
}
callback(isValid: canRegister)
}
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func secondRound(callback:(goodMail:Bool, goodPassL:Bool, goodPassTxt:Bool, nameGood:Bool)->Void){
let availableEmail : Bool = true
var passwordMatch : Bool = false
var passwordLength : Bool = false
var profanity : Bool = false
if(passwordTxt.text!==confirmPassTxt.text!){passwordMatch=true}else{passwordMatch=false}
if(passwordTxt.text!.characters.count>=6&&confirmPassTxt.text!.characters.count>=6){passwordLength=true}else{passwordLength=false}
if(nameFilter(usernameTxt.text!)){profanity=true}else{profanity=false}
callback(goodMail: availableEmail, goodPassL: passwordLength, goodPassTxt: passwordMatch, nameGood: profanity)
}
}
Essentially, what I am trying to do is:
Check to see if the inputted text is formatted as an email correctly
Check to see if the name is available
Check if the username contains profanity (pulled as a json from firebase)
Check to see if the passwords match
Check too see if the passwords are at least 6 characters in length
Each false result would have its own UIAlertView that results from it, but whenever I try to add these views they do not show up, and the app returns with this error.
Note, the false event does intact appear in the first condition only. Other than that, nothing happens.
This is the error I have been getting, and while it seems pretty straight forward I cannot figure out hoe to fix it, nor find any recent information online on how to accomplish this task.
> Warning: Attempt to present <UIAlertController: 0x7fb513fb8bc0> on
> <myApp.LoginViewController: 0x7fb513c6e0a0> whose view is not in the
> window hierarchy!
The logical answer to this would be:
Hey, why don't you just add the window to the hierarchy and be done?
Well, to that question I have a question, if the window is not apart of the hierarchy, then why is the view itself even displaying. Furthermore, why would the first set of UIAlerts be displaying, but as soon as I get into a nested if they cease? Any ideas on how to fix this terrible error?
Thanks all

iOS 8 Swift 1.2 and Parse - Attempt to present UIAlertController on ViewController whose view is not in the window hierarchy

There are many questions on Stack concerning this issue, but none of them seem to solve the issue I'm having.
I am using ParseUI for the login and signup portion of my application. What I would like to have happen is for a UIAlertController to be presented when a user (for example) does not enter in any text in the username and password fields.
Here is the code for my MasterViewController:
class MasterViewController: UIViewController, PFLogInViewControllerDelegate,
PFSignUpViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if (PFUser.currentUser() == nil) {
var logInViewController = LoginViewController()
logInViewController.delegate = self
var signUpViewController = SignUpViewController()
signUpViewController.delegate = self
logInViewController.signUpController = signUpViewController
self.presentViewController(logInViewController, animated: true, completion: nil)
}
}
func logInViewController(logInController: PFLogInViewController,
shouldBeginLogInWithUsername username: String, password: String) -> Bool {
if (!username.isEmpty || !password.isEmpty) {
return true
} else {
let alertController = UIAlertController(title: "Failed to login.",
message: "Login Failure. Please try again.", preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
alertController.addAction(defaultAction)
self.presentViewController(alertController, animated: true, completion: nil)
return false
}
}
func logInViewController(logInController: PFLogInViewController,
didFailToLogInWithError error: NSError?) {
println("Failed to log in.")
}
func signUpViewController(signUpController: PFSignUpViewController,
shouldBeginSignUp info: [NSObject : AnyObject]) -> Bool {
if let password = info["password"] as? String {
return count(password.utf16) >= 8
} else {
return false
}
}
func signUpViewController(signUpController: PFSignUpViewController,
didFailToSignUpWithError error: NSError?) {
println("Failed to sign up.")
}
func logInViewController(logInController: PFLogInViewController,
didLogInUser user: PFUser) {
let installation = PFInstallation.currentInstallation()
installation["user"] = PFUser.currentUser()
installation.saveInBackground()
self.dismissViewControllerAnimated(true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func signUpViewControllerDidCancelSignUp(signUpController:
PFSignUpViewController) {
println("User dismissed signup.")
}
}
After reading another user's answer which seemed like it would be the answer, I added the following class to my workspace:
import Foundation
class AlertHelper: NSObject {
func showAlert(fromController controller: MasterViewController) {
var alert = UIAlertController(title: "abc", message: "def", preferredStyle: .Alert)
controller.presentViewController(alert, animated: true, completion: nil)
}
}
And then modified my method accordingly:
func logInViewController(logInController: PFLogInViewController,
shouldBeginLogInWithUsername username: String, password: String) -> Bool {
if (!username.isEmpty || !password.isEmpty) {
return true
} else {
let alertController = UIAlertController(title: "Failed to login.",
message: "Login Failure. Please try again.", preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
alertController.addAction(defaultAction)
var alert = AlertHelper()
alert.showAlert(fromController: self)
return false
}
}
At this point I'm not quite sure what else to do. One thought I had was to programmatically add a UILabel to my LoginViewController and SignUpViewController and change the content based on the errors (or lack thereof) for the user login and signup, but I would like to have alerts.
EDIT: This is the code in my LoginViewController. It subclassed in order to customize the appearance. The code for my SignUpViewController is almost identical.
import UIKit
import Parse
import ParseUI
class LoginViewController: PFLogInViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = ckPurple
let label = UILabel()
label.textColor = UIColor.whiteColor()
label.text = "Welcome."
label.sizeToFit()
logInView?.logo = label
// Do any additional setup after loading the view.
}
The problem is that you present a login view controller from your master view controller, which removes the master view controller from the view hierarchy. You then attempt to present an alert view controller from your master view controller, but you need to present from a view controller in the view hierarchy. Try presenting the alert from your login view controller instead.
loginController.presentViewController(alertController, animated: true, completion: nil)

Resources