[iOS] Project: Expense Tracker. Part 2. Google Firebase

MYH
彼得潘的 Swift iOS / Flutter App 開發教室
6 min readAug 20, 2021

Alright, time for another round, eh? I try to use different databases every time I do a side project, and since I did CoreData and Realm before in my Contact-like app and To-do list app, it’s time that I tried firebase.

In this article, I’ll walk through:

how to implement Firebase to your Xcode project
how to carry out CRUD using firebase framework in my project

Without further ado, let’s dive in.

How to implement Firebase to your Xcode project

ps: you’ll need a google account to create your firebase project.

Press iOS to go through several process to set up your project.

Follow the step like above and you should be golden. Note that if you accidentally press download the plist twice, you should delete both files and download the plist again since they would be automatically rename to GoogleService-Info-2.plist, which are not the plist that firebase would look for.

Next, we will add Firebase SDK to our Xcode project. (I use Cocoa Pods but SPM should be valid as well.)
1. Go to terminal and direct to your project directory. (cd + file path)
2. pod init (if you already have your ruby pod file, then ignore this step)
3. pod ‘Firebase/Firestore’
We are only gonna use this one for now, but Firebase actually has so many pods with different functionalities. For example: ‘Firebase/Auth’ is used to create register/login function.
4. run pod install in terminal

Okay, let’s open our .xcworkspace file to check whether everything is alright. We can do this by printing out the firestore in the console. If you see your firestore in the console, you’re all set.

For this project, we will use firestore database instead of realmtime database. Firebase actually provides a document on how to choose the right database for your project, but to be honest, I don’t think anyone would choose realtime database over firestore database since the latter is faster, newer, and more efficient than the former. Anyway, here’s the link of the official doucument on which database you should choose.

Finally, we’re done setting up the firestore. It’s time to dig deep to our project.

How to carry out CRUD using firebase framework in my project

Let’s take a quick look at my project’s demo. (It’s really laggy because I’m still using a 2015 MacBook Pro. Sorry guys.)

Basically, we can use this calculator to Create the data and pass it to firebase. Afterward, we will Read the data from firebase and present it on the screen. Simple as that.
Before we get things started, there’s one more thing that can help us code our project. Since this project contains a lot of parameters which require String input, it’s best for us to make those String values become constant so that we won’t crash our app due to misspelling. And we can do this by using static let in struct. (For the rest of my code to make sense, here’s my set up.) For example, if you want to pass in the string value “expense”, now you can just type ExpenseConstant.collection.

Create Data

Finally let’s take a look at the code. As you can see from the demo .gif above, I have a calculator, a note text field, a data picker, and a scroll view where you can select the category. And since they are optional (except for the data picker), we will wrap all of this in a “if let”.

@IBAction func sendPressed(_ sender: UIButton) {let sendDate = formatter.string(from: dateSet.date)if let expenseBody = displayLabel.text, let category = chooseCategory, let note = noteTextField.text {db.collection(ExpenseConstant.collection).addDocument(data: [ExpenseConstant.expense: expenseBody,ExpenseConstant.category: category,ExpenseConstant.date: sendDate,ExpenseConstant.note: note]) { error inif let e = error {print("error saving data to firebase. \(e)")} else {print("data sent successfully.")        }     }  }DispatchQueue.main.async {self.tableView.reloadData()  }}

And the firebase would look like this.

Read Data

You would want to read Data under these circumstancs. 1. in viewDidLoad(). 2. every time when you press the checkmark button and create a new data. Here’s the code.

func loadData(_ dateTime: String) {print("Loading data...")db.collection(ExpenseConstant.collection).whereField(ExpenseConstant.date, isEqualTo: dateTime).addSnapshotListener { querySnapshot, error inself.expenseData = []var total: Int = 0if let e = error {print("There was an isssue retrieving data from Firestore. \(e)")} else {if let snapshotDocuments = querySnapshot?.documents {for doc in snapshotDocuments {let data = doc.data()if let expense = data[ExpenseConstant.expense] as? String, let category = data[ExpenseConstant.category] as? String, let note = data[ExpenseConstant.note] as? String, let timestamp = data[ExpenseConstant.date] as? String{let newData = ExpenseSpend(expense: expense, category: category, date: timestamp, note: note)self.expenseData.append(newData)if let intN = Int(expense) {total = total + intN  }DispatchQueue.main.async {self.totalSpent.text = String(total)self.tableView.reloadData()    }} else {print("Pass unsuccessfully in loadData()")            }          }        }      }    }  }}

I use .whereField( , isEqualTo: ) to get today’s data. Normally, you just need to add .getDocuments to get the data in firebase; however, in this case you should change .getDocuments to .addSnapshotListener. The difference is that .getDocument will only get you the data when you call the function, and .addSnapshotListener will listen for realtime update and get you the data once there is any change to the database.
Some notes: expenseData is an array of dictionary (ExpenseSpend is the struct that I preset up) which I set up to store the data from firebase locally. Note that you should empty the array every time you use .getDocuments/.addSnapshotListener so that you won’t store the data repeatedly.

There are also other functions to query the data. Here’s a link to the file.

Update Data

I didn’t make a function in my project to update the data, however, it’s really easy to make it work. Here’s a simple example on how to update your data.

func updateData(){
db.collection(ExpenseConstant.collection)
.document(//string value of the document)
.updateDate(_:completion)
}

Delete Data

I didn’t make a function to delete the data either, but it’s also pretty easy to implement.

func deleteData(){
db.collection(ExpenseConstant.collection)
.document(//string value of the document)
.delete()
}

And that should do the work for you.

That’s a wrap for part 2. I initially want to do a comparison between Firebase and Realm, but then I found this incredible website where a lot of people share their thought on this subject, so I would leave the link down below instead.

See you guys at part 3 where I will discuss on how to incorporate the calendar (FSCalendar) to my project.

--

--