RxFirebase

RxSwift extensions for Firebase

224
66
Swift

RxFirebase

CI Status
Version
License
Platform

Requirements

Xcode 9.0

Swift 4.0

Installation

RxFirebase is available through CocoaPods. To install
it, simply add the following lines to your Podfile:

pod 'RxFirebase/Firestore'
pod 'RxFirebase/RemoteConfig'
pod 'RxFirebase/Database'
pod 'RxFirebase/Storage'
pod 'RxFirebase/Auth'
pod 'RxFirebase/Functions'

Usage

import RxFirebase

Database

Basic write operation:

let ref = Database.database().reference()

ref.child("users")
    .child("1")
    .rx
    .setValue(["username": "Arnonymous"])
    .subscribe(onNext: { _ in
        print("Document successfully updated")
    }).disposed(by: disposeBag)
    
// https://firebase.google.com/docs/database/ios/read-and-write#basic_write

Listen for value events:

let ref = Database.database().reference()

ref.child("users")
    .child("1")
    .rx
    .observeEvent(.value)
    .subscribe(onNext: { snapshot in
        print("Value:\(snapshot.value)")
    }).disposed(by: disposeBag)
    
// https://firebase.google.com/docs/database/ios/read-and-write#listen_for_value_events

Read data once:

let ref = Database.database().reference()

ref.child("users")
    .child("1")
    .rx
    .observeSingleEvent(.value)
    .subscribe(onNext: { snapshot in
        print("Value:\(snapshot.value)")
    }).disposed(by: disposeBag)
    
// https://firebase.google.com/docs/database/ios/read-and-write#read_data_once

Update specific fields:

let ref = Database.database().reference()

let childUpdates = ["/posts/\(key)": post,
                    "/user-posts/\(userID)/\(key)/": post]
ref.rx.updateChildValues(childUpdates)
    .subscribe(onNext: { _ in
        // Success
    }).disposed(by: disposeBag)

// https://firebase.google.com/docs/database/ios/read-and-write#update_specific_fields

Delete data:

let ref = Database.database().reference()

ref.rx.removeValue()
    .subscribe(onNext: { _ in
        // Success
    }).disposed(by: disposeBag)
    
// https://firebase.google.com/docs/database/ios/read-and-write#delete_data

Save data as transactions

let ref = Database.database().reference()

ref.rx.runTransactionBlock { currentData in
        // TransactionResult
    }.subscribe(onNext: { _ in
        // Success
    }).disposed(by: disposeBag)
    
// https://firebase.google.com/docs/database/ios/read-and-write#save_data_as_transactions

Firestore

Setting data:

let db = Firestore.firestore()

// Add a new document in collection "cities"
db.collection("cities")
    .document("SF")
    .rx
    .setData([
        "name": "San Francisco",
        "state": "CA",
        "country": "USA",
        "capital": false,
        "population": 860000
        ]).subscribe(onError: { error in
            print("Error setting data: \(error)")
        }).disposed(by: disposeBag)
       
// Add a new document with a generated id.
db.collection("cities")
    .rx
    .addDocument(data: [
        "name": "San Francisco",
        "state": "CA",
        "country": "USA",
        "capital": false,
        "population": 860000
        ]).subscribe(onNext: { ref in
            print("Document added with ID: \(ref.documentID)")
        }, onError: { error in
            print("Error adding document: \(error)")
        }).disposed(by: disposeBag)
        
// Set the "capital" field of the city 'SF'
db.collection("cities")
    .document("SF")
    .rx
    .updateData([
        "capital": true
        ]).subscribe(onNext: {
            print("Document successfully updated")
        }, onError: { error in
            print("Error updating document: \(error)")
        }).disposed(by: disposeBag)
        
// https://firebase.google.com/docs/firestore/manage-data/add-data

Get a document:

let db = Firestore.firestore()

db.collection("cities")
    .document("SF")
    .rx
    .getDocument()
    .subscribe(onNext: { document in
        if let document = document {
            print("Document data: \(document.data())")
        } else {
            print("Document does not exist")
        }
    }, onError: { error in
        print("Error fetching snapshots: \(error)")
    }).disposed(by: disposeBag)
    
// https://firebase.google.com/docs/firestore/query-data/get-data

Get Realtime Updates:

