performSegueWithIdentifier causes crash - swift

I'm setting up a sign up/login page using framework PARSE on XCode 6.
When I try to perform a segue (it is spelled correcty), hover, the app crash, even though the segue is inside an if statement.
Here's the code:
import UIKit
import Parse
class ViewController: UIViewController, UINavigationControllerDelegate{
var signUpMode = false
func displayAlert(title:String, message:String){
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
//Outlet and actions
#IBOutlet var username: customTextField!
#IBOutlet var email: customTextField!
#IBOutlet var password: customTextField!
//Need the outlets for changes betweeen signUp and logIn modes!!!
#IBAction func signUp(sender: AnyObject) {
if signUpMode == true {
var user = PFUser()
user.username = username.text
user.password = password.text
user.email = email.text
// other fields can be set just like with PFObject
//user["phone"] = "415-392-0202"
user.signUpInBackgroundWithBlock {
(succeeded: Bool!, error: NSError!) -> Void in
if error == nil {
// Hooray! Let them use the app now.
} else {
println("error")
self.displayAlert("Username already in use", message: "Please use another username")
}
}
}
else {
PFUser.logInWithUsernameInBackground(email.text, password:password.text) {
(user: PFUser!, error: NSError!) -> Void in
if user != nil {
self.displayAlert("You're in", message: "And you'll be successful")
self.performSegueWithIdentifier("goToPost", sender: self)
} else {
self.displayAlert("Wrong username or password", message: "Please try again")
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated: Bool) {
if signUpMode == false {
self.username.hidden = true
self.email.placeholder = "username"
}
}
override func viewDidAppear(animated: Bool) {
if PFUser.currentUser() != nil {
performSegueWithIdentifier("goToPost", sender: self)
}
}
}
The segue is inside the viewWillAppear method.
PFUser().currentUser() stores information about the current logged in user, so it's nil if no user is logged in.
Can you find out why it crashes?
I tried to put the segue inside viewDidLoad, but nothing else, it didn't even crashed.

Try segueing in viewDidAppear: and check if your segue identifier matches the one on your storyboard.

Related

Attempt to present <UIAlertController> on whose view is not in the window hierarchy

I've been trying to present a UIAlertController when user entered wrong password for their account, the UIAlertController is located in one separate file inside a Model group which I extend the UIViewController class to add this alert functionality to it. I also has another file inside my model group namely LogIn which I wrote all the logic behind the login process so that I can call it to my LogInVC. However, I got an error of "Attempt to present on whose view is not in the window hierarchy!" whenever the function get call inside my LogInVC. I'm trying to make my project in MVC and I know what caused this error but I just don't know how to fix it. May anyone tell me how to fix this problem?
Alert
import Foundation
import UIKit
extension UIViewController {
//MARK: - Not Enough Information Alert
func notEnoughInfo(title: String, message: String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let OKAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(OKAction)
self.present(alertController, animated: true, completion: nil)
}
//MARK: - Incorrect Username and Password
func wrongInfo(title: String, message: String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let OKAction = UIAlertAction(title: "Try again", style: .default, handler: nil)
alertController.addAction(OKAction)
self.present(alertController, animated: true, completion: nil)
}
LogIn
import Foundation
import Firebase
class LogIn: UIViewController{
let db = Firestore.firestore()
//MARK: - userValidation()
func userValidation(Username:String, Password:String){
db.collection("users").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
if let snapShotDocument = querySnapshot?.documents {
for doc in snapShotDocument {
let data = doc.data()
if let username = data[C.username] as? String, let password = data[C.password] as? String {
if Username == username, Password == password {
print("Log in Successfully")
}
else {
self.wrongInfo(title: "Incorrect password", message: "Try again please")
}
}
}
}
}
}
}
}
LogInVC
import UIKit
import Firebase
class LogInVC: UIViewController {
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var logInBtn: UIButton!
let db = Firestore.firestore()
let logIn = LogIn()
override func viewDidLoad() {
super.viewDidLoad()
//logInBtn.layer.cornerRadius = logInBtn.frame.height/5
}
#IBAction func logInBtn(_ sender: UIButton) {
if let username = emailTextField.text, let password = passwordTextField.text{
if username.isEmpty || password.isEmpty{
notEnoughInfo(title: "Not enough information", message: "Please fill in all the necessary information.")
}else{
logIn.userValidation(Username: username, Password: password) //here is where problem occured
//move to another viewcontroller
}
}
}
#IBAction func signUpBtn(_ sender: UIButton) {
let push = storyboard?.instantiateViewController(withIdentifier: C.signUpVC) as! SignUpVC
push.modalPresentationStyle = .fullScreen
present(push, animated: true, completion: nil)
}
} //ends of class
You need to first dismiss the current present alert or present controller. currently you are trying to present controller over a controller that's why it shows this error. Don't present . remove this line from self.wrongInfo(title: "Incorrect password", message: "Try again please") from LogIn.
try this and you can comment again if there is anything regarding this.

