Swift 4: Expression implicitly coerced from '[String : String?]' to '[AnyHashable : Any]' - swift

Why I getting an error message in the line that says:
usersReference.updateChildValues(values, withCompletionBlock: { (err, ref)
The error message says:
Expression implicitly coerced from '[String : String?]' to '[AnyHashable : Any]'
What changes could I make to my code to prevent the error message?
Here is all of my code from the view controller:
import UIKit
import Firebase
import FirebaseAuth
class RegisterViewController: UIViewController {
private var ref: DatabaseReference! // референс к БД
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var passwordField: UITextField!
#IBOutlet weak var firstNameField: UITextField!
#IBOutlet weak var lastNameField: UITextField!
#IBOutlet weak var cityField: UITextField!
#IBOutlet weak var telNumField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference() // инициализация БД
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func nextButtonPressed(_ sender: Any) {
Auth.auth().createUser(withEmail: self.emailField.text!, password: self.passwordField.text!) { (user, error) in
if error != nil {
print(error!)
self.showAlert(title: "Error!", msg: "Invalid information", actions: nil)
return
}
print("Registration succesfull")
guard let uid = user?.uid else { //доступ к ID пользователя
return
}
self.ref = Database.database().reference() // инициализация БД
let usersReference = self.ref.child("users").child(uid)
let values = ["firstname": self.firstNameField.text, "lastname": self.lastNameField.text, "email": self.emailField.text, "city": self.cityField.text, "telnumber": self.telNumField.text]
usersReference.updateChildValues(values, withCompletionBlock: { (err, ref) in
if let err = err {
print(err)
return
}
print("Saved user successfully into Firebase db")
})
}
}

What changes could I make to my code to prevent the error message?
this isn't error where compiler won't let you run your code without fixing it, this is just warning. But message of this warning is important since if you didn't fix it, your string would be saved like this:
Optional("Text from text field")
So, in your case problem is, that you're passing optional property text of type String? as Any which shouldn't be optional.
You can silence this warning and fix your code by force-unwrapping text properties of your text fields (it's safe to force-unwrap it here because, regarding to docs, this property is never nil)
let values = ["firstname": self.firstNameField.text!, "lastname": self.lastNameField.text!, "email": self.emailField.text!, "city": self.cityField.text!, "telnumber": self.telNumField.text!]

Related

How to retrieve and show user data from Firebase with swift?

I followed a tutorial to create a register and login screen with firebase and is already working, but now I want to create the screen "My account" with text fields where show the name, mail, etc.. of the user, so my question and I need help is how to get the data from firebase and show that information on that screen, how can I do it please?
I already read the firebase documentation https://firebase.google.com/docs/auth/ios/manage-users but I don't know how to proceed because Im learning programming/swift and I have to still improving my level
I attach the code of the sign up view
import UIKit
import FirebaseAuth
import FirebaseFirestore
import Firebase
class SignUpViewController: UIViewController {
#IBOutlet weak var nameField: UITextField!
#IBOutlet weak var lastNameField: UITextField!
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var passwordField: UITextField!
#IBOutlet weak var signUpButton: UIButton!
#IBOutlet weak var errorLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setUpElements()
}
func setUpElements(){
//Hide the error label
errorLabel.alpha = 0
//Style the elements
Utilities.styleTextField(nameField)
Utilities.styleTextField(lastNameField)
Utilities.styleTextField(emailField)
Utilities.styleTextField(passwordField)
Utilities.styleFilledButton(signUpButton)
}
// Check the fields and validate that the data is correct. If everything is correct, this method returns nil. Otherwise, it returns the error message
func validateFields() -> String? {
// Check that all fields are filled in
if nameField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
lastNameField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
emailField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
passwordField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" {
return "Please fill in all fields."
}
// Check if the password is secure
let cleanedPassword = passwordField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
if Utilities.isPasswordValid(cleanedPassword) == false {
// Password isn't secure enough
return "Please make sure your password is at least 8 characters, contains a special character and a number."
}
return nil
}
#IBAction func signUpTapped(_ sender: Any) {
// Validate the fields
let error = validateFields()
if error != nil {
// There's something wrong with the fields, show error message
showError(error!)
}
else {
// Create cleaned versions of the data
let firstName = nameField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let lastName = lastNameField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let email = emailField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let password = passwordField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
// Create the user
Auth.auth().createUser(withEmail: email, password: password) { (result, err) in
// Check for errors
if err != nil {
// There was an error creating the user
self.showError("Error creating user")
}
else {
// User was created successfully, now store the first name and last name
let db = Firestore.firestore()
db.collection("users").addDocument(data: ["firstname":firstName, "lastname":lastName, "uid": result!.user.uid ]) { (error) in
if error != nil {
// Show error message
self.showError("Error saving user data")
}
}
// Transition to the home screen
self.transitionToHome()
}
}
}
}
func showError(_ message:String) {
errorLabel.text = message
errorLabel.alpha = 1
}
func transitionToHome() {
let homeViewController = storyboard?.instantiateViewController(identifier: Constants.Storyboard.homeViewController) as? HomeViewController
view.window?.rootViewController = homeViewController
view.window?.makeKeyAndVisible()
}
}
And the actual code of my account view controller, just I add it the text fields, I don't know how to proceed
import UIKit
class MyAccountViewController: UIViewController {
#IBOutlet weak var nameField: UITextField!
#IBOutlet weak var lastnameField: UITextField!
#IBOutlet weak var emailField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
I think that I provided all the info necessary, thank you

How can I show the localized description of an error in an error label format to a user in Swift?

I'm very new to Swift, and Instead of just printing the localized description of an error when a user attempts to register for an app, I want to show it in an error label to the user. However, I get the error "Expression is not assignable: function call returns immutable value." I'm not sure what this means or what I should be doing differently in order to show the default description for the error.
class RegisterViewController: UIViewController {
#IBOutlet weak var emailTextfield: UITextField!
#IBOutlet weak var passwordTextfield: UITextField!
#IBOutlet weak var errorLabel: UILabel!
override func viewDidLoad() {
errorLabel.isHidden = true
}
#IBAction func registerPressed(_ sender: UIButton) {
if let email = emailTextfield.text, let password = passwordTextfield.text {
Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
if let e = error {
self.errorLabel.isHidden = false
String(e.localizedDescription) = self.errorLabel.text!
"Expression is not assignable: function call returns immutable value"
} else {
//Navigate to ChatViewController
self.performSegue(withIdentifier: "RegisterToChat", sender: self)
}
}
}
}
}
The assignment must be the other way round. You are going to assign always the right side to the left side.
And you don’t need to create a string from a string
self.errorLabel.text = e.localizedDescription