let db = Firestore.firestore()

// Document
db.collection("cities")
    .document("SF")
    .rx
    .listen()
    .subscribe(onNext: { document in
        print("Current data: \(document.data())")
    }, onError: { error in
        print("Error fetching snapshots: \(error)")
    }).disposed(by: disposeBag)
    
// Collection
db.collection("cities")
    .rx
    .listen()
    .subscribe(onNext: { snapshot in
        snapshot.documentChanges.forEach { diff in
            if (diff.type == .added) {
                print("New city: \(diff.document.data())")
            }
            if (diff.type == .modified) {
                print("Modified city: \(diff.document.data())")
            }
            if (diff.type == .removed) {
                print("Removed city: \(diff.document.data())")
            }
        }
    }, onError: { error in
        print("Error fetching snapshots: \(error)")
    }).disposed(by: disposeBag)

// https://firebase.google.com/docs/firestore/query-data/listen

Batched writes:

let db = Firestore.firestore()

// Get new write batch
let batch = db.batch()

// Update the population of 'SF'
let sfRef = db.collection("cities").document("SF")
batch.updateData(["population": 1000000 ], forDocument: sfRef)

// Commit the batch
batch.rx
    .commit()
    .subscribe(onNext: {
        print("Batch write succeeded.")
    }, onError: { error in
        print("Error writing batch \(error)")
    }).disposed(by: disposeBag)
    
// https://firebase.google.com/docs/firestore/manage-data/transactions

Transactions:

let db = Firestore.firestore()
let sfReference = db.collection("cities").document("SF")

db.rx.runTransaction { transaction in
    let sfDocument = try transaction.getDocument(sfReference)
    
    guard let oldPopulation = sfDocument.data()?["population"] as? Int else {
        let error = NSError(
            domain: "AppErrorDomain",
            code: -1,
            userInfo: [
                NSLocalizedDescriptionKey: "Unable to retrieve population from snapshot \(sfDocument)"
            ]
        )
        throw error
    }
    
    transaction.updateData(["population": oldPopulation + 1], forDocument: sfReference)
    return nil
    }.subscribe(onNext: { _ in
        print("Transaction successfully committed!")
    }, onError: { error in
        print("Transaction failed: \(error)")
    }).disposed(by: disposeBag)
    
    // https://firebase.google.com/docs/firestore/manage-data/transactions

RemoteConfig

Fetch:

// TimeInterval is set to expirationDuration here, indicating the next fetch request will use
// data fetched from the Remote Config service, rather than cached parameter values, if cached
// parameter values are more than expirationDuration seconds old. See Best Practices in the
// README for more information.
RemoteConfig.remoteConfig()
    .rx
    .fetch(withExpirationDuration: TimeInterval(expirationDuration), activateFetched: true)
    .subscribe(onNext: { status in
        print("Config fetched! with success:\(status == .success)")
    }, onError: { error in
        print("Error: \(error)")
    }).disposed(by: disposeBag)

    // https://firebase.google.com/docs/remote-config/ios

Storage

Upload:


let reference = Storage.storage()
    .reference(forURL: "\(your_firebase_storage_bucket)/images/space.jpg")
    .rx
    
let data: Data // Upload data
reference.putData(data)
    .subscribe(onNext: { metadata in
        // Success
    }, onError: { error in
        // Uh-oh, an error occurred!
    }).disposed(by: disposeBag)
    

let fileURL: URL // Upload file
reference.putFile(from: fileURL)
    .subscribe(onNext: { metadata in
        // Success
    }, onError: { error in
        // Uh-oh, an error occurred!
    }).disposed(by: disposeBag)

Observe events:

let reference = Storage.storage()
    .reference(forURL: "\(your_firebase_storage_bucket)/images/space.jpg")
    .rx

let fileURL: URL // Upload file
let uploadTask = reference.putFile(from: fileURL)

// Listen for state changes
uploadTask.rx.observe(.progress)
    .subscribe(onNext: { snapshot in
        // Upload reported progress
        let percentComplete = 100.0 * Double(snapshot.progress!.completedUnitCount)
        / Double(snapshot.progress!.totalUnitCount)
    }, onError: { error in
        // Uh-oh, an error occurred!
    }).disposed(by: disposeBag)

Download:

