Cloud Firestore Android Codelab

1. Panoramica

Obiettivi

In questo codelab creerai un'app di consigli sui ristoranti su Android supportata da Cloud Firestore. Imparerai come:

  • Leggi e scrivi dati su Firestore da un'app Android
  • Ascolta i cambiamenti nei dati di Firestore in tempo reale
  • Usa l'autenticazione Firebase e le regole di sicurezza per proteggere i dati di Firestore
  • Scrivi query Firestore complesse

Prerequisiti

Prima di iniziare questo codelab assicurati di avere:

  • Android Studio 4.0 o versioni successive
  • Un emulatore Android con API 19 o superiore
  • Node.js versione 10 o successiva
  • Java versione 8 o superiore

2. Crea un progetto Firebase

  1. Accedi alla console Firebase con il tuo account Google.
  2. Nella console Firebase , fai clic su Aggiungi progetto .
  3. Come mostrato nella schermata qui sotto, inserisci un nome per il tuo progetto Firebase (ad esempio, "Friendly Eats") e fai clic su Continua .

9d2f625aebcab6af.png

  1. È possibile che ti venga chiesto di abilitare Google Analytics, ai fini di questo codelab la tua selezione non ha importanza.
  2. Dopo circa un minuto, il tuo progetto Firebase sarà pronto. Fare clic su Continua .

3. Impostare il progetto di esempio

Scarica il codice

Eseguire il comando seguente per clonare il codice di esempio per questo codelab. Questo creerà una cartella chiamata friendlyeats-android sulla tua macchina:

$ git clone https://github.com/firebase/friendlyeats-android

Se non hai git sulla tua macchina, puoi anche scaricare il codice direttamente da GitHub.

Aggiungi configurazione Firebase

  1. Nella console Firebase , seleziona Panoramica progetto nel menu di navigazione sinistro. Fare clic sul pulsante Android per selezionare la piattaforma. Quando viene richiesto il nome di un pacchetto, utilizza com.google.firebase.example.fireeats

73d151ed16016421.png

  1. Fare clic su Registra app e seguire le istruzioni per scaricare il file google-services.json e spostarlo nell'app app/ cartella del codice appena scaricato. Quindi fare clic su Avanti .

Importa il progetto

Apri Android Studio. Fai clic su File > Nuovo > Importa progetto e seleziona la cartella friendlyeats-android .

4. Configura gli emulatori Firebase

In questo codelab utilizzerai Firebase Emulator Suite per emulare localmente Cloud Firestore e altri servizi Firebase. Ciò fornisce un ambiente di sviluppo locale sicuro, veloce e gratuito per creare la tua app.

Installa l'interfaccia a riga di comando di Firebase

Per prima cosa dovrai installare l' interfaccia a riga di comando di Firebase . Se utilizzi macOS o Linux, puoi eseguire il seguente comando cURL:

curl -sL https://firebase.tools | bash

Se utilizzi Windows, leggi le istruzioni di installazione per ottenere un file binario autonomo o per l'installazione tramite npm .

Una volta installata la CLI, l'esecuzione firebase --version dovrebbe riportare una versione 9.0.0 o successiva:

$ firebase --version
9.0.0

Login

Esegui firebase login per connettere la CLI al tuo account Google. Si aprirà una nuova finestra del browser per completare il processo di accesso. Assicurati di scegliere lo stesso account che hai utilizzato durante la creazione precedente del tuo progetto Firebase.

Dall'interno della cartella friendlyeats-android esegui firebase use --add per connettere il tuo progetto locale al tuo progetto Firebase. Segui le istruzioni per selezionare il progetto che hai creato in precedenza e, se ti viene chiesto di scegliere un alias, inserisci default .

5. Eseguire l'app

Ora è il momento di eseguire per la prima volta Firebase Emulator Suite e l'app FriendlyEats per Android.

Esegui gli emulatori

Nel tuo terminale dalla directory friendlyeats-android esegui firebase emulators:start per avviare gli emulatori di Firebase. Dovresti vedere log come questo:

