Disable button from going to next viewcontroller if textfields aren't filled - ios

I'm having the issue with the textfields being empty and once I hit the button it continues to the next viewcontroller. I want to disable the button until all textfields are complete.
#IBAction func buttontapped(_ sender: Any, forEvent event: UIEvent) {
let loginFunc = Login()
loginFunc.login(First_Nm: First_Nm.text!, Pw: Pw.text!, Last_Name: Last_Name.text!, Email: Email.text!) { jsonString in
let response = jsonString
print(response)
if response.range(of: "failure") == nil {
DispatchQueue.main.async {
self.performSegue(withIdentifier: "goToHomeVC", sender: nil)
}
}
}

Add a check for the UITextFields to see if the text's isEmpty property is true before you proceed to performSegue.
#IBAction func buttontapped(_ sender: Any, forEvent event: UIEvent) {
if First_Nm.text!.isEmpty || Last_Name.text!.isEmpty || Email.text!.isEmpty {
print("Incomplete, show an alert for user's attention!")
return
}
//...
}
Add-on: Also, follow a standard naming convention for your properties. Eg: Instead of First_Nm use firstNameTextField.

If you want to achieve the disable and enable on the button based on textField input you can implement UITextFieldDelegate - textFieldDidChange
The code would look like this:
#IBOutlet weak var loginButton: UIButton!
#IBOutlet weak var firstNameTextField: UITextField!
#IBOutlet weak var lastNameTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var emailTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
firstNameTextField.delegate = self
lastNameTextField.delegate = self
passwordTextField.delegate = self
emailTextField.delegate = self
}
And on the bottom of the ViewController file you can create extension for UITextFieldDelegate
extension YourViewController: UITextFieldDelegate {
func textFieldDidChangeSelection(_ textField: UITextField) {
checkButtonState()
}
func checkButtonState() {
var validFirstName = false
var validLastName = false
var validPassword = false
var validEmail = false
if firstNameTextField.text != "" {
validFirstName = true
}
if lastNameTextField.text != "" {
validLastName = true
}
if passwordTextField.text != "" {
validPassword = true
}
if emailTextField.text != "" {
validEmail = true
}
if validFirstName && validLastName && validPassword && validEmail {
loginButton.isEnabled = true
} else {
loginButton.isEnabled = false
}
}
}
Then I would also recommend another check when the button is tapped similar to above answer however I think it could be a more extensive check on email and password. (Login might not be as important but during sign up you might want to validate password and email further than not empty)
You can make up your own rules but usually its something along the lines of
Validate password:
• Required 6-8 characters
if string.count < 6 {
print("Password must be more than 6 characters")
}
• Maybe require one capital letter or one number
Validate email:
• Should contain "#"
if !string.contains("#") {
print("probably not a valid email")
}
You can check the strings in the textfield to see if they meet the criteria if not return a login or sign up error to the user. Hopefully this gets you going in the right direction

Related

How to connect multiple action to one button Xcode?

I'm a noob in ios development and i have a simple problem which i still cannot solve. The thing is i making a reverse words app and when the user tap the button at first time it will reverse the sample text but then when user tup second time same button it will clear the text inside sample text and result label. So the main question is how to connect the "clear action" to the same button ?
#IBOutlet var actReverseStyle: UIButton!
#IBOutlet weak var sampletext: UITextField!
var sample: String {return sampletext.text ?? ""
}
#IBOutlet weak var resultscreen: UILabel!
#IBAction func actreverse(_ sender: UIButton!) {
let sampleSentence = sample
func reverseWolrdsInSentance(sentanse:String) -> String{
let allWords = sampleSentence.components(separatedBy: " ")
var newSentence = ""
for word in allWords{
if newSentence != ""{
newSentence += " " }
let reverseWord = String(word.reversed())
newSentence += reverseWord}
return newSentence}
resultscreen.text = reverseWolrdsInSentance(sentanse: sampleSentence)
actReverseStyle.setTitle("Clear", for: .normal)
}
}
This may be more convenient.
#IBAction func actreverse(_ sender: UIButton!) {
sender.isSelected.toggle();
if sender.isSelected {
// do reverse
} else {
// do clear
}
}
Just use a boolean flag which toggles every time the action is fired
private var clearAction = false
#IBAction func actreverse(_ sender: UIButton!) {
if clearAction {
// do clear stuff
clearAction = false
} else {
// do reversing stuff
clearAction = true
}
}

How to add a user with setValue from Firebase Reading and writing data in ios