Swift: How to check if user's phone number is already in Firebase database before creating a new user

I just started working with Swift a few months ago and to help me learn the language better, I am creating a chat application. For the sign-in method, I am using the phone number method. I have the onboarding process already created but I want to implement something that lets me check if the user has already created an account with that phone number. If they have, I want to segue them to the main view controller, skipping the onboarding view controller.
Here is my code for the phone verification view controllers (One is for inputting a phone number and the other is for inputting the code sent to the user's phone):
import UIKit
import Firebase
import FirebaseAuth
import FirebaseDatabase
class PhoneVerification: UIViewController {
//MARK: Properties
#IBOutlet weak var phoneNumber: UITextField!
#IBOutlet weak var code: UITextField!
#IBOutlet weak var verifyCodeImage: UIButton!
#IBOutlet weak var sendCodeImage: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
//MARK: Format phone text field
var phoneFormatter = PhoneNumberFormatter()
#IBAction func formatPhoneNumber(_ sender: UITextField) {
sender.text = phoneFormatter.format(sender.text!, hash: sender.hash)
}
//MARK: When send secret code button is pressed
#IBAction func sendCode(_ sender: Any) {
let submitPhoneNumber = "+1" + phoneNumber.text!
if submitPhoneNumber.count > 9{
PhoneAuthProvider.provider().verifyPhoneNumber(submitPhoneNumber, uiDelegate: nil) {(verificationID, error) in
if error != nil {
print(error!)
}else{
UserDefaults.standard.set(verificationID, forKey: "authVerificationID")
self.performSegue(withIdentifier: "phoneCode", sender: self)
}
}
}else{
let phoneNumAlert = UIAlertController(title: "Please enter your phone number", message: "You must enter your phone number to continue.", preferredStyle: .alert)
phoneNumAlert.addAction(UIAlertAction(title: "Ok", style: .cancel, handler: nil))
self.present(phoneNumAlert, animated: true)
}
}
let verificationID = UserDefaults.standard.string(forKey: "authVerificationID")
//MARK: When verify code button is pressed
#IBAction func verifyCode(_ sender: Any) {
let credential = PhoneAuthProvider.provider().credential(
withVerificationID: verificationID!,
verificationCode: code.text!)
//This is where the user is signed in if the verification code is correct
Auth.auth().signIn(with: credential) { (user, error) in
if let error = error {
let invalidCodeAlert = UIAlertController(title: "That code is incorrect", message: "Please input the correct code", preferredStyle: .alert)
invalidCodeAlert.addAction(UIAlertAction(title: "Ok", style: .cancel, handler: nil))
self.present(invalidCodeAlert, animated: true)
print(error)
return
}
//MARK: User is signed in
print("Phone number: \(String(describing: user?.phoneNumber))")
self.performSegue(withIdentifier: "accountCreated", sender: self)
}
}
}
All help is very much appreciated! Thanks!
Let's say you hold user data at the users ref. When the user signs in check to see if they have any data there, if they do then they are an existing user, if they don't then it is a new account:
Auth.auth().signIn(with: credential, completion: { [weak self](authDataResult, error) in
if let error = error { return }
guard let safeAuthDataResult = authDataResult else { return }
// 1. get the signed in user's userId
let userId = safeAuthDataResult.user.uid
// 2. check to see if their userId exists at the user's path
let usersRef = Database.database().reference().child("users").child(userId)
usersPublicDataRef.observeSingleEvent(of: .value, with: { (snapshot) in
// 3. this is a new user
if !snapshot.exists() {
// *** what you should do here is update the user's ref with some sort of data
} else {
// 4. this is an existing user
}
})
})
You can use FirebaseAuthUI and FirebasePhoneAuthUI to registered your mobile number into firebase. It's default method and UI which is provided by Firebase itself. So you don't have to worry about rest of things.
You just have to installed PhoneAuthUI using pod and write down below code to registered mobile number:
FUIAuth.defaultAuthUI()?.delegate = self
let phoneProvider = FUIPhoneAuth.init(authUI: FUIAuth.defaultAuthUI()!)
FUIAuth.defaultAuthUI()?.providers = [phoneProvider]
let currentlyVisibleController = self.navigationController?.visibleViewController
phoneProvider.signIn(withPresenting: currentlyVisibleController!, phoneNumber: nil)
Once your mobile number is registered then you will get a callback on this method:
func authUI(_ authUI: FUIAuth, didSignInWith user: User?, error: Error?) {
if user != nil{
// here we need to check if current user is registered or not.
var ref: DatabaseReference!
ref = Database.database().reference()
let userID = Auth.auth().currentUser?.uid
}) { (error) in
print(error.localizedDescription)
}
}else if error != nil{
print(error?.localizedDescription)
}
}
For more information, you can see this tutorial.