$ firebase emulators:start
i  emulators: Starting emulators: auth, firestore
i  firestore: Firestore Emulator logging to firestore-debug.log
i  ui: Emulator UI logging to ui-debug.log

┌─────────────────────────────────────────────────────────────┐
│ ✔  All emulators ready! It is now safe to connect your app. │
│ i  View Emulator UI at http://localhost:4000                │
└─────────────────────────────────────────────────────────────┘

┌────────────────┬────────────────┬─────────────────────────────────┐
│ Emulator       │ Host:Port      │ View in Emulator UI             │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Authentication │ localhost:9099 │ http://localhost:4000/auth      │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Firestore      │ localhost:8080 │ http://localhost:4000/firestore │
└────────────────┴────────────────┴─────────────────────────────────┘
  Emulator Hub running at localhost:4400
  Other reserved ports: 4500

Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.

Ora hai un ambiente di sviluppo locale completo in esecuzione sulla tua macchina! Assicurati di lasciare questo comando in esecuzione per il resto del codelab, la tua app Android dovrà connettersi agli emulatori.

Collega l'app agli emulatori

Apri i file util/FirestoreInitializer.kt e util/AuthInitializer.kt in Android Studio. Questi file contengono la logica per connettere gli SDK Firebase agli emulatori locali in esecuzione sul tuo computer, all'avvio dell'applicazione.

Nel metodo create() della classe FirestoreInitializer , esamina questo pezzo di codice:

    // Use emulators only in debug builds
    if (BuildConfig.DEBUG) {
        firestore.useEmulator(FIRESTORE_EMULATOR_HOST, FIRESTORE_EMULATOR_PORT)
    }

Stiamo usando BuildConfig per assicurarci di connetterci agli emulatori solo quando la nostra app è in esecuzione in modalità debug . Quando compileremo l'app in modalità release questa condizione sarà falsa.

Possiamo vedere che sta usando il metodo useEmulator(host, port) per connettere l'SDK Firebase all'emulatore Firestore locale. In tutta l'app utilizzeremo FirebaseUtil.getFirestore() per accedere a questa istanza di FirebaseFirestore , quindi siamo sicuri di connetterci sempre all'emulatore Firestore durante l'esecuzione in modalità debug .

Esegui l'app

Se hai aggiunto correttamente il file google-services.json , il progetto dovrebbe ora essere compilato. In Android Studio fai clic su Crea > Ricostruisci progetto e assicurati che non vi siano errori rimanenti.

In Android Studio Esegui l'app sul tuo emulatore Android. All'inizio ti verrà presentata una schermata "Accedi". Puoi utilizzare qualsiasi e-mail e password per accedere all'app. Questo processo di accesso si sta connettendo all'emulatore di autenticazione Firebase, quindi non vengono trasmesse credenziali reali.

Ora apri l'interfaccia utente degli emulatori accedendo a http://localhost:4000 nel tuo browser web. Quindi fai clic sulla scheda Autenticazione e dovresti vedere l'account che hai appena creato:

Emulatore di autenticazione Firebase

Una volta completato il processo di accesso, dovresti vedere la schermata principale dell'app:

de06424023ffb4b9.png

Presto aggiungeremo alcuni dati per popolare la schermata iniziale.

6. Scrivi i dati su Firestore

In questa sezione scriveremo alcuni dati su Firestore in modo da poter popolare la schermata iniziale attualmente vuota.

L'oggetto modello principale nella nostra app è un ristorante (vedi model/Restaurant.kt ). I dati di Firestore sono suddivisi in documenti, raccolte e sottoraccolte. Memorizzeremo ogni ristorante come documento in una raccolta di primo livello chiamata "restaurants" . Per saperne di più sul modello di dati Firestore, leggi i documenti e le raccolte nella documentazione .

