Giving Usernames to users with Firebase & Swift - swift

I am a new to programming, and right now I want to give my users a username and then store it in the firebase real time database. However, every time I run my code it comes up with:
Thread 1: signal SIGABRT
I have checked all of my #IB buttons etc for clashes but there is nothing that I can find. I think I have written code that may be out dated so I am hoping someone can shed some light on my situation and help out!
I think there error is coming from here:
import UIKit
import Firebase
class HandlerViewController: UIViewController {
#IBOutlet weak var username: UITextField!
var user : AnyObject?
var ref = DatabaseReference()
override func viewDidLoad() {
super.viewDidLoad()
self.user = Auth.auth().currentUser
ref = Database.database().reference()
// Do any additional setup after loading the view.
}
#IBAction func joinHaps(_ sender: Any) {
ref.child("Usernames").childByAutoId().setValue(username)
self.performSegue(withIdentifier:"HomeScreenOne", sender: nil)
}
}

In your crash log saying 'InvalidFirebaseData', reason: '(setValue:) Cannot store object of type UITextField at,
In this line your getting error, because setValue can't accept UITextField as input.
Change your code to :
#IBAction func joinHaps(_ sender: Any) {
//username is UITextfield, you can fetch text from it using .text
ref.child("Usernames").childByAutoId().setValue(username.text ?? "")
self.performSegue(withIdentifier:"HomeScreenOne", sender: nil)
}

Related

How do I add data from a user in firebase rather than replace what is already there?

The app presents users with a random quote. I want users to be able to save the quotes and see which ones they saved. I can get a single quote to save, however, anytime the save button is clicked again, it overrides the previously saved quote with the new one. I've tried to find the answer elsewhere, but I cannot seem to get it to work. Below is my current code. I've also tried replacing setValue with updateChildValues, but I get the error Cannot convert value of type 'String' to expected argument type '[AnyHashable : Any]'.
import UIKit
import FirebaseDatabase
import FirebaseAuth
class QuotesViewController: UIViewController {
var ref: DatabaseReference?
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
}
#IBAction func backToMain(_ sender: UIButton) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(identifier: "mainHome")
vc.modalPresentationStyle = .overFullScreen
present(vc, animated: true)
}
#IBOutlet weak var quotesLabel: UILabel!
#IBAction func saveButton(_ sender: UIButton) {
guard let user = Auth.auth().currentUser?.uid else { return }
ref!.child("users").child(Auth.auth().currentUser!.uid).child("Quotes").child("quote").setValue(quotesLabel.text!)
}
#IBOutlet weak var nextButtonOutlet: UIButton!
#IBAction func nextQuoteButton(_ sender: UIButton) {
let quotesData = QuotesData()
let randomQuote = quotesData.randomQuote()
quotesLabel.text = randomQuote
}
}
I've also tried:
ref!.child("users").child(Auth.auth().currentUser!.uid).child("Quotes").child("quote").updateChildValues(quotesLabel.text!)
That is expected, you basically are referencing the very same node child("quote") and trying to change its value. But if you want to have multiple quotes, what you need to do is to create multiple nodes under the parent child("Quotes") with different names.
One trivial way of doing so, you might append a different number to each new quote node, for example when you want to add a new quote, define the following path:
child("Quotes").child("quote1").setValue("...")
Path for another quote:
child("Quotes").child("quote2").setValue("...")
And so on.
Alternatively, you can use Firebase Database reference method childByAutoId() to generate unique names. You will use that method after defining the parent node:
ref!.child("users").child(Auth.auth().currentUser!.uid).child("Quotes").childByAutoId().setValue(quotesLabel.text!)
Note:
Try to avoid force unwrapping as much as you can because that makes your app more prone to crashes.
I think you have to add one more field inside your firebase database that must be a unique one
For first time adding data into firebase you can write something like this
let key = ref.child("Quotes").childByAutoId().key
let dict = ["quote": quotesLabel.text!,
"quoteId" : key ?? ""
] as [String: Any]
And whenever you are saving the same quote then you can update the value inside the particular quoteID like this
func updateDatainFirebase(quoteId:String,quote:String){
let dict = ["quote": quotesLabel.text!,
"quoteId" : quoteId ?? ""
] as [String: Any]
self.ref.child(quoteId).updateChildValues(dict)
}

if statement executing itself instead of others

