Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add model data objects to Firebase database

I have 2 questions:

  1. I know how to add regular objects to Firebase using simple key-value pairs but how would I add a user object?

  2. In my UserAccount object I'm not to sure about the UserAcct's second init method. Should I use the init(snapshot: FIRDataSnapshot){} to add to Firebase or should I just stick with the regular init method?

My user model object:

import Foundation
import UIKit
import Firebase
import FirebaseDatabase

    class UserAccount{

        var userID: String
        var email: String
        var creationDate: String

        init(userID: String, email: String, creationDate: String){

            self.userID = userID
            self.email = email
            self.creationDate = creationDate
        }//end init

        //Is this second init necessary?
        init(snapshot: FIRDataSnapshot) {
            userID = snapshot.value!["userID"] as! String
            email = snapshot.value!["email"] as! String
            creationDate = snapshot.value!["creationDate"] as! String
        }

    }//END class

My class for signing a user up:

    import UIKit
        import Firebase
        import FirebaseAuth
        import FirebaseDatabase

        class CreateAccountController: UIViewController{

        @IBOutlet weak var emailTextField: UITextField!
        @IBOutlet weak var passwordTextField: UITextField!


        var dbRef: FIRDatabaseReference!

        //Array to hold users
        var userAcct = [UserAccount]()


        override func viewDidLoad() {
                    super.viewDidLoad()

                    //Firebase Ref
                    self.dbRef = FIRDatabase.database().reference()
                }

        //Button to sign the user up
        @IBAction func signUpButtonPressed(sender: UIButton) {


        FIRAuth.auth()?.createUserWithEmail(emailTextField.text!, password: passwordTextField.text!, completion: {

                        (user, error) in

                        if error != nil{
                            print(error?.localizedDescription)
                        }else{

        let emailAddress = self.emailTextField.text!
        let currentUserID: String = (FIRAuth.auth()?.currentUser?.uid)!
        let accountCreationDate = FIRServerValue.timestamp()

        self.userAcct =[UserAccount(userID: currentUserID, email: emailAddress, creationDate: accountCreationDate)]


        self.dbRef.child("Users").child("UserID: \(currentUserID)").child("Account-Creation-Date").setValue([\\How to add my self.userAcct model object in here? Should I add it to an array])    
        }
    })
}
like image 380
Lance Samaria Avatar asked Jul 16 '16 14:07

Lance Samaria


2 Answers

I would suggest you create a protocol like this one:

protocol DictionaryConvertible {
    init?(dict:[String:AnyObject])
    var dict:[String:AnyObject] { get }
}

Note that this is uses an optional initializer, which means that it can fail and return nil. I used this to ensure that all the key-value pairs you need from the dictionary are really there, and return nil otherwise. Now you can add conformance to your UserAccount class like this:

class UserAccount: DictionaryConvertible {

    var userID: String
    var email: String
    var creationDate: String

    init(userID: String, email: String, creationDate: String){
        self.userID = userID
        self.email = email
        self.creationDate = creationDate
    }

    // DictionaryConvertible protocol methods
    required convenience init?(dict: [String:AnyObject]) {
        guard let userID = dict["userID"] as? String, email = dict["email"] as? String, creationDate = dict["creationDate"] as? String else {
            return nil
        }
        self.init(userID: userID, email: email, creationDate: creationDate)
    }
    var dict:[String:AnyObject] {
        return [
            "userID": userID,
            "email": email,
            "creationDate": creationDate
        ]
    }
}

Note: I used the initializer you already made instead to get rid of the boilerplate code. To interact with Firebase simply initialize your UserAccount like this:

let user:UserAccount? = UserAccount(dict: snapshot?.value as! [String:Anyobject])

To answer your first question, you can write your object to firebase like this:

ref.child("Users").child(user!.userID).setValue(user!.dict)

You cannot just write any type of object to firebase (only NSNumber (includes BOOL), NSDictionary, NSArray, NSString, nil / NSNull to remove the data), therefore you have to 'convert' your user object into a dictionary.

What is great about this approach is that is flexible so you can use with any data object simply by adding conformance to the protocol (especially easy when you use structs instead of classes because then you can just add conformance to the protocol using extensions). You could even use this with any kind of database that works with dictionaries without needing to change much. Also, you should make sure all those optionals are dealt with in a safe way and avoid those '!' whenever possible.

like image 123
dennism Avatar answered Oct 08 '22 07:10

dennism


This is the method I used. I explained everything in the comments above the code.

The end result is you create a dictionary:

let dict = [String:Any]()

You then update the key value pairs using the dictionary’s updateValue method:

dict.updateValue(someValue, forKey: “someKey”)

Then you finally upload that dict to the data base:

let userAccountRef = self.dbRef.child("users").child(theUsersID).child(“userAccount”)

userAccountRef.updateChildValues(dict)

My class for signing a user up:

import UIKit
import Firebase
import FirebaseAuth
import FirebaseDatabase

class CreateAccountController: UIViewController{

@IBOutlet weak var emailTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!

//Your firebase reference
var dbRef: FIRDatabaseReference!

//Current Timestamp in Seconds. You can convert the value later on
let timeStamp:NSNumber? = Int(NSDate().timeIntervalSince1970)

override func viewDidLoad() {
      super.viewDidLoad()
     //Firebase Reference to our database
     self.dbRef = FIRDatabase.database().reference()
}

//Button to sign the user up
@IBAction func signUpButtonPressed(sender: UIButton) {

   FIRAuth.auth()?.createUserWithEmail(emailTextField.text!, password: passwordTextField.text!, completion: {

        //This is where your uid is created. You can access it by using user!uid. Be sure to unwrap it.
        (user, error) in
        print("my userID is \(user.uid)")

      if error != nil{
          print("Account Creation Error: \(error?.localizedDescription)")
          return
      }

      //This constant holds the uid. It comes from the (user, error). The user argument has a uid string property
      let currentUserID = user!.uid // which is the same as FIRAuth.auth()?.currentUser?.uid

      //Here you initialize an empty dictionary to hold the keys and values you want uploaded to your database
      let dict = [String:Any]()

      //use the dictionary’s updateValue() method to update the values and matching keys
      dict.updateValue(currentUserID, forKey: "userIDKey")
      dict.updateValue(self.emailTextField.text!, forKey: "emailKey")
      dict.updateValue(self.timeStamp!, forKey: "acctCreationDateKey")

      //This gives you reference to your database, then to a child node named "users", then another node using the uid, and finally to another node named "userAccount". This final node is where you will keep your dictionary values for your database.
      let userAccountRef = self.dbRef.child("users").child(currentUserID).child(“userAccount”)

      //Here you upload your dictionary to the userAccountRef with the dictionary key/values you set above using the dict’s updateValue() method
      userAccountRef.updateChildValues(dict)
   })
}
}
like image 30
Lance Samaria Avatar answered Oct 08 '22 06:10

Lance Samaria