Fetch Data From Firestore for User Profile (swift, iOS, Xcode, firebase/firestore)

I'm a bit of a newb here, so please be kind. I'm a former Air Force pilot and am currently in law school, so coding is not my full time gig...but I'm trying to learn as I go (as well as help my kiddos learn).
I'm working on a profile page for my iOS app. I've gone through the firebase documentation quite extensively, but it just doesn't detail what I'm trying to do here. I've also searched on this site trying to find an answer...I found something that really helped, but I feel like something is just not quite right. I posted this previously, but I deleted because I did not receive any helpful input.
What I'm trying to do is display the user's data (first name, last name, phone, address, etc.) via labels. The code (provided below) works to show the user id and email...I'm thinking this is because it is pulled from the authentication, and not from the "users" collection. This code is attempting to pull the rest of the user's data from their respective document in the users collection.
Here is the full code for the viewController. I've tried and failed at this so many times that I'm really on my last straw...hard stuck! Please help!
My guess is that something is not right with the firstName variable...whether that be something wrong with the preceding database snapshot, or with the actual coding of the variable. But then again...I don't know what I'm doing...so perhaps I'm way off on what the issue is.
// ClientDataViewController.swift
import UIKit
import Firebase
import FirebaseAuth
import FirebaseFirestore
class ClientDataViewController: UIViewController {
#IBOutlet weak var firstNameLabel: UILabel!
#IBOutlet weak var lastNameLabel: UILabel!
#IBOutlet weak var emailLabel: UILabel!
#IBOutlet weak var phoneLabel: UILabel!
#IBOutlet weak var streetLabel: UILabel!
#IBOutlet weak var street2Label: UILabel!
#IBOutlet weak var cityLabel: UILabel!
#IBOutlet weak var stateLabel: UILabel!
#IBOutlet weak var zipLabel: UILabel!
#IBOutlet weak var attorneyLabel: UILabel!
#IBOutlet weak var updateButton: UIButton!
#IBOutlet weak var passwordButton: UIButton!
#IBOutlet weak var uidLabel: UILabel!
let id = Auth.auth().currentUser!.uid
let email = Auth.auth().currentUser!.email
// MARK: Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
self.uidLabel.text = id
self.emailLabel.text = email
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) // call super
getName { (name) in
if let name = name {
self.firstNameLabel.text = name
print("great success")
}
}
}
// MARK: Methods
func getName(completion: #escaping (_ name: String?) -> Void) {
let uid = "dL27eCBT70C4hURGqV7P"
let docRef = Firestore.firestore().collection("users").document(uid)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
print("Document data: \(dataDescription)")
} else {
print("Document does not exist")
}
completion("put the first name data here after we figure out what's in the doc")
}
}
}
The following with solve your problems. However, I'd advise against declaring id and email as force-unwrapped instance properties; they don't even need to be instance properties, let alone force unwrapped. Always safely unwrap optionals before using their values, especially these authorization properties because if the user isn't signed in or is signed out underneath you (expired token, for example), the app would crash here and, as with flying planes, crashing is always to be avoided.
class ClientDataViewController: UIViewController {
#IBOutlet weak var firstNameLabel: UILabel!
#IBOutlet weak var lastNameLabel: UILabel!
#IBOutlet weak var emailLabel: UILabel!
#IBOutlet weak var phoneLabel: UILabel!
#IBOutlet weak var streetLabel: UILabel!
#IBOutlet weak var cityLabel: UILabel!
#IBOutlet weak var stateLabel: UILabel!
#IBOutlet weak var zipLabel: UILabel!
#IBOutlet weak var attorneyLabel: UILabel!
#IBOutlet weak var updateButton: UIButton!
#IBOutlet weak var passwordButton: UIButton!
#IBOutlet weak var uidLabel: UILabel!
let id = Auth.auth().currentUser!.uid
let email = Auth.auth().currentUser!.email
// MARK: Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
self.uidLabel.text = id
self.emailLabel.text = email
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) // call super
getName { (name) in
if let name = name {
self.firstNameLabel.text = name
print("great success")
}
}
}
// MARK: Methods
func getName(completion: #escaping (_ name: String?) -> Void) {
guard let uid = Auth.auth().currentUser?.uid else { // safely unwrap the uid; avoid force unwrapping with !
completion(nil) // user is not logged in; return nil
return
}
Firestore.firestore().collection("users").document(uid).getDocument { (docSnapshot, error) in
if let doc = docSnapshot {
if let name = doc.get("firstName") as? String {
completion(name) // success; return name
} else {
print("error getting field")
completion(nil) // error getting field; return nil
}
} else {
if let error = error {
print(error)
}
completion(nil) // error getting document; return nil
}
}
}
}
And thank you for your service! Hopefully you got to fly a B1-B.
I suspect from the evidence in your question that you are getting a doc, but have an incorrect field name or an uninitialized field in the retrieved doc. As a debug step, replace your getName function with this one, which prints all of the data found in the doc.
func getName(completion: #escaping (_ name: String?) -> Void) {
let uid = Auth.auth().currentUser!.uid
let docRef = Firestore.firestore().collection("users").document(uid)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
print("Document data: \(dataDescription)")
} else {
print("Document does not exist")
}
completion("put the first name data here after we figure out what's in the doc")
}
}
Once we know what's in the doc, it should be easy to work out what value to pass to the completion function.