I have a code here that, each time I run it, only the if statement which states "All fields are required" works but NOT ONLY when it must be called, it actually runs in place of the others. So whatever I do even when all the fields are complete, I have "All fields are required" as an alert message.
Here is the code, all help is appreciated, thank you in advance.
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 = ""
let userPassword = ""
let userRepeatPassword = ""
// Check for empty fields
if (userEmail.isEmpty || userPassword.isEmpty ||
userRepeatPassword.isEmpty)
{
// Display Alert Message
displayMyAlertMessage(userMessage:"All fields are required")
return
}
//Check if passwords match
if (userPassword != userRepeatPassword)
{
// Display an alert message
displayMyAlertMessage(userMessage:"Passwords do not match")
return
}
// Store data
UserDefaults.standard.set(userEmail, forKey:"userEmail")
UserDefaults.standard.set(userEmail, forKey:"userPassword")
UserDefaults.standard.synchronize()
// Display alert message with confirmation
_ = UIAlertController(title:"Alert", message:"Registration is
successfull. Thank you!",
preferredStyle:UIAlertControllerStyle.alert);
_ = UIAlertAction(title:"Ok", style:UIAlertActionStyle.default)
{
action in
self.dismiss(animated: true, completion:nil)
}
}
func displayMyAlertMessage(userMessage:String)
{
let myAlert = UIAlertController(title:"Alert", message: userMessage,
preferredStyle:UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title:"Ok",
style:UIAlertActionStyle.default, handler:nil)
myAlert.addAction(okAction)
self.present(myAlert, animated:true, completion:nil)
}
}
Do you ever assign any values to userEmail, userPassword, userRepeatPassword? You initialize them as empty at the start of the function, and it looks like their values never change.
Instead of declaring them in the function, try using class level variables, and linking them to your textfields in Storyboard.
#IBOutlet weak var userEmail: UITextField!
#IBOutlet weak var userPassword: UITextField!
#IBOutlet weak var userRepeatPassword: UITextField!
#IBAction func registerButtonTapped(_ sender: AnyObject) {
// Check for empty fields
if (self.userEmail.text.isEmpty || self.userPassword.text.isEmpty || self.userRepeatPassword.text.isEmpty) {
// Display Alert Message
displayMyAlertMessage(userMessage:"All fields are required")
return
}
...
}
I suspect that this code is not an accurate representation of your implementation. Would you be able to copy and paste the registerButtonTapped(_:) function unedited?
If it is, I would agree with #unmarshalled: It appears that you have declared each of the variables with an empty string as their value. If the code you have posted is implemented exactly as above, that is the cause of your issue.
based on the code you have posted, I would also recommend the following alterations:
get the email, password & repeatPassword from outside the scope of the function: usually, by just pulling it directly from the UI, most commonly from text fields: i.e. userEmailTextField.text
extracting your user defaults keys, and any other string literals you have, into a constants file is good practice and avoid any unnecessary misspelling related bugs.
you don't need to add a handler to a UIAlertAction if all you want it to do is dismiss the alert. UIAlterController will automatically be dismissed automatically: the handler argument has a default value of nil and can be omitted, simply:
let okayButton = UIAlertAction(title: "Ok", style: .default)
Generally speaking, you don't want to store a reference to a shared instance. However, within small local scopes its a little cleaner to do so:
let userDefaults = UserDefaults.standard
userDefaults.set(userEmail, forKey:"userEmail")
userDefaults.set(userEmail, forKey:"userPassword")
userDefaults.synchronize()
Cheers :)
EDIT:
I would suggest extracting the conditional out to a computed property for readability and check if count == 0 rather than isEmpty. The advantage of this is that you can make the computed property more comprehensive, I.e this will check that the strings are not nil or empty. Usually checking the count is enough, but there’s no harm in covering your bases.
As it stands with the current UIKit implementation, UITextField.text can never be nil. That being said, official documentation does not make that guarantee explicitly, so the best way to handle it is to implement it like an optional, below.
So something like:
fileprivate var registrationFormCompleted: Bool {
guard username = usernameTextfield.text,
password = passwordTextField.text,
repeat = repeatPasswordTextField.text,
else {
return false
}
return username.count > 0 &&
password.count > 0 &&
repeat.count > 0
}
In use it would be:
#IBAction func registerButtonTapped(_ sender: AnyObject) {
// Check for empty fields
if !registrationFormCompleted {
// Display Alert Message
displayMyAlertMessage(userMessage:"All fields are required")
return
}
//....
}