How can I change the Image in UIViewController permantly

Apologies again if this is a noobie question, but still relatively new to swift, so please bear with me,
I am trying to change the image in a UIViewController even after the user has left the page or closed the app, the idea being that the image is pressed a password enter and the image is changed, (which I have done with the help of dzk) and the image changes as it should.
but when i leave the app page and then come back in it has reset to it's original image, so frustrating!
below is the code as it stands that will change the image after UIAlertController is validated.
Any help would be grateful.
class Man_VS_Cocktail : UIViewController{
#IBOutlet weak var Cocktail_Image: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nil
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func Cocktail_check_button(sender: AnyObject) {
var password_Text: UITextField?
let alertController = UIAlertController(title: "One more ticked off", message: "ask the barman to enter the password", preferredStyle: UIAlertControllerStyle.Alert)
let tickoff_action = UIAlertAction(title: "sign it off", style: UIAlertActionStyle.Default) {
action -> Void in
if let password = password_Text?.text{
print("password = \(password)")
if password == "pass123" {
self.Cocktail_Image.image = UIImage(named: "riddler_question_marks")
}
} else {
print("No password entered")
}
}
alertController.addTextFieldWithConfigurationHandler { (txtpassword) -> Void in
password_Text = txtpassword
password_Text!.secureTextEntry = true
password_Text!.placeholder = ""
}
alertController.addAction(tickoff_action)
self.presentViewController(alertController, animated: true, completion: nil)
}
As a side note would it be possible to have master rest action that resets all images to their original state? I presume this would be an if statement?
This is an example on using NSDefaults. You can change and format in a way that would fit your needs more.
class Man_VS_Cocktail : UIViewController{
let defaults: NSUserDefaults
#IBOutlet weak var Cocktail_Image: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Check saved password.
let passSaved = defaults.stringForKey("password")
if passSaved == "pass123" {
self.Cocktail_Image.image = UIImage(named: "riddler_question_marks")
} else {
// Set default image.
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func Cocktail_check_button(sender: AnyObject) {
var password_Text: UITextField?
let alertController = UIAlertController(title: "One more ticked off", message: "ask the barman to enter the password", preferredStyle: UIAlertControllerStyle.Alert)
let tickoff_action = UIAlertAction(title: "sign it off", style: UIAlertActionStyle.Default) {
action -> Void in
if let password = password_Text?.text{
print("password = \(password)")
if password == "pass123" {
// Save the password
self.defaults.setObject(password_Text, forKey: "password")
// End save password
self.Cocktail_Image.image = UIImage(named: "riddler_question_marks")
}
} else {
print("No password entered")
}
}
alertController.addTextFieldWithConfigurationHandler { (txtpassword) -> Void in
password_Text = txtpassword
password_Text!.secureTextEntry = true
password_Text!.placeholder = ""
}
alertController.addAction(tickoff_action)
self.presentViewController(alertController, animated: true, completion: nil)
}
You could even do a check in you your AppDelegate with the same format. You could even add some some kind of delay or clear the saved password via the AppDelegate applicationWillTerminate.

UIAlertController whose view is not in the window hierarchy

I'm trying to create an app with Swift and Parse. I'm using Xcode 7 and Swift 2.
I want to show a alert message when User is login failed, here is my function:
func logInViewController(logInController: PFLogInViewController, didFailToLogInWithError error: NSError?){
let alertLoginFailed = UIAlertController(title: "Login Failed", message: "Your username or password is invalid!", preferredStyle: UIAlertControllerStyle.Alert)
alertLoginFailed.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alertLoginFailed, animated: true, completion: nil)
print("Login failed.........!")
}
But I've got this error when run in the emulator:
2015-10-02 11:32:39.988 RedString[2089:886501] Warning: Attempt to present <UIAlertController: 0x7a934400> on <MyProject.ViewController: 0x7b985150> whose view is not in the window hierarchy!
Login failed.........!
I've googled about it, but I didn't found the clear solution.
Here is my whole class:
import UIKit
import Parse
import ParseUI
class ViewController: UIViewController, PFLogInViewControllerDelegate, PFSignUpViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
self.setupLoginView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func logInViewController(logInController: PFLogInViewController, didLogInUser user: PFUser){
self.dismissViewControllerAnimated(true, completion: nil)
}
func logInViewController(logInController: PFLogInViewController, didFailToLogInWithError error: NSError?){
let alertLoginFailed = UIAlertController(title: "Login Failed", message: "Your username or password is invalid!", preferredStyle: UIAlertControllerStyle.Alert)
alertLoginFailed.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alertLoginFailed, animated: true, completion: nil)
print("Login failed.........!")
}
func signUpViewController(signUpController: PFSignUpViewController, didSignUpUser user: PFUser){
self.dismissViewControllerAnimated(true, completion: nil)
}
func signUpViewController(signUpController: PFSignUpViewController, didFailToSignUpWithError error: NSError?){
print("Sign up failed.........!")
}
func setupLoginView(){
if(PFUser.currentUser() == nil){
let loginViewController = PFLogInViewController()
loginViewController.delegate = self
let signUpViewController = PFSignUpViewController()
signUpViewController.delegate = self
loginViewController.logInView!.logo = UIImageView(image: UIImage(named:"logo.png"))
loginViewController.signUpController = signUpViewController
self.presentViewController(loginViewController, animated: true, completion: nil)
}else{
print("login as: " + PFUser.currentUser()!.username!)
//prepare new view here
}
}
}
when
func logInViewController(logInController: PFLogInViewController, didFailToLogInWithError error: NSError?){
let alertLoginFailed = UIAlertController(title: "Login Failed", message: "Your username or password is invalid!", preferredStyle: UIAlertControllerStyle.Alert)
alertLoginFailed.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alertLoginFailed, animated: true, completion: nil)
print("Login failed.........!")
}
is called, your view controller is not on screen. Therefore you could either
dismiss the logInViewController, then display the alert and pass
setUpLogInView as the handler.
or in the above function, try logInController.presentViewController
instead of self.presentViewController
You have to call presentViewController:animated:completion on your login view controller.
Here is a simplified version of your class to show you what I mean:
class ViewController: UIViewController {
weak var loginViewController: UIViewController?
func setupLoginViewController() {
let loginVC = PFLogInViewController()
// setup loginVC
loginViewController = loginVC // store the reference
}
func loginDidFail() {
let alertVC = UIAlertController(...)
// setup alertVC
loginViewController?.presentViewController(...) // present the alert from the login view controller
}
}
I had the same problem in Objective-C, it occurs when the parent window has not yet been drawn to the screen. I solved the problem by calling the code from within viewDidAppear rather than viewDidLoad. See UIAlert error, whose view is not in the window hierarchy