A scopo dimostrativo, aggiungeremo funzionalità nell'app per creare dieci ristoranti casuali quando faremo clic sul pulsante "Aggiungi elementi casuali" nel menu di overflow. Apri il file MainFragment.kt e sostituisci il contenuto nel metodo onAddItemsClicked() con:

    private fun onAddItemsClicked() {
        val restaurantsRef = firestore.collection("restaurants")
        for (i in 0..9) {
            // Create random restaurant / ratings
            val randomRestaurant = RestaurantUtil.getRandom(requireContext())

            // Add restaurant
            restaurantsRef.add(randomRestaurant)
        }
    }

Ci sono alcune cose importanti da notare sul codice sopra:

  • Abbiamo iniziato prendendo un riferimento alla collezione "restaurants" . Le raccolte vengono create implicitamente quando vengono aggiunti i documenti, quindi non era necessario creare la raccolta prima di scrivere i dati.
  • I documenti possono essere creati utilizzando le classi di dati Kotlin, che usiamo per creare ogni documento Restaurant.
  • Il metodo add() aggiunge un documento a una raccolta con un ID generato automaticamente, quindi non è stato necessario specificare un ID univoco per ogni ristorante.

Ora esegui nuovamente l'app e fai clic sul pulsante "Aggiungi elementi casuali" nel menu di overflow (nell'angolo in alto a destra) per richiamare il codice che hai appena scritto:

95691e9b71ba55e3.png

Ora apri l'interfaccia utente degli emulatori accedendo a http://localhost:4000 nel tuo browser web. Quindi fai clic sulla scheda Firestore e dovresti vedere i dati che hai appena aggiunto:

Emulatore di autenticazione Firebase

Questi dati sono locali al 100% sulla tua macchina. In effetti, il tuo vero progetto non contiene ancora nemmeno un database Firestore! Ciò significa che è sicuro sperimentare la modifica e l'eliminazione di questi dati senza conseguenze.

Congratulazioni, hai appena scritto i dati a Firestore! Nel passaggio successivo impareremo come visualizzare questi dati nell'app.

7. Visualizzare i dati da Firestore

In questo passaggio impareremo come recuperare i dati da Firestore e visualizzarli nella nostra app. Il primo passo per leggere i dati da Firestore è creare una Query . Apri il file MainFragment.kt e aggiungi il seguente codice all'inizio del metodo onViewCreated() :

        // Firestore
        firestore = Firebase.firestore

        // Get the 50 highest rated restaurants
        query = firestore.collection("restaurants")
            .orderBy("avgRating", Query.Direction.DESCENDING)
            .limit(LIMIT.toLong())

Ora vogliamo ascoltare la query, in modo da ottenere tutti i documenti corrispondenti e ricevere notifiche in tempo reale sui futuri aggiornamenti. Poiché il nostro obiettivo finale è associare questi dati a un RecyclerView , è necessario creare una classe RecyclerView.Adapter per ascoltare i dati.

Apri la classe FirestoreAdapter , che è già stata parzialmente implementata. Per prima cosa, facciamo in modo che l'adattatore implementi EventListener e definiamo la funzione onEvent in modo che possa ricevere aggiornamenti a una query Firestore:

abstract class FirestoreAdapter<VH : RecyclerView.ViewHolder>(private var query: Query?) :
        RecyclerView.Adapter<VH>(),
        EventListener<QuerySnapshot> { // Add this implements
    
    // ...

    // Add this method
    override fun onEvent(documentSnapshots: QuerySnapshot?, e: FirebaseFirestoreException?) {
        
        // Handle errors
        if (e != null) {
            Log.w(TAG, "onEvent:error", e)
            return
        }

        // Dispatch the event
        if (documentSnapshots != null) {
            for (change in documentSnapshots.documentChanges) {
                // snapshot of the changed document
                when (change.type) {
                    DocumentChange.Type.ADDED -> {
                        // TODO: handle document added
                    }
                    DocumentChange.Type.MODIFIED -> {
                        // TODO: handle document changed
                    }
                    DocumentChange.Type.REMOVED -> {
                        // TODO: handle document removed
                    }
                }
            }
        }

        onDataChanged()
    }
    
    // ...
}