Learning Swift & Xcode - #IBAction func reset for display if UITextField is empty

Disclaimer: I am teaching myself Swift & Xcode so my question is rather simple.
I'm building a simple application to get started, which has a text field connected to a String output.
The lesson I'm on currently has an excerpt which reads:
"The reset method simply needs to clear out the text of both the nameField and the lyricsView—you can do this by setting each of their text properties to an empty string."
I understand this probably involves an if statement, but I think the explanation on this is rather poor.
Here's the viewcontroller:
class ViewController: UIViewController {
#IBOutlet weak var nameField: UITextField!
#IBOutlet weak var lyricsView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func reset(_ sender: Any) {
}
#IBAction func displayLyrics(_ sender: Any) {
}
}
Can someone explain what they mean by setting the properties of nameField and lyricsView to an empty string in order to reset?
Thanks!
If you want to clear the text of a textField or a textView, just set the text property to an empty string. As your lesson hint:
The reset method simply needs to clear out the text of both the
nameField and the lyricsView—you can do this by setting each of their
text properties to an empty string.
The reset method should like this:
#IBAction func reset(_ sender: Any) {
nameField.text = ""
lyricsView.text = ""
}
To clear those fields I'd use:
#IBAction func reset(_ sender: Any) {
nameField?.text = ""
lyricsView?.text = ""
}
The question mark will safely execute the code even if, for some reasons, those fields are not loaded yet, or have been freed or removed.
ADD this to reset method to remove the content:-
nameField.text = ""
lyricsView.text = ""
When you enter something into nameField something shows up in your lyricsView. So, there should be a way to clear what you've entered (and what is displayed). Hence, the reset function (I guess it's bound to a button).
Once you hit reset the text in nameField and lyricsView should disappear. You can do this by assigning both to something called an empty string, which is just two double-quotes:
let anEmptyString = ""
You'd need to assign the "" to the nameField and lyricsView text property.

Firebase swift ios login system error Assertion failed/Exec_BAD_INSTRUNCTION (code=EXC_i386_INVOP, subcode=0x0)