Saving username and password using UISwitch in Swift

Ive searched a lot of threads and this is my last resort because Ive seen this question asked different ways but not exactly for swift and for Username and password saving. I want my UISwitch when turned on to save my username and password info. I've been trying all day to get this UISwitch to save my username and password using NSUserDefaults. Please help me I'm at my whits end with trying it, I've searched almost every forum to find the answer but not many people show you exactly how to implement it. Below is my code. I know its bleak, as I am a beginner, but I have my "Login Button" saving my username and password, but i don't know how to get it to only save that information when I click the UISwitch and how to save it in the "view did load method". thanks for the help in advance!! I don't know what code to include into my UISwitch Method.
Here is the first part of my login button and then my view did load method. I don't have any code for the UISwitchMethod
override func viewDidLoad() {
super.viewDidLoad()
//Save username and password info if Save UISwitch is selected
switchState.on = NSUserDefaults.standardUserDefaults().boolForKey("switchState")
NSUserDefaults.standardUserDefaults().boolForKey("keepUsername")
NSUserDefaults.standardUserDefaults().boolForKey("keepPassword")
}
#IBAction func LoginButton(sender: AnyObject) {
var username = self.usernameTextField.text
var password = self.passwordTextField.text
var user = PFUser.currentUser()
NSUserDefaults.standardUserDefaults().setObject(username, forKey: "keepUsername")
NSUserDefaults.standardUserDefaults().setObject(password, forKey: "keepPassword")
if count(username) < 4 || count(password) < 5 {
var alert: UIAlertView = UIAlertView(title: "Sorry!", message: "Username Must be greater than 4 characters and the password greater that 5 characters", delegate: self, cancelButtonTitle: "Ok")
alert.show()
}else {
self.actInd.startAnimating()
PFUser.logInWithUsernameInBackground(username, password: password, block: { (user, NSError) -> Void in
self.actInd.stopAnimating()
if ((user) != nil) {
println("Success \(user) logged in")
self.performSegueWithIdentifier("toHomeFromLogin", sender: self)
}else {
var alert: UIAlertView = UIAlertView(title: "error", message: "Please Sign up :)", delegate: self, cancelButtonTitle: "Ok")
alert.show()
}
#IBAction func switchStateChanged(sender: UISwitch) {
NSUserDefaults.standardUserDefaults().setBool(switchState.on, forKey: "switchState")
}
I will add some notes and edits I would do and I hope it can help you:
var switchState = Bool()
var userName = String()
var password = String()
override func viewDidLoad() {
super.viewDidLoad()
//Load all values
switchState = NSUserDefaults.standardUserDefaults().boolForKey("switchState")
userName = NSUserDefaults.standardUserDefaults().stringForKey("keepUsername")
password = NSUserDefaults.standardUserDefaults().stringForKey("keepPassword")
//Display values somewhere
}
#IBAction func LoginButton(sender: AnyObject) {
var enteredUser = self.usernameTextField.text
var enteredPassword = self.passwordTextField.text
var user = PFUser.currentUser()
NSUserDefaults.standardUserDefaults().setObject(enteredUser, forKey: "keepUsername")
NSUserDefaults.standardUserDefaults().setObject(enteredPassword, forKey: "keepPassword")
NSUserDefaults.standardUserDefaults().synchronize()
if count(username) < 4 || count(password) < 5 {
var alert: UIAlertView = UIAlertView(title: "Sorry!", message: "Username Must be greater than 4 characters and the password greater that 5 characters", delegate: self, cancelButtonTitle: "Ok")
alert.show()
}else {
self.actInd.startAnimating()
PFUser.logInWithUsernameInBackground(username, password: password, block: { (user, NSError) -> Void in
self.actInd.stopAnimating(
if ((user) != nil) {
println("Success \(user) logged in")
self.performSegueWithIdentifier("toHomeFromLogin", sender: self)
}else {
var alert: UIAlertView = UIAlertView(title: "error", message: "Please Sign up :)", delegate: self, cancelButtonTitle: "Ok")
alert.show()
}
#IBAction func switchStateChanged(sender: UISwitch) {
//var readValueFromSwitch = something bolean
//NSUserDefaults.standardUserDefaults().setBool(readValueFromSwitch, forKey: "switchState")
//NSUserDefaults.standardUserDefaults().synchronize()
}
I send you the hole page so you can get and idea and also there is a way you can send alert messages for the Register page, look the //store data
import UIKit
class RegisterPageViewController: UIViewController {
#IBOutlet weak var userEmailTextField: UITextField!
#IBOutlet weak var userPasswordTextField: UITextField!
#IBOutlet weak var repeatPasswordTextField: 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 registerButtonTapped(sender: AnyObject) {
let userEmail = userEmailTextField.text;
let userPassword = userPasswordTextField.text;
let userRepeatPassword = repeatPasswordTextField.text;
// Check for empty fields
if (userEmail.isEmpty || userPassword.isEmpty || userRepeatPassword.isEmpty)
{
// Display alert message
displayMyAlertMessage("All fields are required");
return;
}
// Check if passwords match
if(userPassword != userRepeatPassword)
{
//Display an alert message
displayMyAlertMessage("Passwords do not match");
return;
}
// Store data
NSUserDefaults.standardUserDefaults().setObject(userEmail, forKey: "userEmail");
NSUserDefaults.standardUserDefaults().setObject(userPassword, forKey: "userPassword");
NSUserDefaults.standardUserDefaults().synchronize();
// Display alert message with confirmation.
var myAlert = UIAlertController(title: "Alert", message: "Registration successful, Thank you!", preferredStyle: UIAlertControllerStyle.Alert);
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default){action in
self.dismissViewControllerAnimated(true, completion: nil);
}
myAlert.addAction(okAction);
self.presentViewController(myAlert, animated:true, completion:nil);
}
func displayMyAlertMessage(userMessage:String)
{
var myAlert = UIAlertController(title: "Alert", message: userMessage, preferredStyle: UIAlertControllerStyle.Alert);
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil);
myAlert.addAction(okAction);
self.presentViewController(myAlert , animated: true, completion: nil)
}
}