let reference = Storage.storage()
    .reference(forURL: "\(your_firebase_storage_bucket)/images/space.jpg")
    .rx

// Download in memory with a maximum allowed size of 1MB (1 * 1024 * 1024 bytes)
reference.getData(maxSize: 1 * 1024 * 1024)
    .subscribe(onNext: { data in
        // Data for "images/space.jpg" is returned
    }, onError: { error in
        // Uh-oh, an error occurred!
    }).disposed(by: disposeBag)
    
    
    
// Create local filesystem URL
let localURL = URL(string: "path/to/image")!
    
// Download to the local filesystem
reference.write(toFile: localURL)
    .subscribe(onNext: { data in
        // Local file URL for "images/space.jpg" is returned
    }, onError: { error in
        // Uh-oh, an error occurred!
    }).disposed(by: disposeBag)

URL:

let reference = Storage.storage()
    .reference(forURL: "\(your_firebase_storage_bucket)/images/space.jpg")
    .rx
    
// Fetch the download URL
reference.downloadURL()
    .subscribe(onNext: { url in
        // Get the download URL for 'images/space.jpg'
    }, onError: { error in
        // Handle any errors
    }).disposed(by: disposeBag)

Metadata:

let reference = Storage.storage()
    .reference(forURL: "\(your_firebase_storage_bucket)/images/space.jpg")
    .rx
    
// Create file metadata to update
let newMetadata = StorageMetadata()
    
// Update metadata properties
reference.updateMetadata(newMetadata)
    .subscribe(onNext: { metadata in
        // Updated metadata for 'images/space.jpg' is returned
    }, onError: { error in
        // Uh-oh, an error occurred!
    }).disposed(by: disposeBag)
    
    
// Get metadata properties
reference.getMetadata()
    .subscribe(onNext: { metadata in
        // Metadata now contains the metadata for 'images/space.jpg'
    }, onError: { error in
        // Uh-oh, an error occurred!
    }).disposed(by: disposeBag)

Delete:

let reference = Storage.storage()
    .reference(forURL: "\(your_firebase_storage_bucket)/images/space.jpg")
    .rx
    
// Delete the file
reference.delete()
    .subscribe(onNext: {
        // File deleted successfully
    }, onError: { error in
        // Uh-oh, an error occurred!
    }).disposed(by: disposeBag)

Auth

Create:

let auth = Auth.auth()
    
// Create a password-based account
auth.rx.createUser(withEmail: "[email protected]", password: "1q2w3e4r")
    .subscribe(onNext: { authResult in
        // User signed in
    }, onError: { error in
        // Uh-oh, an error occurred!
    }).disposed(by: disposeBag)

// https://firebase.google.com/docs/auth/ios/password-auth

Sign In:

let auth = Auth.auth()
    
// Sign in a user with an email address and password
auth.rx.signIn(withEmail: "[email protected]", password: "1q2w3e4r")
    .subscribe(onNext: { authResult in
        // User signed in
    }, onError: { error in
        // Uh-oh, an error occurred!
    }).disposed(by: disposeBag)

// https://firebase.google.com/docs/auth/ios/password-auth

User

Update Email:

let user = Auth.auth().currentUser?
    
// Set a user's email address
user.rx.updateEmail(to: "[email protected]")
    .subscribe(onNext: {
        // Completed updating Email
    }, onError: { error in
        // Uh-oh, an error occurred!
    }).disposed(by: disposeBag)

// https://firebase.google.com/docs/auth/ios/manage-users

Delete:

let user = Auth.auth().currentUser?

// Delete a user
user.rx.delete()
    .subscribe(onNext: {
        // User deleted
    }, onError: { error in
        // Uh-oh, an error occurred!
    }).disposed(by: disposeBag)

// https://firebase.google.com/docs/auth/ios/manage-users

Functions

let functions = Functions.functions()
let request = functions.httpsCallable("functionName").rx

request
    .call(["parameter": "value"])
    .subscribe(onNext: { result in
        print("response:\(result)")
    }, onError: { error in
        print("error:\(error)")
    }).disposed(by: disposeBag)
    
    // https://firebase.google.com/docs/functions/callable#call_the_function

License

This library belongs to RxSwiftCommunity.

RxFirebase is available under the MIT license. See the LICENSE file for more info.