I am a newbie in Swift and I am learning by building a social media application.
I am struck at trying to implement self.ref.child("users").child(user.uid).setValue(["username": username]) in my code (from https://firebase.google.com/docs/database/ios/read-and-write).
I have been following the instructions of Kasey Schlaudt and at this point of the tutorial https://youtu.be/GrRggN41VF0?t=619 he tried to add a user with setValue as shown in the Firebase documentation I have linked. The errors I get in the line self.ref.child("users").child(user.uid).setValue(["username": username]) are
Use of unresolved identifier 'user' and Use of unresolved identifier 'username'.
My code so far (with some little changes from the original code in the video in my signInPress function) is
import UIKit
import Firebase
import SwiftKeychainWrapper
class ViewController: UIViewController {
#IBOutlet weak var UserImageView: UIButton!
#IBOutlet weak var usernameField: UITextField!
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var passwordField: UITextField!
override func viewDidLoad()
{
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool)
{
if let _ : Bool = KeychainWrapper.standard.string(forKey: "uid") != nil
{
self.performSegue(withIdentifier: "toFeed", sender: nil)
}
}
func storeUserData(userID: String)
{
//---------------------------problematic line---------------------------
//from https://firebase.google.com/docs/database/ios/read-and-write
//from https://youtu.be/GrRggN41VF0?t=619
self.ref.child("users").child(user.uid).setValue(["username": username])
([
"username": usernameField.text
])
}
#IBAction func signInPress(_ sender: Any)
{
//this way you make sure there is a property inside emailField.text and you have a variable you can easily use
if let email = emailField.text, let password = passwordField.text
{
Auth.auth().signIn(withEmail: email, password: password)
{ (result, error) in
if error != nil && self.usernameField.text!.isEmpty && self.UserImageView.image != nil
{
Auth.auth().createUser(withEmail: email, password: password)
{ (result, error) in
self.storeUserData(userID: (result?.user.uid)!)
KeychainWrapper.standard.set((result?.user.uid)!, forKey: "KEY_UID")
self.performSegue(withIdentifier: "toFeed", sender: nil)
}
} else
{
KeychainWrapper.standard.set((result?.user.uid)!, forKey: "KEY_UID")
self.performSegue(withIdentifier: "toFeed", sender: nil)
}
}
}
}
}
I would very much appreciate any indication as to why the error does not occur for Kasey and what I might need to change to do the same process.
Thank you in advance !
You're actually pretty close, more of a typo issue. See that your storeUserData function is expecting a string called userID? That's what's needed in the line to store that data instead of user.uid
func storeUserData(userID: String) {
let username = self.usernameField.text
self.ref.child("users").child(userID).setValue(["username": username])
here ^^^^^^ userID instead of user.uid
}

Firebase Auth creating users

I can't seem to get this to work. The database portion works and I'm getting user info as intended in the database, but it is not creating users in Firebase Auth. For the following code, it printed "can't register."
Can someone please tell me what I'm doing wrong?
import UIKit
import Firebase
import FirebaseAuth
class AddUserTableViewController: UITableViewController, UITextFieldDelegate {
#IBOutlet weak var firstNameTextField: UITextField!
#IBOutlet weak var emailTextField: UITextField!
#IBAction func saveUserButton(_ sender: Any) {
let ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
FIRAuth.auth()?.createUser(withEmail: emailTextField.text!, password: "pass", completion: { (user, error) in
if error != nil {
print ("Can't Register")
}
else {
print ("I don't know what this means")
}
})
ref?.child("Users").childByAutoId().setValue(["First Name": self.firstNameTextField.text, "Email": self.emailTextField.text])
}
Just include Firebase, you don't need to include FirebaseAuth as well on each page.
Here's my working code for FireBase login, I did this from a Youtube tutorial a few weeks ago.
import UIKit
import Firebase
class LoginController: UIViewController {
#IBOutlet weak var menuButton:UIBarButtonItem!
#IBOutlet weak var signinSelector: UISegmentedControl!
#IBOutlet weak var signinLabel: UILabel!
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var signinButton: UIButton!
var isSignIn:Bool = true
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func signinSelectorChanged(_ sender: UISegmentedControl) {
//Flip the boolean true to false
isSignIn = !isSignIn
//Check the boolean and set the buttons and labels
if isSignIn {
signinLabel.text = "Sign In"
signinButton.setTitle("Sign In", for: .normal)
}
else {
signinLabel.text = "Register"
signinButton.setTitle("Register", for: .normal)
}
}
#IBAction func signinButtonTapped(_ sender: UIButton) {
//Do some form validation on email and password
if let email = emailTextField.text, let pass = passwordTextField.text
{
//Check if it's signed or register
if isSignIn {
//Sign in the user with Firebase
Auth.auth().signIn(withEmail: email, password: pass, completion: { (user, error) in
//Check that user isn't nil
if let u = user {
//User is found, goto home screen
self.performSegue(withIdentifier: "goToHome", sender: self)
}
else{
//Error: Check error and show message
}
})
}
else {
//Register the user with Firebase
Auth.auth().createUser(withEmail: email, password: pass, completion: { (user, error) in
//Check that user isn't NIL
if let u = user {
//User is found, goto home screen
self.performSegue(withIdentifier: "goToHome", sender: self)
}
else {
//Check error and show message
}
})
}
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
//Dismiss the keyboard when the view is tapped on
emailTextField.resignFirstResponder()
passwordTextField.resignFirstResponder()
}
}

outlets renaming themselves? breaking geocodeAddressString()?

Environment:Xcode-8iOS-10Swift-3
Overview:
I've got what, to me, is a bizarre issue with respect to Outlets, which seem to change the name of their target when being setup and, I believe, is the source of the problems I'm having with geocodeAddressString()
A Bit Of Backstory:
My view has a number of elements, but for the purposes of this posting, I'm primarily concerned about the UITextField's and how I believe they are affecting my MKMapView code (based somewhat on comments I saw here)
My UITextField's are utilizing a slightly modified form of an extension (originally by 'nhgrif') which I found here where the aim is to be able to setup a daisy-chain of textfields such that hitting the Next (or Return) button on the pop-up keyboard will automatically proceed to the desired next (or in some cases, previous) textfield.
private var kAssociationKeyNextField: UInt8 = 0
private var kAssociationKeyPreviousField: UInt8 = 1 // I added this
extension UITextField {
#IBOutlet var nextField: UITextField? {
get { return objc_getAssociatedObject(self, &kAssociationKeyNextField) as? UITextField }
set(newField) { objc_setAssociatedObject(self, &kAssociationKeyNextField, newField, .OBJC_ASSOCIATION_RETAIN) }
}
// I added the following
#IBOutlet var previousField: UITextField? {
get { return objc_getAssociatedObject(self, &kAssociationKeyPreviousField) as? UITextField }
set(newField) { objc_setAssociatedObject(self, &kAssociationKeyPreviousField, newField, .OBJC_ASSOCIATION_RETAIN) }
}
}
From the Xcode / Storyboard perspective, this provides the following UI's for setting the next (and/or previous) field in the daisy-chain:
Drilling down
I'm not sure how to really explain the issue I'm seeing other than with a screen-capture video, but since I cannot figure out how to post such here, a bunch of screenshots will have to do...
Start with the Name field, and set the nextField to Address:
Then select the Address field and set the previousField to Name and the nextField to City:
So far, everything seems to be working fine...
Now select the City field and set the previousField to Address and the nextField to State:
Yikes! Note that the name associated with the State field is now "Next Field"
Continue with the State field, setting the previousField to City and nextField to Zipcode:
The State field still shows up as "Next Field" - and now the Zipcode field ALSO shows up as "Next Field"
Finish with the Zipcode field, setting the previousField to State - intentionally leaving the nextField unset:
Some More Code
Here is most of the rest of this particular view class's code
class NewLocationViewController: UIViewController, CLLocationManagerDelegate, UITextFieldDelegate {
#IBOutlet weak var doGeoLocate: UISwitch!
#IBOutlet weak var name: UITextField!
#IBOutlet weak var address: UITextField!
#IBOutlet weak var city: UITextField!
#IBOutlet weak var state: UITextField!
#IBOutlet weak var zipcode: UITextField!
#IBOutlet weak var done: UIBarButtonItem!
#IBOutlet weak var map: MKMapView!
var coords: CLLocationCoordinate2D?
var locationManager: CLLocationManager = CLLocationManager()
var currentLocation: CLLocation!
override func viewDidLoad() {
super.viewDidLoad()
name.delegate = self
address.delegate = self
city.delegate = self
state.delegate = self
zipcode.delegate = self
locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.delegate = self
locationManager.startUpdatingLocation()
}
currentLocation = nil
doGeoLocate.isOn = false
map.isHidden = true
done.isEnabled = false
navigationController?.isNavigationBarHidden = false
navigationController?.isToolbarHidden = false
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if doGeoLocate.isOn == true {
textField.resignFirstResponder()
}
else if textField.nextField == nil {
if (!checkFields()) {
// walk back up chain to find last non-filled text-field...
var tmpField = textField
while ((tmpField.previousField != nil) && (tmpField.previousField?.hasText)!) {
tmpField = tmpField.previousField!
}
tmpField.previousField?.becomeFirstResponder()
}
else {
textField.resignFirstResponder()
}
}
else {
textField.nextField?.becomeFirstResponder()
}
return checkFields()
}
func checkFields() -> Bool {
//... if doGeoLocate switch is on - return true
//... if ALL fields are populated, call geocodeAddress() and return true
//... otherwise return false
}
func geocodeAddress() {
print("GA") //#=#
let geoCoder = CLGeocoder()
let addr = "\(address.text) \(city.text) \(state.text) \(zipcode.text)"
print("ADDR: `\(addr)'")//#=#
geoCoder.geocodeAddressString(addr, completionHandler: {
(placemarks: [CLPlacemark]?, error: NSError?) -> Void in
print("IN geocodeAddressString")//#=#
//if error.localizedDescription.isEmpty == false {
// print("Geocode failed with error: \(error.localizedDescription)")
//}
//else if placemarks!.count > 0 {
let placemark = placemarks![0]
let location = placemark.location
self.coords = location!.coordinate
self.map.isHidden = false
//}
} as! CLGeocodeCompletionHandler) //<<<=== NOTE THIS LINE
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//...
}
#IBAction func toggleGeoLocate(_ sender: AnyObject) {
//...
}
#IBAction func useNewLocation(_ sender: AnyObject) {
//...
}
}
Upon running the app, filling in all the fields, when I click on the 'Done' button in the number-keypad associated with the Zipcode field - I get an exception. The debugging log looks like this:
TFSR: (TFSR Optional("Name") => Optional("Address"))
Returning false
TFSR: (TFSR Optional("Address") => Optional("City"))
Returning false
TFSR: (TFSR Optional("City") => Optional("State"))
Returning false
TFSR: (TFSR Optional("State") => Optional("Zipcode"))
Returning false
GA
ADDR: `Optional("2112 Murray Avenue ") Optional("Pittsburgh ") Optional("PA") Optional("15217")'
(lldb)
The exception shows up as:
func geocodeAddress() {
//...
geoCoder.geocodeAddressString(addr, completionHandler: {
(placemarks: [CLPlacemark]?, error: NSError?) -> Void in
//...
} as! CLGeocodeCompletionHandler) //<<< Thread 1: EXC_BREAKPOINT (code=1, subcode=0x10006c518)
}
And yes, I verified that I have no breakpoints set in the code
SummationI'm reasonably sure that the geocodeAddressString() code is correct (I used it in another app for Swift-2), but I'm very suspicious of the way the State and Zipcode Outlets get renamed when I attempt to chain them with the other fields.Anyone have any ideas?
I'd suggest getting rid of those various casts:
func geocodeAddress() {
//...
geoCoder.geocodeAddressString(addr) { placemarks, error in
//...
}
}
It's easiest to let it infer the correct types for you.
Regarding the naming of outlets, IB is trying to make life easier for you, naming them for you in the absence of any specified name. But with these additional outlets, its default algorithm is falling short. You can probably get around this by naming the outlets yourself in the "Document" section of the "Identity Inspector".

How do I add a sign up page into my Parse app with Swift?

Dose anyone know how I can make a user sign up with parse in Swift Xcode 6.4?
I Have searched everything and can't find one that works.
I Tried this code but it did not work.
It said:
Use of unresolved identifier PFUser
import UIKit
class SignupViewController: UIViewController {
#IBOutlet var usernameTextField: UITextField!
#IBOutlet var passwordTextField: UITextField!
#IBOutlet var emailTextField: UITextField!
#IBOutlet var messageLabel: UILabel!
#IBAction func loginVerifyButton(sender: AnyObject) {
var usrEntered = usernameTextField.text
var pwdEntered = passwordTextField.text
var emlEntered = emailTextField.text
if usrEntered != "" && pwdEntered != "" && emlEntered != "" {
// If not empty then yay, do something
} else {
WrongInfo()
}
}
func userSignUp() {
var user = PFUser()
user.username = usrEntered
user.password = pwdEntered
user.email = emlEntered
}
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.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
func WrongInfo(){
var WrongInfo:UIAlertView = UIAlertView(title: "ALL FEILDS REQUIRED", message: "Please use all feilds!", delegate: self, cancelButtonTitle: "ok")
}
}
You need to import Parse , in Appdelegate.swift file! if still getting same error import Parse in signup view controller too
You have to create your own view and then implement it/segue users to it based on the users current status. If they click your sign up button segue them to a custom view and then act accordingly. You would sign them up with a function similar to what you have offered already in your question:
func myMethod() {
var user = PFUser()
user.username = "myUsername"
user.password = "myPassword"
user.email = "email#example.com"
// other fields can be set just like with PFObject
user["phone"] = "415-392-0202"
user.signUpInBackgroundWithBlock {
(succeeded: Bool, error: NSError?) -> Void in
if let error = error {
let errorString = error.userInfo?["error"] as? NSString
// Show the errorString somewhere and let the user try again.
} else {
// Hooray! Let them use the app now.
}
}
You essentially could use the same view you already have since your fields are identical but call different methods depending on the button they select.

Resources