Switching From REST to Firebase

      5 Comments on Switching From REST to Firebase

Firebase is an online database. It’s called ‘Realtime Database’. But it’s much, much more than that. In this post, I’ll go through one of my apps and the process of converting it from my REST API to Firebase. Since this is such a massive topic, this article will cover setting up Firebase, fetching data from it and using authentication, so the database is not available to the public.

Intro

I recently published an app on the AppStore. It’s an app that connects to this blog and pulls down the JSON feed. In case you’re like me, and you didn’t know, modern versions of WordPress have a REST API built-in. This is great, you can create an app without any knowledge of backend, and you can do it relatively quickly. My only problem was, API calls were slow, very slow. I’m on a shared hosting plan, so I understand why, but still, it takes around 20 seconds to load the feed with abridged articles. When you pull to refresh in the app and have to wait for 20 seconds, you lose patience.

I could tell you I explored other options, but that would be a lie, I worked with Firebase before and knew I wanted to use it. I used dependency injection in my project, so switching to Firebase meant creating one new class, and injecting the new implementation in my view controller. It wouldn’t be a problem. It was so simple and performance gains were so drastic, that I wanted to share it with you.

Firebase Setup

Go to Firebase console and setup a new project:

Give it a good name and select a region. Your latencies and response times will vary based on the region you select. If you expect most of your users to come from the U.S. then select ‘United States’:

You’ll be greeted with a welcome screen and a button ‘Add Firebase to your iOS app’. Select it and follow the steps:

Google is using Cocoapods for Firebase, so setting it up should be easy enough.

Security

You can control who has access to your database. You can lock it completely, so you can only access it through the Firebase Console, you can open it to the public, or you can allow access to authenticated users (these are the basic rules, you can create a lot more complex rules). You can control read and write access to the database. I didn’t want anyone writing to the database, I would be adding content to it from the console, for the time being, and I wanted to lock the database so only authenticated users had access to it. In my case, that user would be the iOS app.

You can keep the database open to the public while you’re working on it if you really have to. But it’s a bad idea to go to production with these rules, for obvious reasons. One less obvious reason is if someone else is accessing your database they’re increasing your data transfer quota (10GB on a free plan at the moment).

Select the ‘Realtime Database’ from the menu, click on ‘Rules’ and put this in:

This will make your database read-only to authenticated users. You have a great little feature, the ‘Simulator’, where you can quickly test if have everything set correctly, let’s do that. Click on ‘Simulator’, select ‘Read’ and make sure ‘Authenticated’ is switched off when you run this, you should see a failure message:

Great, now let’s check if your authenticated users have access, flip the ‘Authenticated’ switch, and run it again:

If you see a green banner, all is good. You can test the write operations as well. Authenticated and non-authenticated write operations should both fail.

Our app won’t really work with users, there won’t be a login screen anywhere in the app. The app itself will be a user. We’ll have to create this user before we can use the database. Let’s do this now. Select ‘Authentication’ and set up your sign in method to Email/Password:

Hit save, and add your user:

 Remember the password 🙂 All that’s left is the data.

Data

WordPress API is returning a lot of data in the JSON and I only needed 6 attributes: post ID, date, title, excerpt, post URL and featured image URL. So I modelled my database to hold only this data. ‘Realtime Database’ is basically a dictionary and in my dictionary, I used post ID as the key and saved other values as a sub-dictionary, like so:

Now, all that’s left is the legwork of inputting all the data in the database. We’re all set to go. We can now start working on the iOS app. It’s less work than you might think.

iOS App Integration

Assuming you’re using Cocoapods, add these pods to your Podfile:

pod 'Firebase/Core'
pod 'FirebaseDatabase'
pod 'Firebase/Auth'

Run pod install from the terminal and open the workspace.

One last thing, before we can start using Firebase we have to configure it. In the AppDelegate have a method similar to this one, and call it from application didFinishLaunching:

private func setupFirebase() {
    FirebaseApp.configure()
    Database.database().isPersistenceEnabled = true
}

I enabled persistence on my shared database instance because I want the users to be able to use the app while offline. We’re all set. Let’s get the data and parse it.

Getting Data

I already had a class that was fetching data from the API and parsing it, the class was implementing a protocol ‘GetPostsUseCase’, so all I had to do was create a new class that implements the same protocol and inject it into my view controller. Here’s the protocol:

protocol GetPostsUseCase {
    var postsFactory: PostsProvider { get set }
    var posts: [WPPostItem] { get }
    
    func getPosts(onCompleted: (([WPPostItem]) -> ())?)
}

The class is actually quite small, let’s first see it, then we’ll go over it:

import Foundation
import FirebaseDatabase
import Firebase

class GetPostsFB: GetPostsUseCase {
    
    private enum Constants: String {
        case posts
    }
    
    private struct Credentials {
        static let Email = "dejan.agostini+firebasetest@gmail.com"
        static let Password = "Test1234"
    }
    
    var postsFactory: PostsProvider = PostsFactory()
    var posts: [WPPostItem] = []
    
    private let ref = Database.database().reference()
    