Al caricamento iniziale l'ascoltatore riceverà un evento ADDED per ogni nuovo documento. Man mano che il set di risultati della query cambia nel tempo, il listener riceverà più eventi contenenti le modifiche. Ora finiamo di implementare il listener. Per prima cosa aggiungi tre nuovi metodi: onDocumentAdded , onDocumentModified e onDocumentRemoved :

    private fun onDocumentAdded(change: DocumentChange) {
        snapshots.add(change.newIndex, change.document)
        notifyItemInserted(change.newIndex)
    }

    private fun onDocumentModified(change: DocumentChange) {
        if (change.oldIndex == change.newIndex) {
            // Item changed but remained in same position
            snapshots[change.oldIndex] = change.document
            notifyItemChanged(change.oldIndex)
        } else {
            // Item changed and changed position
            snapshots.removeAt(change.oldIndex)
            snapshots.add(change.newIndex, change.document)
            notifyItemMoved(change.oldIndex, change.newIndex)
        }
    }

    private fun onDocumentRemoved(change: DocumentChange) {
        snapshots.removeAt(change.oldIndex)
        notifyItemRemoved(change.oldIndex)
    }

Quindi chiama questi nuovi metodi da onEvent :

    override fun onEvent(documentSnapshots: QuerySnapshot?, e: FirebaseFirestoreException?) {

        // Handle errors
        if (e != null) {
            Log.w(TAG, "onEvent:error", e)
            return
        }

        // Dispatch the event
        if (documentSnapshots != null) {
            for (change in documentSnapshots.documentChanges) {
                // snapshot of the changed document
                when (change.type) {
                    DocumentChange.Type.ADDED -> {
                        onDocumentAdded(change) // Add this line
                    }
                    DocumentChange.Type.MODIFIED -> {
                        onDocumentModified(change) // Add this line
                    }
                    DocumentChange.Type.REMOVED -> {
                        onDocumentRemoved(change) // Add this line
                    }
                }
            }
        }

        onDataChanged()
    }

Infine implementa il metodo startListening() per collegare il listener:

    fun startListening() {
        if (registration == null) {
            registration = query.addSnapshotListener(this)
        }
    }

Ora l'app è completamente configurata per leggere i dati da Firestore. Esegui di nuovo l'app e dovresti vedere i ristoranti che hai aggiunto nel passaggio precedente:

9e45f40faefce5d0.png

Ora torna all'interfaccia utente dell'emulatore nel tuo browser e modifica uno dei nomi dei ristoranti. Dovresti vederlo cambiare nell'app quasi istantaneamente!

8. Ordinare e filtrare i dati

L'app attualmente mostra i ristoranti più votati dell'intera raccolta, ma in una vera app per ristoranti l'utente vorrebbe ordinare e filtrare i dati. Ad esempio, l'app dovrebbe essere in grado di mostrare "I migliori ristoranti di pesce a Filadelfia" o "Pizza meno costosa".

Facendo clic sulla barra bianca nella parte superiore dell'app viene visualizzata una finestra di dialogo dei filtri. In questa sezione utilizzeremo le query Firestore per far funzionare questa finestra di dialogo:

67898572a35672a5.png

Modifichiamo il metodo onFilter() di MainFragment.kt . Questo metodo accetta un oggetto Filters che è un oggetto helper che abbiamo creato per acquisire l'output della finestra di dialogo dei filtri. Modificheremo questo metodo per costruire una query dai filtri:

    override fun onFilter(filters: Filters) {
        // Construct query basic query
        var query: Query = firestore.collection("restaurants")

        // Category (equality filter)
        if (filters.hasCategory()) {
            query = query.whereEqualTo(Restaurant.FIELD_CATEGORY, filters.category)
        }

        // City (equality filter)
        if (filters.hasCity()) {
            query = query.whereEqualTo(Restaurant.FIELD_CITY, filters.city)
        }

        // Price (equality filter)
        if (filters.hasPrice()) {
            query = query.whereEqualTo(Restaurant.FIELD_PRICE, filters.price)
        }

        // Sort by (orderBy with direction)
        if (filters.hasSortBy()) {
            query = query.orderBy(filters.sortBy.toString(), filters.sortDirection)
        }

        // Limit items
        query = query.limit(LIMIT.toLong())

        // Update the query
        adapter.setQuery(query)

        // Set header
        binding.textCurrentSearch.text = HtmlCompat.fromHtml(
            filters.getSearchDescription(requireContext()),
            HtmlCompat.FROM_HTML_MODE_LEGACY
        )
        binding.textCurrentSortBy.text = filters.getOrderDescription(requireContext())

        // Save filters
        viewModel.filters = filters
    }

