Should the Firebase Object Be a Singleton in Swift

should the firebase object be a singleton in swift?

You don't need to make a singleton. Behind the scenes, Firebase manages a single connection and will dedupe appropriately if you have multiple Firebase objects or even if you have multiple observers at a single location. If you create a new Firebase object per view, so long as it's using the same base url, it will still use the same connection to the server.

While you don't need to manage how many Firebase objects you have, you should manage your observers. These do not get removed between views. To remove observers, you can use the FirebaseHandle returned by the observeEventType methods with the removeObserverWithHandle: method or call removeAllObservers. Note that both these methods require you call them at the same url location as the place you attached the observer (although it need not be the same object, just the same url). If you don't remove the observers, you might see them triggering from a view you left because someone else is changing the data. You can read more in the docs under Detaching Blocks.

This is all true for Swift or Objective-C.

Referencing a single instance of a class for entire application (Firebase)

Have a singleton manager class for the database interaction. In that class, you can have a property of type [String: CarRepository]. The String key is the path to your document. This way you can guarantee only 1 object per path.

Whenever you think of something that needs to be unique and live with your application, think of singleton.

How can I get a variable declared in a class in another class?

You could maybe make a singleton class for managing Firebase methods. I do something similar whenever building apps with Firebase. This lets you reference values and use methods related to Firebase globally, across multiple classes. Before I adopted this method, I found myself rewriting the same code for uploading objects to firebase. A singleton will let you create reusable code, including storage of a "last key" set in this global class.

class FirebaseManager {

//You've probably seen something similar to this "shared" in other Apple frameworks
//Maybe URLSession.shared or FileManager.default or UserDefaults.standard or SKPaymentQueue.default() or UIApplication.shared
static var shared = FirebaseManager()

func createEvent(name: String, timeStamp: [String:String], uid: String) {
let postNameObject = [
"EventName": name,
"timestamp": timeStamp,
"userID": uid
] as [String:Any]

postName.setValue(postNameObject, withCompletionBlock: { error, ref in
if error == nil {
self.dismiss(animated: true, completion: nil)
} else {
//Do something
}
})
let childautoID = postName.key

//Whatever else you need to do in the function below here...

}

}

Usage:

class SomeViewController: UIViewController {

@IBOutlet var nameTextField: UITextField!

//Some code...

@IBAction func createEventButtonPressed(_ sender: Any) {
FirebaseManager.shared.createEvent(name: nameTextField.text, timeStamp: [".sv":"timestamp"], uid: Auth.auth().currentUser!.uid)
}

//Some more code...
}

Similarly, you could add a value called lastKey of type String in our FirebaseManager class. Note the variable we add to the top of our FirebaseManager class:

class FirebaseManager {

var lastKey: String!

static var shared = FirebaseManager()

func createEvent(name: String, timeStamp: [String:String], uid: String) {
let postNameObject = [
"EventName": name,
"timestamp": timeStamp,
"userID": uid
] as [String:Any]

postName.setValue(postNameObject, withCompletionBlock: { error, ref in
if error == nil {
self.dismiss(animated: true, completion: nil)
} else {
//Do something
}
})
let childautoID = postName.key

//Whatever else you need to do in the function below here...

}

}

Similarly, we can set this value in one view controller:

class ViewControllerA: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
FirebaseManager.shared.lastKey = "aBcDeFg9876543210"
}
}

And grab this value in another view controller that loads following the previous view controller:

class ViewControllerB: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print(FirebaseManager.shared.lastKey)
}
}

Prints: aBcDeFg9876543210

This is the beauty of the static keyword. You can learn more about creating singleton classes here: https://cocoacasts.com/what-is-a-singleton-and-how-to-create-one-in-swift

Firestore Disable Cache - Swift Singleton

Firestore can be tricky to configure with the singleton pattern if you aren’t careful about order of execution. I would avoid the singleton pattern with Firestore but if you want it to work, here is one way:

class HCFireStoreService {
static let shared = HCFireStoreService()
private let db = Firestore.firestore()

private init() {}

func configure() {
let settings = FirestoreSettings()
settings.isPersistenceEnabled = false
db.settings = settings
}

func someMethod() {
db.document("yyy/xxx").getDocument { (snapshot, error) in
print("xxx")
}
}
}

To get this to work, you must instantiate this class (first use of the shared instance) after you've configured Firebase and before you've interfaced with the database. Therefore, if you're configuring Firestore in the App Delegate, then simply configure the singleton thereafter and then you are free to use its methods.

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

HCFireStoreService.shared.someMethod()

Where's the best place to call methods that interact with my database?

A couple of thoughts:

  1. Rather than accessing the singleton directly with, DatabaseManager.shared.update(for:), I might instead have a property for the database manager, initialize/inject it with the DatabaseManager.shared, and have whatever needs to interact with the database use that reference, e.g., dataManager.update(for:). The goal would be to allow your unit tests to mock a database manager if and when necessary.

  2. I would not be inclined to have a view controller interact directly with the DatabaseManager. Many of us consider the view controller, which interacts directly with UIKit/AppKit objects, as part of the broader “V” of MVC/MVP/MVVM/whatever. We’d often extricate business logic (including interaction with the database manager) out of the view controller.

    I personally wouldn’t bury it under the User object, either. I’d put it in an extension of the database manager, and called from the view model, the presenter, or whatever you personally want to call that object with the business logic.

Firebase reference on iOS

Firebase references are lightweight objects. The Firebase SDK keeps a single connection to the server that all references re-use. So you can keep many references without introducing network problems.

Updating label if value in singleton changes

You can do either of these:-

  • Communicate between the singleton and your class with delegate-protocol method , fire the delegate method in the class whenever your repo changes and update your label.

  • Open a different thread in your network link for the user's reputation in the viewController itself:-

    override func viewDidLoad() {
    super.viewDidLoad()

    FIRDatabase.database().reference().child("users").child(FIRAuth.auth()!.currentUser!.uid).child("reputation").observe(.childChanged, with: {(Snapshot) in

    print(Snapshot.value!)
    //Update yor label
    })

    which will get called every time the value of reputation changes.



Related Topics



Leave a reply



Submit