0

I've created an app in which, when the user signs up, should create two values in the Firestore server in the user's document, that is level and subjects (meaning it's in /users/userid).

I've tried manually creating the 'users' collection, but nothing is being created when the user signs up.

The following is my code (SignUpViewController):

import Firebase

var reference: DocumentReference!

func firebaseAuth() {
    let userDisplayName = textfieldDisplayName.text!
    let userEmail = textfieldEmail.text!
    let userPassword = textfieldPassword.text!

    if userEmail == "" || userPassword == "" {
        labelMessage.isHidden = false
        labelMessage.textColor = UIColor.red
        labelMessage.text = "Error: A compulsory field is left blank."
    } else {
        Auth.auth().createUser(withEmail: userEmail, password: userPassword) { (user, error) in
            if user != nil && error == nil {
                let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest()
                changeRequest?.displayName = userDisplayName
                changeRequest?.commitChanges(completion: { (error) in
                    if error == nil {
                        let userID = Auth.auth().currentUser?.uid
                        let dataToSave: [String: Any] = ["level":0, "subjects":[""]]

                        self.reference = Firestore.firestore().collection("users").document(userID ?? "")
                        self.reference.setData(dataToSave, completion: { (error) in
                            if error == nil {
                                self.performSegue(withIdentifier: "presentInitial", sender: self)
                            } else {
                                self.labelMessage.isHidden = false
                                self.labelMessage.textColor = UIColor.red
                                self.labelMessage.text = "Error: \(error?.localizedDescription ?? "")"
                            }
                        })

                        self.performSegue(withIdentifier: "presentInitial", sender: self)
                    } else {
                        self.labelMessage.isHidden = false
                        self.labelMessage.textColor = UIColor.red
                        self.labelMessage.text = "Error: \(error?.localizedDescription ?? "")"
                    }
                })
            } else {
                self.labelMessage.isHidden = false
                self.labelMessage.textColor = UIColor.red
                self.labelMessage.text = "Error: \(error?.localizedDescription ?? "")"
            }
        }
    }
}

The following code is from another View Controller which SignUpViewController redirects to (HomeViewController):

import Firebase

var reference: DocumentReference!

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
    let userID = Auth.auth().currentUser?.uid
    reference.getDocument { (docSnapshot, error) in // Fatal error occured here
        let data = docSnapshot?.data()
        let userLevel = data?["level"] as? String ?? ""

        if userLevel == "" {
            self.performSegue(withIdentifier: "performSetup", sender: self)
        }
    }
}

I expected that when redirected to the homepage (segued through presentInitial), the homepage will then read the value of 'level'. However, the app crashed with a fatal error: "Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value" where 'level' was meant to be read from the server.

  • Hi, I just found this, looks similar, no? https://stackoverflow.com/questions/32170456/what-does-fatal-error-unexpectedly-found-nil-while-unwrapping-an-optional-valu – Itang Sanjana Jun 08 '19 at 06:36

2 Answers2

1

I think the problem is not with Firestore. According to the error message, the code wraps an optional value but failed because it is nil, so the problem is probably about these three lines:

let userDisplayName = textfieldDisplayName.text!
let userEmail = textfieldEmail.text!
let userPassword = textfieldPassword.text!

Sometimes, when a UITextField has no text, its text is nil instead of "", which may cause the problem. You can replace the three lines with the following:

let userDisplayName = textfieldDisplayName.text ?? ""
let userEmail = textfieldEmail.text ?? ""
let userPassword = textfieldPassword.text ?? ""

In this way, these three variables will always be "" when there are no text, and your logic of checking blank fields will still work.

Edit: For future reference, the real problem is not in the question but in the comments of this answer.

KeroppiMomo
  • 564
  • 6
  • 18
  • The error occurred in another view controller, I forgot to mention that earlier. Please see the updated question; sorry for the misunderstanding! –  Jun 08 '19 at 07:55
  • What is `reference`? Did you use the variable `uid` when initializing `reference`? – KeroppiMomo Jun 08 '19 at 08:03
  • `reference` is a variable, as seen in the code as `var reference: DocumentReference!`. –  Jun 08 '19 at 08:29
  • `DocumentReference!` is called an [implicitly unwrapped optional](https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html#ID334). If you do not initialize `reference`, it will remain `nil`. It tries to unwrap to `DocumentReference` but it is `nil`, so the app crashes. – KeroppiMomo Jun 08 '19 at 08:35
  • I've changed some of the code around, and it works now. Thanks so much for your help! –  Jun 08 '19 at 08:47
0

Did you check your database rules ? you need to setup the rules when to work and when not to so no one can access the database but your apps for a test now use this code to verify it woks then you should change it

firebase rules

{
  "rules": {
    ".read": true,
    ".write": true
  }
}
Marchal
  • 286
  • 2
  • 16
  • My database rules seem fine; both reading and writing are allowed. –  Jun 08 '19 at 03:46
  • To be clear, when you register do you see your registered account in the Authentication tab ? – Marchal Jun 08 '19 at 03:50
  • Yep, the user is recorded in the Authentication tab. –  Jun 08 '19 at 03:52
  • Try this line from the firebase docs, i think the write code you have is wrong `self.ref.child("users").child(user.uid).setValue(["username": username])` – Marchal Jun 08 '19 at 04:00
  • I've tried that, but that's for the Realtime Database; the Firestore, I believe, is different. –  Jun 08 '19 at 04:04