Nello snippet sopra costruiamo un oggetto Query allegando le clausole where e orderBy per abbinare i filtri dati.

Esegui nuovamente l'app e seleziona il seguente filtro per mostrare i ristoranti a basso prezzo più popolari:

7a67a8a400c80c50.png

Ora dovresti vedere un elenco filtrato di ristoranti contenente solo opzioni a basso prezzo:

a670188398c3c59.png

Se sei arrivato fin qui, ora hai creato un'app di visualizzazione dei consigli sui ristoranti perfettamente funzionante su Firestore! Ora puoi ordinare e filtrare i ristoranti in tempo reale. Nelle prossime sezioni aggiungeremo recensioni ai ristoranti e regole di sicurezza all'app.

9. Organizzare i dati in sottoraccolte

In questa sezione aggiungeremo valutazioni all'app in modo che gli utenti possano recensire i loro ristoranti preferiti (o meno preferiti).

Raccolte e sottoraccolte

Finora abbiamo archiviato tutti i dati dei ristoranti in una raccolta di primo livello chiamata "ristoranti". Quando un utente valuta un ristorante, vogliamo aggiungere un nuovo oggetto Rating ai ristoranti. Per questo compito useremo una sottoraccolta. Puoi pensare a una raccolta secondaria come a una raccolta allegata a un documento. Quindi ogni documento del ristorante avrà una sottoraccolta di valutazioni piena di documenti di valutazione. Le sottoraccolte aiutano a organizzare i dati senza gonfiare i nostri documenti o richiedere query complesse.

Per accedere a una sottoraccolta, chiama .collection() sul documento principale:

val subRef = firestore.collection("restaurants")
        .document("abc123")
        .collection("ratings")

Puoi accedere e interrogare una sottoraccolta proprio come con una raccolta di primo livello, non ci sono limiti di dimensioni o modifiche alle prestazioni. Puoi leggere ulteriori informazioni sul modello di dati Firestore qui .

Scrivere dati in una transazione

L'aggiunta di una Rating alla sottoraccolta corretta richiede solo la chiamata .add() , ma dobbiamo anche aggiornare la valutazione media dell'oggetto Restaurant e il numero di valutazioni per riflettere i nuovi dati. Se utilizziamo operazioni separate per apportare queste due modifiche, ci sono una serie di race condition che potrebbero portare a dati obsoleti o errati.

Per garantire che le valutazioni vengano aggiunte correttamente, utilizzeremo una transazione per aggiungere valutazioni a un ristorante. Questa transazione eseguirà alcune azioni:

  • Leggi la valutazione attuale del ristorante e calcola quella nuova
  • Aggiungi la valutazione alla sottoraccolta
  • Aggiorna la valutazione media del ristorante e il numero di valutazioni

Apri RestaurantDetailFragment.kt e implementa la funzione addRating :

    private fun addRating(restaurantRef: DocumentReference, rating: Rating): Task<Void> {
        // Create reference for new rating, for use inside the transaction
        val ratingRef = restaurantRef.collection("ratings").document()

        // In a transaction, add the new rating and update the aggregate totals
        return firestore.runTransaction { transaction ->
            val restaurant = transaction.get(restaurantRef).toObject<Restaurant>()
                ?: throw Exception("Restaurant not found at ${restaurantRef.path}")

            // Compute new number of ratings
            val newNumRatings = restaurant.numRatings + 1

            // Compute new average rating
            val oldRatingTotal = restaurant.avgRating * restaurant.numRatings
            val newAvgRating = (oldRatingTotal + rating.rating) / newNumRatings

            // Set new restaurant info
            restaurant.numRatings = newNumRatings
            restaurant.avgRating = newAvgRating

            // Commit to Firestore
            transaction.set(restaurantRef, restaurant)
            transaction.set(ratingRef, rating)

            null
        }
    }