Despite multiple unwrapping attempts, fatal error: unexpectedly found nil while unwrapping an Optional value

I have been practicing a login page and the creation of an account with Firebase. I am successfully signing up a user and saving the user in firebase. Now I am trying to save a first and last name to the user id when they create their account. I've tried looking at other SO answers, but still can't seem to get this to work.
I have been going through multiple tutorials, and have tried multiple unwrapping attempts, but keep running into this error. Below is my view controller:
View Controller
import UIKit
import Firebase
class ViewController: UIViewController {
var ref: DatabaseReference!
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var firstNameField: UITextField!
#IBOutlet weak var lastNameField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
var ref = Database.database().reference()
}
#IBAction func createAccountTapped(_ sender: Any) {
if let email = emailTextField.text, let password = passwordTextField.text, let firstName = firstNameField.text, let lastName = lastNameField.text {
Auth.auth().createUser(withEmail: email, password: password ) { (user, error) in
// ...
if let firebaseError = error {
print(firebaseError.localizedDescription)
return
//add popup later
}
let userId = user!.uid
self.ref.child("users").child(userId).setValue(["firstName": firstName])
print("User registered in Firebase with a userId of " + user!.uid)
}
}
}
Where am I going wrong? I thought I was unwrapping the variables at the top, with my 'if let'. I tried force unwrapping them individually, as well, but keep having the same error. A bit lost.
in viewDidLoad(), you call
var ref = Database.database().reference()
but it should be
ref = Database.database().reference()
Swift is treating it like a different variable that you're declaring within the scope of viewDidLoad(), so when you go to use ref, it still has no value.
Please change your Database reference from
var ref: DatabaseReference!
to
var databaseRef = Database.database().reference()
and then do
self.child("users").child(userId).setValue(["firstName": firstName])
or in you viewDidLoad do
self.ref = Database.database().reference()
This error happens because you are not initialising your Database reference
This should work without any problems
import UIKit
import Firebase
import FirebaseDatabase
class ViewController: UIViewController {
var ref: DatabaseReference!
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var firstNameField: UITextField!
#IBOutlet weak var lastNameField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.ref = Database.database().reference()
}
#IBAction func createAccountTapped(_ sender: Any) {
if let email = emailTextField.text, let password = passwordTextField.text, let firstName = firstNameField.text, let lastName = lastNameField.text {
Auth.auth().createUser(withEmail: email, password: password ) { (user, error) in
// ...
if let firebaseError = error {
print(firebaseError.localizedDescription)
return
//add popup later
}
let userId = user!.uid
self.ref.child("users").child(userId).setValue(["firstName": firstName])
print("User registered in Firebase with a userId of " + user!.uid)
}
}
}