    func getPosts(onCompleted: (([WPPostItem]) -> ())?) {
        if Auth.auth().currentUser == nil {
            Auth.auth().signIn(withEmail: Credentials.Email,
                               password: Credentials.Password,
                               completion: { (user, error) in
                                
                                if error == nil {
                                    self.getFBPosts(onCompleted: onCompleted)
                                } else {
                                    onCompleted?([])
                                }
            })
        } else {
            getFBPosts(onCompleted: onCompleted)
        }
    }
    
    private func getFBPosts(onCompleted: (([WPPostItem]) -> ())?) {
        ref.child(Constants.posts.rawValue).observeSingleEvent(of: .value, with: { (snapshot) in
            
            guard let postsDict = snapshot.value as? Dictionary<String, Any> else {
                onCompleted?([])
                return
            }
            
            self.posts = self.postsFactory.posts(withDictionary: postsDict).sorted(by: { $0.date > $1.date })
            onCompleted?(self.posts)
            
        }) { (error) in
            print(error.localizedDescription)
        }
    }
    
}

In our ‘getPosts’ method we’re checking if the user is logged in, if not, we sign the user in with the email/password that we created earlier. If the login was successful we call the internal ‘getFBPosts’ method, otherwise, we return an empty array. You might want to return an error here or print an error in the console, I was just keeping it simple. If the user is logged in, we call the ‘getFBPosts’ method directly.

In the ‘Realtime Database’, we have a dictionary in the root of our database, called ‘posts’. We’ll query this dictionary by observing events on it. We could actively observe this dictionary, so we would get notifications every time values within it change, but we don’t want that. We just want to download a copy of data from it, since the data won’t be updated dynamically. So we observe a single event of ‘.value’ type. The callback will give us back a snapshot, ‘value’ property of the snapshot is the dictionary we created earlier (with post IDs as keys and sub-dictionaries). We cast it into a dictionary and parse the values using posts factory.

The factory method is simple enough:

    func posts(withDictionary dict: [String: Any?]) -> [WPPostItem] {
        
        return dict.flatMap { (key, value) -> WPPostItem? in
            guard
                let postID = Int(key),
                let postDict = value as? [String: Any?],
                let dateString = postDict[PostKey.date.rawValue] as? String,
                let date = PostsFactory.date(withString: dateString),
                let postTitle = postDict[PostKey.title.rawValue] as? String,
                let excerpt = postDict[PostKey.excerpt.rawValue] as? String,
                let postLink = postDict[PostKey.link.rawValue] as? String,
                let postImage = postDict[PostKey.imageURL.rawValue] as? String
            else {
                return nil
            }
            
            return WPPost(
                postID: postID,
                date: date,
                link: postLink,
                title: postTitle,
                excerpt: excerpt,
                imageURL: postImage
            )
        }
    }

Remember, the key is the post ID and the value is a sub-dictionary. And that’s it, we only have to switch implementations in our view controller, build and run and admire the simplicity. Here’s a screenshot:

The speed improvements are incredible and you get offline caching for free. In my case, the loading times decreased 10 times. From over 20 seconds to less than 2.

Conclusion

Firebase is great, it eliminates the need for a backend for simple apps, like my blog reader app. It’s fast and has a free tier, so it’ll cost you nothing to try out. It’s really easy to use as well.

In this post, I covered the very basics of using Firebase. It should be enough to get you started. Firebase is much, much more powerful. Its true power lies in the real-time updates. You can subscribe to listen to changes on a path (dictionary) and you’ll get notifications when the changes occur. So when any one of your users changes a dictionary, all of them will get that change! It supports file storage, push notifications, and cloud functions. You can actually execute JavaScript functions in Firebase. In one of the future articles, I’ll cover one of these topics.

I hope you found something useful in this article. If you did, please share it with friends 🙂 This time I have no code for you to share on GitHub because this project is in my private repo. I added full classes for you in the article, though, so you can see all of the code related to Firebase.

Have a nice day 🙂

Dejan.

More resources

5 thoughts on “Switching From REST to Firebase

    1. Dejan Agostini Post author

      Hi Renan,

      Thank you very much for your thoughts. The links you posted are very good articles and I believe everyone should read them.

      I’ve read the article and I agree with the author when he says that their case was unique. They were pushing firebase to the limits. If you’re an indie developer, or a team of two, I believe this is a good way to start an app, or to get your idea out there.

      You posted a second comment below and I agree with something the author said. Design your systems so you can swap implementations and don’t depend on a single provider. If you’re using Firebase and you decide to switch to anything else, you should have a flexible enough architecture to be able to swap those implementations out. I believe this article was demonstrating that, using dependency injection to swap out service implementations. I switched from REST to Firebase easily, so the opposite is true, you can switch from Firebase to REST.

      Thank you very much Renan for sharing and as always… Have a nice day 🙂
      Dejan.

      Reply
    1. Dejan Agostini Post author

      This is a famous article on Medium, I remember reading it a month ago. Their cost increased by 7000% as a result of an error in how Firebase was reporting data usage. You can read it in the post, the Firebase team contacted the author and resolved the issue.

      Reply
  1. Pingback: Using Firebase Cloud Functions | agostini.tech

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.