La funzione addRating() restituisce un Task che rappresenta l'intera transazione. Nella funzione onRating() i listener vengono aggiunti all'attività per rispondere al risultato della transazione.

Ora esegui di nuovo l'app e fai clic su uno dei ristoranti, che dovrebbe far apparire la schermata dei dettagli del ristorante. Fai clic sul pulsante + per iniziare ad aggiungere una recensione. Aggiungi una recensione selezionando un numero di stelle e inserendo del testo.

78fa16cdf8ef435a.png

Colpire Invia darà il via alla transazione. Al termine della transazione, vedrai la tua recensione visualizzata di seguito e un aggiornamento al conteggio delle recensioni del ristorante:

f9e670f40bd615b0.png

Congratulazioni! Ora disponi di un'app per recensioni di ristoranti social, locale e mobile basata su Cloud Firestore. Ho sentito che sono molto popolari di questi tempi.

10. Proteggi i tuoi dati

Finora non abbiamo considerato la sicurezza di questa applicazione. Come sappiamo che gli utenti possono solo leggere e scrivere i propri dati corretti? I database Firestore sono protetti da un file di configurazione denominato Regole di sicurezza .

Apri il file firestore.rules , dovresti vedere quanto segue:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      //
      // WARNING: These rules are insecure! We will replace them with
      // more secure rules later in the codelab
      //
      allow read, write: if request.auth != null;
    }
  }
}

Modifichiamo queste regole per impedire l'accesso o modifiche indesiderate ai dati, apriamo il file firestore.rules e sostituiamo il contenuto con quanto segue:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Determine if the value of the field "key" is the same
    // before and after the request.
    function isUnchanged(key) {
      return (key in resource.data)
        && (key in request.resource.data)
        && (resource.data[key] == request.resource.data[key]);
    }

    // Restaurants
    match /restaurants/{restaurantId} {
      // Any signed-in user can read
      allow read: if request.auth != null;

      // Any signed-in user can create
      // WARNING: this rule is for demo purposes only!
      allow create: if request.auth != null;

      // Updates are allowed if no fields are added and name is unchanged
      allow update: if request.auth != null
                    && (request.resource.data.keys() == resource.data.keys())
                    && isUnchanged("name");

      // Deletes are not allowed.
      // Note: this is the default, there is no need to explicitly state this.
      allow delete: if false;

      // Ratings
      match /ratings/{ratingId} {
        // Any signed-in user can read
        allow read: if request.auth != null;

        // Any signed-in user can create if their uid matches the document
        allow create: if request.auth != null
                      && request.resource.data.userId == request.auth.uid;

        // Deletes and updates are not allowed (default)
        allow update, delete: if false;
      }
    }
  }
}

Queste regole limitano l'accesso per garantire che i client apportino solo modifiche sicure. Ad esempio, gli aggiornamenti al documento di un ristorante possono modificare solo le valutazioni, non il nome o qualsiasi altro dato immutabile. Le valutazioni possono essere create solo se l'ID utente corrisponde all'utente che ha eseguito l'accesso, il che impedisce lo spoofing.

Per saperne di più sulle regole di sicurezza, visita la documentazione .

11. Conclusione

Ora hai creato un'app completa su Firestore. Hai appreso le funzionalità più importanti di Firestore, tra cui:

  • Documenti e collezioni
  • Lettura e scrittura dei dati
  • Ordinamento e filtraggio con query
  • Sottoraccolte
  • Transazioni

Saperne di più

Per continuare a conoscere Firestore, ecco alcuni buoni punti da cui iniziare:

L'app del ristorante in questo codelab era basata sull'applicazione di esempio "Friendly Eats". Puoi sfogliare il codice sorgente per quell'app qui .