Im trying to use the built in authentication system in firebase and followed this TuT to do so
Firebase Login Tutorial
I am able to run my app but when i go to login and type in a valid (or invalid) login email and password it crashes with this error in the console:
Assertion failed: (request.URL), function -[FSRWebSocket initWithURLRequest:protocols:queue:andUserAgent:], file /Users/vikrum/dev/git/firebase-client-objc/Firebase/Firebase/Libraries/SocketRocket/FSRWebSocket.m, line 302.
(lldb)
When I don't type anything and hit login it crashes with this error (i have measures to prevent the app from crashing because of this, possiably side affect of the bigger issue):
<pre>fatal error: unexpectedly found nil while unwrapping an Optional value</pre>
The inline errors below are the same w/ both above console errors
Edit: I have noticed that in the pod directory in my app in the framework and then ios folders the contents are (My hierarchy with red error **LINKhttp://i.stack.imgur.com/IHCQw.png) highlighted in red. The directory for the 4 frameworks was iPhoneOS9.0.sdk for some reason i only have a iPhoneOS.sdk and iPhoneOS9.2.sdk also the firebase and the other framework below it have strange directory locations [(They all go to this location **LINKhttp://i.stack.imgur.com/Z6dX5.png) and i cant for the life of me figure out how to fix it (i'm not totally sure this is the error causing my app to crash after I try to log in but its the only error I have seen so it must be)
Edit 2: i've been looking around and every recent tut to make an app as of at least ios 9 has been confusing me because of this, i have seen 2 separate apps tuts where i downloaded the finale project and i wasn't able to run it because in the pod dir(all where for firebase and ios 9.2 when made and had all the frameworks for firebase) it said "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.sdk/System/Library/Frameworks/CFNetwork.framework" and i cant change it because when i go to the little dir change button i have to get into the package contents of xcode and it wont let me do that
The inline error when I am redirected
Second files inline error (same)
Here is where i get directed when the app crashes and i get the error(2 places)
import Foundation
import Firebase
let BASE_URL = "https://baseball-pitcher-app.firebaseIO.comΩΩ"
let FIREBASE_REF = Firebase(url: BASE_URL)
var CURRENT_USER: Firebase
{
***let userID = NSUserDefaults.standardUserDefaults().valueForKey("uid") as! String*** ERROR IN THIS LINE
let currentUser = Firebase(url: "\(FIREBASE_REF)").childByAppendingPath("users").childByAppendingPath(userID)
return currentUser!
}
One thing I did find odd was that the error was in the logout button which is weird because i've never pressed it since the error occurred.
import UIKit
import Firebase
class LoginViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var userUsernameTextField: UITextField!
#IBOutlet weak var userPasswordTextField: UITextField!
#IBOutlet weak var logoutButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.userUsernameTextField.delegate = self;
self.userPasswordTextField.delegate = self;
}
override func viewDidAppear(animated: Bool)
{
super.viewDidAppear(animated)
if NSUserDefaults.standardUserDefaults().valueForKey("uid") != nil && CURRENT_USER.authData != nil
{
self.logoutButton.hidden = false
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
self.view.endEditing(true)
return false
}
#IBAction func loginButtonTapped(sender: AnyObject) {
let email = self.userUsernameTextField.text
let password = self.userPasswordTextField.text
if email != "" && password != ""
{
FIREBASE_REF.authUser(email, password: password, withCompletionBlock: { error, authData in
if error == nil
{
NSUserDefaults.standardUserDefaults().setValue(authData.uid, forKey: "uid")
print("Logged In :)")
self.logoutButton.hidden = false
}
else
{
print(error)
}
})
}
else
{
let alert = UIAlertController(title: "Error", message: "Enter Email and Password.", preferredStyle: UIAlertControllerStyle.Alert)
let action = UIAlertAction(title: "Ok", style: .Default, handler: nil)
alert.addAction(action)
self.presentViewController(alert, animated: true, completion: nil)
}
}
#IBAction func logoutButtonTapped(sender: AnyObject) {
CURRENT_USER.unauth()**** ERROR IN THIS LINE
NSUserDefaults.standardUserDefaults().setValue(nil, forKey: "uid")
self.logoutButton.hidden = true
}
The userId id is most likely nil, and in general it's better to catch the cases in which is it nil, like this
if let userID = NSUserDefaults.standardUserDefaults().valueForKey("uid") as? String {
print(userID) //do something with the user id here
} else {
print("The userID was nil.") //avoid the user id.
}
And the reason for the second error is that CURRENT_USER, since it never Auth'd to start with is now nil. Not sure why the button's IBAction is being called but check the actions and outlets in IB.
Also, the
var CURRENT_USER
is a little suspect.
Based on the name of the var, I would expect it to contain the Current User (as maybe a User Class?).
However when called, it returns a Firebase object which contains a path to the current users node (/appPath/users/users node) and no other user data. It doesn't appear to be populated when a user authenticates either.
Not sure if that was the intention or if it's used somewhere else, but doing this
CURRENT_USER.unauth()
may be an issue since it doesn't contain any auth data.

(swift) do not let anonymous users in

my app needs from the user to log in. okay i connected my app to parse, and in the login view controller i wrote these codes
import UIKit
class SigninPageViewController: UIViewController {
#IBOutlet weak var userNameTextField: UITextField!
#IBOutlet weak var userEmailTextField: UITextField!
#IBOutlet weak var userPasswordTextField: 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 doneButtonTapped(sender: AnyObject) {
let userEmail = userEmailTextField.text;
let userPassword = userPasswordTextField.text;
PFUser.logInWithUsernameInBackground(userEmail, password: userPassword) {
(user: PFUser?, error: NSError?) -> Void in
if user != nil {
// Login is successful
NSUserDefaults.standardUserDefaults().setBool(true, forKey:"isUserLoggedIn");
NSUserDefaults.standardUserDefaults().synchronize();
self.dismissViewControllerAnimated(true, completion: nil);
} else {
println("Could not find user")
}
}
}
}
the problem is when i tried my app and tried to write the wrong email and the wrong password, the app let me log in without checking if the user is exist or not and without printing line (Could not find user). So the question here is how to let my app check if the user is exist or not, and do not let anonymous users in.
You could establish your own requirements for logging in. If you do a fetch for the user with the email address they entered, you can allow log in only if the email address exists. If it doesn't, just send the user an alert, and don't go through with the login.