0
votes

I am following a tutorial and cannot seem to register my user as the user variable in the Firebase .createUser method appears to be nil. Therefore, when I unwrap it, I get an error.

I have read through a lot of the documentation as well as checked many other questions similar to mine but nothing seems to work

import UIKit
import Firebase
import SwiftKeychainWrapper

class ViewController: UIViewController {

    @IBOutlet weak var userImgView: UIImageView!
    @IBOutlet weak var usernameField: UITextField!
    @IBOutlet weak var emailField: UITextField!
    @IBOutlet weak var passwordField: UITextField!

    var imagePicker: UIImagePickerController!
    var selectedImage: UIImage!

    override func viewDidLoad() {
        super.viewDidLoad()
        imagePicker = UIImagePickerController()
        imagePicker.allowsEditing = true
        imagePicker.delegate = self
    }

    override func viewDidAppear(_ animated: Bool) {
        if let _ = KeychainWrapper.standard.string(forKey: "uid") {
            self.performSegue(withIdentifier: "toFeed", sender: nil)
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func setupUser(userUid: String) {
        if let imageData = self.userImgView.image!.jpegData(compressionQuality: 0.2) {
            let imgUid = NSUUID().uuidString
            let metaData = StorageMetadata()
            Storage.storage().reference().child(imgUid).putData(imageData, metadata: metaData) { (metadata, error) in
                let downloadURL = metadata
                let userData = [
                    "username": self.usernameField.text!,
                    "userImg": downloadURL!
                    ] as [String : Any]

                Database.database().reference().child("users").child(userUid).setValue(userData)
                self.performSegue(withIdentifier: "toFeed", sender: nil)
            }
        }
    }

    @IBAction func signInPressed(_ sender: Any) {
        if let email = emailField.text, let password = passwordField.text {
            Auth.auth().signIn(withEmail: email, password: password) { user, error in
                if error != nil && !(self.usernameField.text?.isEmpty)! {
                    Auth.auth().createUser(withEmail: email, password: password) { (user, error) in
                        self.performSegue(withIdentifier: "toFeed", sender: nil)
                        let userID = (user?.user.uid)!
                        self.setupUser(userUid: userID)
                        KeychainWrapper.standard.set(userID, forKey: "uid")
                    }
                } else {
                    if let userID = (user?.user.uid) {
                        KeychainWrapper.standard.set((userID), forKey: "uid")
                        self.performSegue(withIdentifier: "toFeed", sender: nil)
                    }
                }
            }
        }
    }

    @IBAction func getPhoto (_ sender: AnyObject) {
        present(imagePicker, animated: true, completion: nil)
    }
}

extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    internal func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        if let image = info[.originalImage] as? UIImage {
            userImgView.image = image
        } else {
            print("image wasnt selected")
        }
        imagePicker.dismiss(animated: true, completion: nil)
    }
}

The error I am getting is one the "let userID = (user?.user.uid)!". It is

Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

1
Have you tried checking if the error object is not nil (inside Auth.auth()... completionblock)? You have no error handling on that block.. - Joshua Francis Roman
I dont use firebase, but does it make sense that you are creating a user with the same email and password that already successfully signed in? Won't the email be already used therefore createUser should return an error saying email already registered? - Joshua Francis Roman
Thank You. I am on the right track again. :) - XXXOMEGALORDXXX
No, if there isn't an account that matches, there will be an error and the second block of code will run therefore bringing them to sign up. If there account exists, no problem. Thanks again - XXXOMEGALORDXXX

1 Answers

2
votes

The completion block for createUser(withEmail:,password:) gets called with either a AuthResult.user or an error. That why, as Joshua commented, you should check if error is nil before accessing any of the user properties.

From the auth quickstart for Swift:

Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
  strongSelf.hideSpinner {
    guard let user = authResult?.user, error == nil else {
      strongSelf.showMessagePrompt(error!.localizedDescription)
      return
    }
    print("\(user.email!) created")
    strongSelf.navigationController?.popViewController(animated: true)
  }
}