Facoltativo: distribuire in produzione

Finora questa app ha utilizzato solo Firebase Emulator Suite. Se vuoi imparare come distribuire questa app a un vero progetto Firebase, vai al passaggio successivo.

12. (Facoltativo) Distribuisci la tua app

Finora questa app è stata interamente locale, tutti i dati sono contenuti nella Firebase Emulator Suite. In questa sezione imparerai come configurare il tuo progetto Firebase in modo che questa app funzioni in produzione.

Autenticazione Firebase

Nella console di Firebase vai alla sezione Autenticazione e fai clic su Inizia . Passare alla scheda Metodo di accesso e selezionare l'opzione Email/Password da Provider nativi .

Abilita il metodo di accesso Email/Password e fai clic su Salva .

provider-di-accesso.png

Firestore

Crea banca dati

Passa alla sezione Database Firestore della console e fai clic su Crea database :

  1. Quando viene richiesto di scegliere le regole di sicurezza per iniziare in modalità di produzione , aggiorneremo presto tali regole.
  2. Scegli la posizione del database che desideri utilizzare per la tua app. Si noti che la selezione di una posizione del database è una decisione permanente e per modificarla sarà necessario creare un nuovo progetto. Per ulteriori informazioni sulla scelta della posizione del progetto, consultare la documentazione .

Regole di distribuzione

Per distribuire le regole di sicurezza che hai scritto in precedenza, esegui il seguente comando nella directory codelab:

$ firebase deploy --only firestore:rules

Questo distribuirà il contenuto di firestore.rules al tuo progetto, che puoi confermare accedendo alla scheda Regole nella console.

Distribuisci indici

L'app FriendlyEats ha un ordinamento e un filtro complessi che richiedono una serie di indici composti personalizzati. Questi possono essere creati manualmente nella console di Firebase, ma è più semplice scrivere le relative definizioni nel file firestore.indexes.json e distribuirli utilizzando l'interfaccia a riga di comando di Firebase.

Se apri il file firestore.indexes.json vedrai che gli indici richiesti sono già stati forniti:

{
  "indexes": [
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "city", "mode": "ASCENDING" },
        { "fieldPath": "avgRating", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "category", "mode": "ASCENDING" },
        { "fieldPath": "avgRating", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "price", "mode": "ASCENDING" },
        { "fieldPath": "avgRating", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "city", "mode": "ASCENDING" },
        { "fieldPath": "numRatings", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "category", "mode": "ASCENDING" },
        { "fieldPath": "numRatings", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "price", "mode": "ASCENDING" },
        { "fieldPath": "numRatings", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "city", "mode": "ASCENDING" },
        { "fieldPath": "price", "mode": "ASCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "fields": [
        { "fieldPath": "category", "mode": "ASCENDING" },
        { "fieldPath": "price", "mode": "ASCENDING" }
      ]
    }
  ],
  "fieldOverrides": []
}

Per distribuire questi indici eseguire il seguente comando:

$ firebase deploy --only firestore:indexes

Tieni presente che la creazione dell'indice non è istantanea, puoi monitorare l'avanzamento nella console Firebase.

Configura l'app

Nei file util/FirestoreInitializer.kt e util/AuthInitializer.kt abbiamo configurato l'SDK Firebase per connettersi agli emulatori in modalità debug:

    override fun create(context: Context): FirebaseFirestore {
        val firestore = Firebase.firestore
        // Use emulators only in debug builds
        if (BuildConfig.DEBUG) {
            firestore.useEmulator(FIRESTORE_EMULATOR_HOST, FIRESTORE_EMULATOR_PORT)
        }
        return firestore
    }

Se desideri testare la tua app con il tuo vero progetto Firebase, puoi:

  1. Crea l'app in modalità di rilascio ed eseguila su un dispositivo.
  2. Sostituire temporaneamente BuildConfig.DEBUG con false ed eseguire nuovamente l'app.

Tieni presente che potrebbe essere necessario disconnettersi dall'app e accedere nuovamente per connettersi correttamente alla produzione.