= FirebaseFirestore.getInstance() val ref = firestore.collection("coll").document("id") ref.get() .addOnSuccessListener { snapshot -> // We have data! Now what? } .addOnFailureListener { exception -> // Oh, snap } How should I do async programming? Do I directly update my views here? What manages this singleton?
val firestore = FirebaseFirestore.getInstance() val ref = firestore.collection("stocks-live").document("HSTK") ref.addSnapshotListener { snapshot, exception -> if (snapshot != null) { val model = StockPrice( ticker = snapshot.id, price = snapshot.getDouble("price")!!.toFloat() ) } else if (exception != null) { TODO("This is just a simple code sample, ain't got time for this.") } }
val firestore = FirebaseFirestore.getInstance() val ref = firestore.collection("stocks-live").document("HSTK") ref.addSnapshotListener { snapshot, exception -> if (snapshot != null) { val model = StockPrice( ticker = snapshot.id, price = snapshot.getDouble("price")!!.toFloat() ) someTextView.text = model.ticker } else if (exception != null) { TODO("This is just a simple code sample, ain't got time for this.") } }
val firestore = FirebaseFirestore.getInstance() val ref = firestore.collection("stocks-live").document("HSTK") ref.addSnapshotListener { snapshot, exception -> if (snapshot != null) { val model = StockPrice( ticker = snapshot.id, price = snapshot.getDouble("price")!!.toFloat() ) someTextView.text = model.ticker throw PoorArchitectureException("let's rethink mixing data store and views”) } else if (exception != null) { TODO("This is just a simple code sample, ain't got time for this.") } }
updates. Genericized Subclasses must specify a type of object containing updates. Lifecycle-aware Handles component start, stop, and destroy states automatically.
DocumentSnapshot?, e: FirebaseFirestoreException?) { if (snap != null && snap.exists()) { val model = StockPrice( snap.id, snap.getDouble("price")!!.toFloat() ) // Here you go, all my admiring observers! Go update your UI! setValue(model) } }
DocumentSnapshot?, e: FirebaseFirestoreException?) { if (snap != null && snap.exists()) { val model = StockPrice( snap.id, snap.getDouble("price")!!.toFloat() ) // Here you go, all my admiring observers! Go update your UI! setValue(model) } else if (e != null) { TODO("You should handle errors. Do as I say, not as I do.") } }
constructor exposes Firestore implementation details. ◦ Remember: views should know nothing of data store. • LiveData loses its data on configuration change. ◦ Would rather have immediate LiveData results after configuration change. Two problems to resolve
in reconfigured activities Shared & Managed System manages the scope of instances, may be shared Lifecycle-aware Automatically cleaned up on final destroy
: ViewModel() { // Find a repository object using dependency injection private val repository: StockDataRepository = ... fun getStockPriceLiveData(ticker: String): StockPriceLiveData { } } // StockDataRepository knows where the data actually comes from interface StockDataRepository { fun getStockPriceLiveData(ticker: String): StockPriceLiveData }
: ViewModel() { // Find a repository object using dependency injection private val repository: StockDataRepository = ... fun getStockPriceLiveData(ticker: String): StockPriceLiveData { return repository.getStockPriceLiveData(ticker) } } // StockDataRepository knows where the data actually comes from interface StockDataRepository { fun getStockPriceLiveData(ticker: String): StockPriceLiveData }
fun getStockPriceLiveData(ticker: String): StockPriceLiveData } class FirestoreStockDataRepository : StockDataRepository { // Should use DI for this too private val firestore = FirebaseFirestore.getInstance() override fun getStockPriceLiveData(ticker: String): StockPriceLiveData { } }
fun getStockPriceLiveData(ticker: String): StockPriceLiveData } class FirestoreStockDataRepository : StockDataRepository { // Should use DI for this too private val firestore = FirebaseFirestore.getInstance() override fun getStockPriceLiveData(ticker: String): StockPriceLiveData { val ref = firestore.collection("stocks-live").document(ticker) return StockPriceLiveData(ref) } }
bubble up to the UI class DataOrException<T, E: Exception?>(val data: T?, val exception: E?) typealias StockPriceOrException = DataOrException<StockPrice, Exception> LiveData<StockPrice> => LiveData<StockPriceOrException> What about errors?
Query → RecyclerView support is inefficient and still needs work • Implementations for both Realtime Database and Firestore • Background sync with WorkManager • Backend implemented with TypeScript for node and Cloud Functions • Stay tuned for more! Lots more in the repo