Firebase Database Tutorial for iOS iOS 14.12.2019

Firebase Realtime Database is a type of database that is cloud-hosted i.e. it runs on the cloud and it can be accessed from any platform i.e. it can be accessed from Android, iOS and Web at the same time. Firebase is easy to use, can store a ton of data, and you can observe data changes in real-time!

The data stored in the Firebase Realtime Database is JSON structured i.e. the entire database will be a JSON tree with multiple nodes. So, unlike SQL database, we don't have tables or records in the JSON tree.

The data present in the database is very important and you shouldn't give access to everyone to use the data present in your database. So, in order to do so, Firebase Realtime Database has some database configuration rules that can be used to provide different access to different users. Following are the database configuration rules:

  • Default. By default, the read and write access to your database is disabled and no one can read or write data from the database in this mode. But here, you can access the data of the database from the Firebase Console only.
{
  "rules": {
    ".read": false,
    ".write": false
  }
}
  • Public. By using public rules, anyone can change the data present in the database. This rule is generally used when you are testing your application and after testing the application you can set the rule to User only.
{
  "rules": {
    ".read": true,
    ".write": true
  }
}
  • User. In this rule, the user of your application can read and write in your database. You can authenticate your user using the Firebase Login and Authentication and after that, your user will have the access to your database.
{
  "rules": {
    "users": {
      "$uid": {
        ".read": "$uid === auth.uid",
        ".write": "$uid === auth.uid"
      }
    }
  }
}

Let's prepare account and create a project.

Open the Firebase Website and sign up for a free account. Then, log into your new account and go to your Firebase Dashboard.

Next, click Add Project. In the dialogs that appear, set a project name.

Finally, click Create Project. Then, in the screen that appears, choose Add Firebase to your iOS app.

Another dialog appears. Choose the following settings:

  • iOS Bundle ID: Input your app’s Bundle ID. You choose that when creating the Xcode project. It’s something like: me.proft.MyApp.
  • App Nickname: Anything, something like "My App"

Finally, click Register App. In the next screen that appears, click the big Download GoogleService-Info.plist button. You’ll now download a .plist file, so save it in a convenient location (like ~/Downloads).

Add the .plist file to your Xcode project. Drag-and-drop the file from Finder into Xcode, and add it to the Project Navigator. When a dialog appears, make sure to tick the checkbox for Copy items if needed, and tick the checkbox next to Target.

Finally, click Continue.

Install Firebase SDK

The easiest way to add third-party libraries to your project is with CocoaPods. CocoaPods is a package manager, and it helps you to manage your libraries, download new versions, and integrate them with Xcode.

Open the Podfile file, and add the following lines:

pod 'Firebase/Core'
pod 'Firebase/Auth'
pod 'Firebase/Database'
pod 'ObjectMapper', '~> 3.4'

Configure Firebase in AppDelegate. Start by making sure the Firebase module is imported.

import Firebase

Use the configure method in FirebaseApp inside the application:didFinishLaunchingWithOptions function to configure underlying Firebase services from your .plist file.

func application(_ application: UIApplication, 
    didFinishLaunchingWithOptions launchOptions: 
        [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    FirebaseApp.configure()
    return true
}

Creating a Connection to Firebase. Following establishes a connection to your Firebase database using the provided path.

let ref = Database.database().reference(withPath: "movies")
// or
let rootRef = Database.database().reference()
let itemsRef = rootRef.child("movies")

In order to work easier with the Firebase paths, and references, you create a Constants.swift file to hard-code the paths for the chat data.

Do this:

  1. Right-click on the project in Project Navigator and choose New File....
  2. Pick the Swift File template, name the file Constants.swift, add it to the Target, and save the file in your project folder.
  3. Finally, click Create.

Then, add the following Swift code to Constants.swift:

import Firebase

struct Constants {
    struct refs {
        static let databaseRoot = Database.database().reference()
        static let databaseMovies = databaseRoot.child("movies")
    }
}

Whenever you now need access to the reference for chat data, you can use:

Constants.refs.databaseMovies

Create model.

import Foundation
import ObjectMapper
import FirebaseDatabase

class Movie: Mappable, CustomStringConvertible {
    var oid: String?
    var title: String?
    var year: Int?

    public var description: String { return "Movie: oid=\(oid), title=\(title)" }

    init(oid: String?, title: String?, year: Int?) {
        self.oid = oid
        self.title = title
        self.year = year
    }

    func mapping(map: Map) {
        oid <- map["oid"]
        title <- map["title"]
        year <- map["year"]
    }
    required init?(map: Map) { }
}

extension BaseMappable {
    init?(snapshot: DataSnapshot) {
        guard let json = snapshot.value as? [String: Any] else {
            return nil
        }

        self.init(JSON: json)
    }
}

Or without ObjectMapper.

import Foundation
import Firebase

struct Movie {
    var title: String
    var year: Int

    var dictionary: [String: Any] {
        return [
            "title": title,
            "year": year
        ]
    }
}

extension Movie: DocumentSerializable {

    init?(dictionary: [String : Any]) {
        guard let owner = dictionary["owner"] as? String,
            let place = dictionary["place"] as? String,
            let time = dictionary["time"] as? Date,
            let title = dictionary["title"] as? String
            else { return nil }

        self.init(owner: owner, place: place, time: time, title: title)
    }
}

Save object. Use setValue(_:) to save data to the database. This method expects a Dictionary.

let rootRef = Database.database().reference()
let itemsRef = rootRef.child("movies")

func create() {
    let oid = itemsRef.childByAutoId().key!

    let movie = Movie(
        oid: oid,
        title: "Movie 1",
        year: 2009)

    itemsRef.child(oid).setValue(movie.toJSON())
}

You can update data using updateChildValues(_:) which passes a dictionary, to update Firebase. This method is different than setValue(_:) because it only applies updates, whereas setValue(_:) is destructive and replaces the entire value at that reference.

itemsRef.updateChildValues([
    "year": 2010
])

Retrieving Data. One of the great things Firebase can do is push real-time data to your app, with very little effort or code. It’s literally real-time: you change your data in Firebase, and your UI or data on your iPhone changes. You retrieve data in Firebase by attaching an asynchronous listener to a reference using observe(_:with:). This method takes two parameters: an instance of DataEventType and a closure.

The event type specifies what event you want to listen for. The code listens for a .value event type, which in turn listens for all types of changes to the data in your Firebase database — add, removed, and changed.

func load() {
    itemsRef.observe(.value, with: { snapshot in
        var items: [Movie] = []

        for child in snapshot.children {
            if let snapshot = child as? DataSnapshot,
                let item = Movie(snapshot: snapshot) {
                items.append(item)
            }
        }
        print(items)

    })
}

Sorting. To order the data by the year value you call queryOrdered(byChild:) on the Firebase reference, which takes a key to order by.

ref.queryOrdered(byChild: "year").observe(.value, with: { snapshot in
    ...
})

Useful links