Getting a nil error with UITextView

I am creating an app that will have several segments of it that will connect to parse, so I thought I would try to create a custom Class for the parse functions.
This address book tab is the first attempt.
I hit a snag with something that I thought would be so simple, but after 10 hours of research, I am turning here.
Here is the ViewController
var addressUUID = NSUUID().UUIDString
class AddViewController : UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var addressImage : UIImageView!
#IBOutlet weak var nameField : UITextField!
#IBOutlet weak var lastNameField : UITextField!
#IBOutlet weak var phoneField : UITextField!
#IBOutlet weak var emailField : UITextField!
#IBOutlet weak var addressCityField : UITextField!
#IBOutlet weak var addressCountryField : UITextField!
#IBOutlet weak var nameFieldLabel : UILabel!
#IBOutlet weak var lastNameFieldLabel : UILabel!
#IBOutlet weak var phoneFieldLabel : UILabel!
#IBOutlet weak var emailFieldLabel : UILabel!
#IBOutlet weak var addressCityFieldLabel : UILabel!
#IBOutlet weak var addressCountryFieldLabel : UILabel!
#IBOutlet weak var doneButton: UIButton!
#IBOutlet weak var scrollView: UIScrollView!
var scrollViewHeight : CGFloat = 0
var person : Person?
var parse : ParseData?
// creating frame for keyboard to force scroll view up
var keyboard = CGRect()
#IBAction func addButtonPressed(sender : UIButton) {
//NSLog("Button pressed")
print ("\(nameField.text)")
parse = ParseData.init(firstName: nameField.text!)
// Saves data to Parse class, regardless of if new or updated record
do {
try parse!.setFirstName(nameField.text!)
try parse!.setLastName(lastNameField.text!)
try parse!.setPhone(phoneField.text!)
try parse!.setEmail(emailField.text!)
try parse!.setAddressCity(addressCityField.text!)
try parse!.setAddressCountry(addressCountryField.text!)
try parse!.setAddressImage(addressImage.image!)
try parse!.setUUID(addressUUID)
} catch let error as PersonValidationError {
var errorMsg = ""
switch(error) {
case .InvalidFirstName:
errorMsg = "Invalid first name"
case .InvalidAddressCity:
errorMsg = "Invalid City"
case .InvalidEmail:
errorMsg = "Invalid email address"
case .InvalidPhone:
errorMsg = "Invalid phone number"
case .InvalidAddressImage:
errorMsg = "Invalid Image"
case .InvalidAddressCountry:
errorMsg = "Invalid Country"
}
let alert = UIAlertController(title: "Error", message: errorMsg, preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "Okay", style: .Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
} catch {
}
if person == nil
{
parse!.saveAddressToParse()
} else {
parse!.updateAddressToParse()
}
As noted, the error is the first line of "try"
The strange thing is the data sent to the Person class works fine.
Also, when I had the Parse functions on this VC, it worked (albeit with a modified code)
Here is the class ParseData
import Foundation
import Parse
enum ParseValidationError : ErrorType {
case InvalidFirstName
case InvalidAddressCity
case InvalidPhone
case InvalidEmail
case InvalidAddressCountry
case InvalidAddressImage
}
// class ParseData : PFObject, PFSubclassing
class ParseData : PFObject, PFSubclassing
{
private(set) var firstName : String?
private(set) var lastName : String?
private(set) var addressCity : String?
private(set) var addressCountry : String?
private(set) var phone : String?
private(set) var email : String?
private(set) var uuid : String?
private(set) var addressImageFile : UIImage?
var person : Person?
init?(firstName fn: String) {
super.init()
do {
try setFirstName(fn)
} catch {
return nil
}
}
static func parseClassName() -> String {
return "ParseData"
}
func saveAddressToParse () {
print ("saveToParse function begins")
let savedAddressObject = PFObject(className: "addressBook")
savedAddressObject["firstName"] = self.firstName!
savedAddressObject["lastName"] = self.lastName!
savedAddressObject["phone"] = self.phone!
savedAddressObject["email"] = self.email!
savedAddressObject["addressCity"] = self.addressCity!
savedAddressObject["addressCountry"] = self.addressCountry!
savedAddressObject["username"] = PFUser.currentUser()!.username
savedAddressObject["uuid"] = addressUUID
savedAddressObject["entryFrom"] = "Divelog New"
let addressBookImageData = UIImageJPEGRepresentation(self.addressImageFile!, 0.5)
let addressBookImageFile = PFFile(name: "addressBookImage.jpg", data: addressBookImageData!)
savedAddressObject ["addressBookImage"] = addressBookImageFile
savedAddressObject.pinInBackground()
savedAddressObject.saveEventually()
}
func updateAddressToParse () {
print ("updateToParse function begins")
let updateAddressQuery = PFQuery(className: "addressBook")
updateAddressQuery.whereKey("uuid", equalTo: person!.uuid!)
updateAddressQuery.getFirstObjectInBackgroundWithBlock {(objects: PFObject?, error: NSError?) -> Void in
if error == nil {
if let updateAddressObject = objects {
updateAddressObject.setValue(self.firstName!, forKey: "firstName")
updateAddressObject.setValue(self.lastName!, forKey: "lastName")
updateAddressObject.setValue(self.phone!, forKey: "phone")
updateAddressObject.setValue(self.email!, forKey: "email")
updateAddressObject.setValue(self.addressCity!, forKey: "addressCity")
updateAddressObject.setValue(self.addressCountry!, forKey: "addressCountry")
updateAddressObject.setValue("Divelog Update", forKey: "entryFrom")
let addressBookImageData = UIImageJPEGRepresentation(self.addressImageFile!, 0.5)
let addressBookImageFile = PFFile(name: "addressImage.jpg", data: addressBookImageData!)
updateAddressObject.setValue(addressBookImageFile!, forKey: "addressBookImage")
updateAddressObject.pinInBackground()
updateAddressObject.saveEventually()
}
}
}
}
func setFirstName(fn : String) throws {
firstName = fn
}
func setLastName(ln : String) throws {
lastName = ln
}
func setPhone (ph : String) throws {
phone = ph
}
func setEmail (em : String) throws {
email = em
}
func setAddressCity(adc : String) throws {
addressCity = adc
}
func setAddressCountry(ad : String) throws {
addressCountry = ad
}
func setAddressImage(ai : UIImage) throws {
addressImageFile = ai
}
func setUUID(ui : String) throws {
uuid = ui
}
}
Needed to add to the AppDelete:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
ParseData.registerSubclass()
Parse.enableLocalDatastore()
The above is the corrected answer and it works fine.
I cant see, where you create an instance of ParseData. I see, that you declare it with
var parse : ParseData?
and then in addButtonPressed you use it. But in between, where is the code that defines it? Something like:
parse = ParseData()