Codelab Android di Cloud Firestore

1. Panoramica

Obiettivi

In questo codelab creerai un'app per consigliare ristoranti su Android supportata da Cloud Firestore. Imparerai come:

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

Prerequisiti

Prima di iniziare questo codelab assicurati di avere:

  • Android Studio Flamingo o versione successiva
  • Un emulatore Android con API 19 o successiva
  • Node.js versione 16 o successiva
  • Java versione 17 o successiva

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. Potrebbe esserti 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. Configurare il progetto di esempio

Scarica il codice

Esegui il comando seguente per clonare il codice di esempio per questo codelab. Questo creerà una cartella chiamata friendlyeats-android sul tuo computer:

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

Se non hai Git sul tuo computer, puoi anche scaricare il codice direttamente da GitHub.

Aggiungi la configurazione Firebase

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

73d151ed16016421.png

  1. Fai clic su Registra app e segui le istruzioni per scaricare il file google-services.json e spostarlo nella cartella app/ del codice appena scaricato. Quindi fare clic su Avanti .

Importa il progetto

Apri AndroidStudio. 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 la CLI di Firebase

Per prima cosa dovrai installare la CLI 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 installarlo tramite npm .

Dopo aver installato 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 del tuo progetto Firebase in precedenza.

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

5. Esegui l'app

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

Esegui gli emulatori

Nel tuo terminale dalla directory friendlyeats-android esegui firebase emulators:start per avviare gli emulatori 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 sul tuo computer! Assicurati di lasciare questo comando in esecuzione per il resto del codelab, la tua app Android dovrà connettersi agli emulatori.

Connetti 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 utilizzando BuildConfig per assicurarci di connetterci agli emulatori solo quando la nostra app è in esecuzione in modalità debug . Quando compiliamo l'app in modalità release questa condizione sarà falsa.

Possiamo vedere che sta utilizzando 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 essere sempre connessi 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 Costruisci > Ricostruisci progetto e assicurati che non vi siano errori rimanenti.

In Android Studio esegui l'app sul tuo emulatore Android. Inizialmente ti verrà presentata una schermata di "Accedi". Puoi utilizzare qualsiasi indirizzo email 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 appena creato:

Emulatore di autenticazione Firebase

Una volta completata la procedura di accesso dovresti vedere la schermata iniziale 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 Firestore sono suddivisi in documenti, raccolte e sottoraccolte. Memorizzeremo ogni ristorante come documento in una raccolta di primo livello chiamata "restaurants" . Per ulteriori informazioni sul modello dati Firestore, leggi i documenti e le raccolte nella documentazione .

A scopo dimostrativo, aggiungeremo funzionalità nell'app per creare dieci ristoranti casuali quando facciamo clic sul pulsante "Aggiungi elementi casuali" nel menu extra. 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 facendo riferimento alla collezione "restaurants" . Le raccolte vengono create implicitamente quando vengono aggiunti i documenti, quindi non è stato necessario creare la raccolta prima di scrivere i dati.
  • I documenti possono essere creati utilizzando le classi di dati Kotlin, che utilizziamo per creare ciascun documento del ristorante.
  • Il metodo add() aggiunge un documento a una raccolta con un ID generato automaticamente, quindi non abbiamo bisogno di specificare un ID univoco per ciascun 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% sul tuo computer. In effetti, il tuo progetto reale 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 su Firestore! Nel passaggio successivo impareremo come visualizzare questi dati nell'app.

7. Visualizza i dati da Firestore

In questo passaggio impareremo come recuperare i dati da Firestore e visualizzarli nella nostra app. Il primo passaggio 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 RecyclerView , dobbiamo creare una classe RecyclerView.Adapter per ascoltare i dati.

Apri la classe FirestoreAdapter , che è già stata parzialmente implementata. Innanzitutto, 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, l'ascoltatore riceverà più eventi contenenti le modifiche. Ora terminiamo l'implementazione dell'ascoltatore. 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 l'ascoltatore:

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

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

9e45f40faefce5d0.png

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

8. Ordina e filtra i dati

L'app attualmente mostra i ristoranti più apprezzati 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 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 creiamo un oggetto Query allegando le clausole where e orderBy per abbinare i filtri specificati.

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, hai creato un'app per la 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 aggiungeremo 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).

Collezioni e sottocollezioni

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 questa attività utilizzeremo una sottoraccolta. Puoi considerare una sottoraccolta come una raccolta allegata a un documento. Pertanto, 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 ed eseguire query su una sottoraccolta proprio come con una raccolta di livello superiore, non ci sono limitazioni di dimensioni o modifiche delle prestazioni. Puoi leggere ulteriori informazioni sul modello dati Firestore qui .

Scrittura dei dati in una transazione

Per aggiungere una Rating alla sottoraccolta corretta è sufficiente chiamare .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, esiste una serie di condizioni di gara che potrebbero risultare in 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 Task che rappresenta l'intera transazione. Nella funzione onRating() gli ascoltatori vengono aggiunti all'attività per rispondere al risultato della transazione.

Ora esegui nuovamente l'app e fai clic su uno dei ristoranti, che dovrebbe visualizzare 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

Premendo Invia si avvierà la transazione. Una volta completata la transazione, vedrai la tua recensione visualizzata di seguito e un aggiornamento del 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 facciamo a sapere che gli utenti possono solo leggere e scrivere i propri dati corretti? I database Firestore sono protetti da un file di configurazione chiamato 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 accessi 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 altri dati immutabili. Le valutazioni possono essere create solo se l'ID utente corrisponde all'utente che ha effettuato l'accesso, il che impedisce lo spoofing.

Per saperne di più sulle regole di sicurezza, consulta 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 ottimi punti da cui iniziare:

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

Facoltativo: distribuzione in produzione

Finora questa app ha utilizzato solo Firebase Emulator Suite. Se vuoi sapere come distribuire questa app in 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 Firebase vai alla sezione Autenticazione e fai clic su Inizia . Passare alla scheda Metodo di accesso e selezionare l'opzione E-mail/Password dai provider nativi .

Abilita il metodo di accesso tramite email/password e fai clic su Salva .

accesso-providers.png

Firestore

Crea banca dati

Passare alla sezione Database Firestore della console e fare clic su Crea database :

  1. Quando viene richiesto se le regole di sicurezza scelgono di iniziare in modalità produzione , aggiorneremo presto tali regole.
  2. Scegli la posizione del database che desideri utilizzare per la tua app. Tieni presente che la selezione della posizione del database è una decisione permanente e per modificarla dovrai 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 scritte in precedenza, esegui il comando seguente nella directory codelab:

$ firebase deploy --only firestore:rules

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

Distribuire gli indici

L'app FriendlyEats dispone di ordinamenti e filtri complessi che richiedono una serie di indici composti personalizzati. Questi possono essere creati manualmente nella console Firebase ma è più semplice scrivere le loro definizioni nel file firestore.indexes.json e distribuirle utilizzando la CLI 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 comando seguente:

$ firebase deploy --only firestore:indexes

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

Configura l'applicazione

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. Compila l'app in modalità di rilascio ed eseguila su un dispositivo.
  2. Sostituisci temporaneamente BuildConfig.DEBUG con false ed esegui nuovamente l'app.

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