Firebase is really powerful tool, it enables you to create observer patterns in a declarative way and without any boilerplate code. And recently, I have a great opportunity to migrate existing project to the Firebase and I want to share with you how it happens on practice. This topic is quite a wide so I tried to divide it to several parts from simpler to more complicated.
Let’s dive into the simpler part.
1. Create new iOS project
2. Create new project in the Firebase console
3. Connect to the Firebase DB
4. Create first collections
5. Try add / update / delete queries
6. Create Observers and data sync
7. Working with DateTime types
8. Last but not the least – rules
Create a new iOS project
A good starting point is to create new iOS project. Just run Xcode and create new one. But before we proceed it would be better to install required libraries using cocoapods as dependency manager.
I’m sure you’re familiar with this dance already, however if you haven’t experience with Pods before please check official site and tutorials:
Let’s add ‘Firebase/Database’ library to the Podfile it would be enough for now.
pod 'Firebase/Database'
Btw, if you want to get more info about Firebase libs check this link
Create new project in the Firebase console
Now let’s turn to Google Firebase. To create a new project just go here and don’t forget to choose “Add Firebase to your iOS app”.
The important thing here is to get the GoogleService.plist configuration file for your iOS project.
After downloading the file, keep it somewhere, we will need it soon. If you missed this step, you can download .plist from project settings at any time.
Connect to the Firebase DB
Now we need to go back our iOS project and connect to Firebase by putting on GoogleService.plist to the project Resources folder.
After this, check if you made a connection to the new database in AppDelegateFIRApp.configure()
.
Otherwise, you will get an error: Terminating app due to uncaught exception 'MissingDatabaseURL', reason: 'Failed to get FIRDatabase instance: FIRApp object has no databaseURL in its FirebaseOptions object
.
And of course, please do not forget to put import Firebase
.
Create first collections
Firebase based on NoSQL paradigm and if I’m not mistaken it uses MongoDB behind the scene, which requires quite a bit of providence. The more important thing you need to remember is how you are going to manipulate with data. The best way is to use nested data poorly and to focus on flatten data.
Pay attention that you haven’t an opportunity to see the db scheme at once. Usually Firebase creates collections dynamically as soon as first value appears.
On the off-chance, I provided several ways to come over this:
– Import already prepared json if you have such
– Add each collection and data manually
– Create collections dynamically
Ok, let’s start from dynamically creation of collections. Maybe it seems a bit scary, but It’s actually pretty simple. So, to work with our DB in the iOS app we need the basic building block that contains 4 classes(listed below) and enum called PostKeys:
FIRManager – DB configuration
Post – DataModel
PostsService – To store all queries
ObservePostManager – Interlayer for PostsService
I guess it would be better to show you simple “NoUML” scheme. Notice, the basic building block is circled with orange and the enum is colored by yellow:
The enum PostKeys plays a significant role. It reflects the Post’s collection structure.
enum PostKeys: String {
case PostID = "PostID"
case PosterID = "PosterID"
case Message = "Message"
case PostDate = "PostDate"
case IsPublished = "IsPublished"
}
FIRManager class allows us to initialize DB connection and define references to collections.
var rootRef = FIRDatabaseReference.init()
var postsRef = FIRDatabaseReference.init()
func initialize() -> FIRManager {
rootRef = FIRDatabase.database().reference() // define the root ref of DB
postsRef = rootRef.child("posts") // define the ref on collection
return self
}
Post is the DataModel class. The main goal to initialize object after getting data and convert them to appropriate view.
var postID: String! // Unique ID of Post
var posterID: String! // User ID
let message: String! // Message
let postDate: AnyObject! // The time when the post was created
let isPublished: Int! // The published status of post
var ref: FIRDatabaseReference? // Will be the direct reference to object in DB, so you can easy get an access to it
// Initialize object with DB data
init(snapshot: FIRDataSnapshot) {
postID = snapshot.key
// ...
ref = snapshot.ref
}
// Initialize object for pushing to DB
init (message: String) {
self.postID = nil
// ...
self.ref = nil
}
As I mentioned before PostsService stores all necessary queries. ObservePostManager serves the first one and handles all possible errors and data result.
Try add / update / delete queries
After finishing with initializing let’s proceed with queries.
To create new post we must care about uniqueID. To achieve that we need to use next childByAutoId()
.
class func createNewPost(message: String, completion: (post: Post?) -> Void) {
let post = Post(message: message)
firManager?.postRef
.childByAutoId()
.setValue(post.toAnyObject()) { error, fb in
if error == nil {
post.postID = fb.key // setup ID
post.ref = fb // setup reference to the object in DB
completion(post: post)
} else {
completion(post: nil)
}
}
For updating of object you can use directly the ref
– reference of this object in DB, that’s why we defined this value var ref: FIRDatabaseReference?
in DataModel class. In my opinion, it is really cool fancy tool🐥.
So, let’s update our object. Just imagine that we created post which is not published yet and we are going to publish it using the following code:
class func publishPost(post: Post?, isPublish: Int) {
// Btw, you shouldn’t use all fields, only which you need exactly
let dic = [PostKeys.IsPublished.rawValue: isPublish]
post?.ref!.updateChildValues(dic as [NSObject : AnyObject])
}
And the last one is delete. Nothing special just use the ref
as we did before.
class func removePost(post: Post) {
post.ref?.removeValue()
}
Create Observers and data sync
Now I am going to describe a really cool feature – observers. Observer is probably the most common scheduler operator you’ll use. Observers allow us to track DataModel changes without making special queries, which we used to write before. Let’s quickly investigate observeEventType
it might prove its usefulness.
class func getPosts(completion: (postsArray: [Post]) -> Void) {
firManager?.postsRef
.observeEventType(.Value, withBlock: {
snapshot in
// ...
}, withCancelBlock: { error in
// ...
})
}
As you can see we created the query for getting post list, however, the key-words are observeEventType
and Value
.
ObserveEventType – means that we are going to track it
Value – means that we observe all value changes in what we try to observe (creating, removing, updating and so on…)
Sometimes we don’t need to observe child nodes and of course we can skip it. Decide yourself which ones you want to emit and which ones to skip.
Working with DateTime types
Additionally I would like to show you one of the possible ways to store DateTime values:
let postDate: AnyObject! // Use AnyObject type in Data Model
self.postDate = FIRServerValue.timestamp() // to pack object for pushing into DB
postDate = snapshot.value![PostKeys.PostDate.rawValue] as! NSTimeInterval // to unpack object from DB
Last but not the least – rules
We will consider the rules in the following articles in details, but the only cryptic thing here that we need to change is default security rules.
Replace them to this one { "rules": { ".read": true, ".write": true } }
Because, as soon as you try to execute any db query, you will get an error:
[FirebaseDatabase] setValue: or removeValue: at /post/POST_ID failed: permission_denied
Firebase is very simple and powerful service. And of course, there are a lot of things that I’ve not mentioned, but I hope my article will help to go through the basics.
You can read more about firebase at codelabs , guidelines and quickstart tutorials. Also if you have some questions, do not hesitate to ask me link or put your comments, I will be happy to help you.
All code samples you can find on GitHub