Cloud Firestore Android Codelab

Mantieni tutto organizzato con le raccolte Salva e classifica i contenuti in base alle tue preferenze.

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 le modifiche ai dati di Firestore in tempo reale
  • Usa l'autenticazione Firebase e le regole di sicurezza per proteggere i dati di Firestore
  • Scrivi complesse query Firestore

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 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, "Mangia amichevoli") e fai clic su Continua .

9d2f625aebcab6af.png

  1. Ti potrebbe essere 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

Eseguire 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 sulla tua macchina, puoi anche scaricare il codice direttamente da GitHub.

Aggiungi la configurazione di Firebase

  1. Nella console Firebase , seleziona Panoramica del progetto nella barra di navigazione a sinistra. Fare clic sul pulsante Android per selezionare la piattaforma. Quando viene richiesto il nome di un 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 spostalo nella cartella app/ del codice appena scaricato. Quindi fare clic su Avanti .

Importa il progetto

Apri Android Studio. Fare clic su File > Nuovo > Importa progetto e selezionare la cartella friendlyats-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 la CLI di Firebase . Se utilizzi macOS o Linux, puoi eseguire il seguente comando cURL:

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

Se stai usando Windows, leggi le istruzioni di installazione per ottenere un binario autonomo o per installare tramite npm .

Dopo aver installato la CLI, l'esecuzione di firebase --version dovrebbe riportare una versione 9.0.0 o successiva:

$ firebase --version
9.0.0

Accesso

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 progetto Firebase in precedenza.

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. 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 firebase emulators:start ad 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 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.

Sul 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à di debug . Quando compiliamo l'app in modalità di release , questa condizione sarà falsa.

Possiamo vedere che sta usando il useEmulator(host, port) per connettere Firebase SDK 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à di 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 Compila > 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 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 navigando su 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 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 di Firestore sono suddivisi in documenti, raccolte e sottoraccolte. Conserveremo ogni ristorante come documento in una raccolta di primo livello denominata "restaurants" . Per saperne di più 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 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 ottenendo un riferimento alla collezione "restaurants" . Le raccolte vengono create in modo implicito 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 ogni documento del ristorante.
  • 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 di nuovo 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 navigando su 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 è possibile sperimentare la modifica e l'eliminazione di questi dati senza conseguenze.

Congratulazioni, hai appena scritto 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 codice seguente 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 di futuri aggiornamenti in tempo reale. Poiché il nostro obiettivo finale è associare questi dati a un RecyclerView , è necessario creare una classe RecyclerView.Adapter per ascoltare i dati.

Aprire 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 su 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 il listener riceverà un evento ADDED per ogni nuovo documento. Poiché il set di risultati della query cambia nel tempo, il listener riceverà più eventi contenenti le modifiche. Ora finiamo di implementare il listener. Innanzitutto 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. Ordina e filtra i dati

L'app attualmente mostra i ristoranti più votati nell'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 di supporto che abbiamo creato per acquisire l'output della finestra di dialogo dei filtri. Cambieremo 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 in modo che corrispondano ai filtri indicati.

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 che contengono solo opzioni a basso prezzo:

a670188398c3c59.png

Se sei arrivato così lontano, ora hai creato un'app per la visualizzazione di consigli sui ristoranti completamente 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).

Raccolte e sottoraccolte

Finora abbiamo archiviato tutti i dati sui 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 pensare a una sottoraccolta 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 padre:

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

È possibile accedere e interrogare una sottoraccolta proprio come con una raccolta di primo livello, senza limitazioni di dimensioni o modifiche alle prestazioni. Puoi leggere di più sul modello di dati Firestore qui .

Scrittura di 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 condizioni di gara che potrebbero comportare dati obsoleti o errati.

Per garantire che le valutazioni vengano aggiunte correttamente, utilizzeremo una transazione per aggiungere le 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() vengono aggiunti listener all'attività per rispondere al risultato della transazione.

Ora esegui di nuovo l'app e fai clic su uno dei ristoranti, che dovrebbe visualizzare la schermata dei dettagli del ristorante. Fare 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 avvierà la transazione. Al termine della transazione, vedrai la tua recensione visualizzata di seguito e un aggiornamento del conteggio delle recensioni del ristorante:

f9e670f40bd615b0.png

Congratulazioni! Ora hai un'app per la recensione di ristoranti social, locale e mobile basata su Cloud Firestore. Ho sentito che sono molto popolari in questi giorni.

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 Security Rules .

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;
    }
  }
}

Cambiamo queste regole per prevenire accessi o modifiche ai dati indesiderati, apriamo il file firestore.rules e sostituiamo il contenuto con il seguente:

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 a un documento di un ristorante possono modificare solo le valutazioni, non il nome o altri dati immutabili. Le classificazioni 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 raccolte
  • Lettura e scrittura di dati
  • Ordinamento e filtraggio con le query
  • Sottoraccolte
  • Transazioni

Per saperne di più

Per continuare a conoscere Firestore, ecco alcuni buoni punti di partenza:

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 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 Email/Password dai provider nativi .

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

sign-in-providers.png

Firestore

Crea database

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

  1. Quando richiesto sulle regole di sicurezza, scegli di iniziare in modalità produzione , le aggiorneremo presto.
  2. Scegli la posizione del database che desideri utilizzare per la tua app. Tieni presente che la selezione di una posizione del database è una decisione permanente e per modificarla dovrai creare un nuovo progetto. Per ulteriori informazioni sulla scelta di una posizione di progetto, vedere 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 navigando nella scheda Regole nella console.

Distribuisci indici

L'app FriendlyEats ha un ordinamento e un filtraggio 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 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 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'app

Nei file util/FirestoreInitializer.kt e util/AuthInitializer.kt abbiamo configurato l'SDK Firebase per la connessione 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. Sostituisci temporaneamente BuildConfig.DEBUG con false ed esegui di nuovo l'app.

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