sql >> Databasteknik >  >> RDS >> Database

Komma igång med Cloud Firestore för iOS

Mobilkodare har utnyttjat Googles Mobile Backend as a Service (MBaaS)-plattform Firebase Realtime Database under många år, och hjälpt dem att fokusera på att bygga funktioner för sina appar utan att behöva oroa sig för back-end-infrastrukturen och databasen. Genom att göra det enkelt att lagra och bevara data i molnet och ta hand om autentisering och säkerhet, tillåter Firebase kodare att fokusera på klientsidan.

Förra året tillkännagav Google ännu en back-end-databaslösning, Cloud Firestore, byggd från grunden med löftet om större skalbarhet och intuitivitet. Detta skapade dock en viss förvirring om dess plats i förhållande till Googles redan befintliga flaggskeppsprodukt, Firebase Realtime Database. Denna handledning kommer att beskriva skillnaderna mellan de två plattformarna och de distinkta fördelarna med var och en. Du kommer att lära dig hur du arbetar med Firestore-dokumentreferenser, såväl som att läsa, skriva, uppdatera och radera data i realtid, genom att bygga en enkel påminnelseapp.

Syften med denna handledning

Den här självstudien kommer att exponera dig för Cloud Firestore. Du lär dig hur du använder plattformen för databasbeständighet och synkronisering i realtid. Vi tar upp följande ämnen:

  • vad Cloud Firestore är
  • Firestore-datamodellen
  • konfigurera Cloud Firestore
  • skapa och arbeta med Cloud Firestore-referenser
  • läsa data i realtid från Cloud Firestore
  • skapa, uppdatera och ta bort data
  • filtrering och sammansatta frågor

Antagen kunskap

Den här handledningen förutsätter att du har haft en viss exponering för Firebase och en bakgrund som utvecklats med Swift och Xcode.

Vad är Cloud Firestore?

Precis som Firebase Realtime Database förser Firestore mobil- och webbutvecklare med en molnlösning över flera plattformar för att bevara data i realtid, oavsett nätverkslatens eller internetanslutning, samt sömlös integrering med Google Cloud Platform-produkterna. Tillsammans med dessa likheter finns det distinkta fördelar och nackdelar som skiljer den ena från den andra.

Datamodell

På en grundläggande nivå lagrar Realtime Database data som ett stort, monolitiskt, hierarkiskt JSON-träd, medan Firestore organiserar data i dokument och samlingar, såväl som undersamlingar. Detta kräver mindre denormalisering. Att lagra data i ett JSON-träd har fördelarna med enkelhet när det gäller att arbeta med enkla datakrav; men det blir mer besvärligt i skala när man arbetar med mer komplex hierarkisk data.

Offlinesupport

Båda produkterna erbjuder offlinestöd, aktivt cachelagrar data i köer när det finns latent eller ingen nätverksanslutning – synkroniserar lokala ändringar tillbaka till baksidan när det är möjligt. Firestore stöder offlinesynkronisering för webbappar utöver mobilappar, medan Realtime Database endast möjliggör mobilsynkronisering.

Frågor och transaktioner 

Realtime Database stöder endast begränsade sorterings- och filtreringsmöjligheter – du kan bara sortera eller filtrera på en egenskapsnivå, men inte båda, i en enda fråga. Frågorna är också djupa, vilket innebär att de returnerar ett stort underträd med resultat. Produkten stöder endast enkla skriv- och transaktionsoperationer som kräver en komplettering av återuppringning.

Firestore, å andra sidan, introducerar indexfrågor med sammansatt sortering och filtrering, så att du kan kombinera åtgärder för att skapa kedjefilter och sortering. Du kan också utföra ytliga frågor som returnerar undersamlingar i stället för hela samlingen du skulle få med Realtime Database. Transaktioner är atomära till sin natur, oavsett om du skickar en batchoperation eller enstaka, med transaktioner som upprepas automatiskt tills de avslutas. Dessutom stöder Realtime Database endast enskilda skrivtransaktioner, medan Firestore erbjuder batchoperationer atomärt.

Prestanda och skalbarhet

Realtidsdatabasen är, som du kan förvänta dig, ganska robust och har låg latens. Databaser är dock begränsade till enskilda regioner, beroende på zontillgänglighet. Firestore, å andra sidan, rymmer data horisontellt över flera zoner och regioner för att säkerställa verklig global tillgänglighet, skalbarhet och tillförlitlighet. Faktum är att Google har lovat att Firestore kommer att vara mer pålitlig än Realtime Database.

En annan brist i realtidsdatabasen är begränsningen till 100 000 samtidiga användare (100 000 samtidiga anslutningar och 1 000 skrivningar/sekund i en enda databas) varefter du skulle behöva klippa din databas (dela upp din databas i flera databaser) för att stödja fler användare . Firestore skalar automatiskt över flera instanser utan att du behöver ingripa.

Designad från grunden med skalbarhet i åtanke, har Firestore en ny schematisk arkitektur som replikerar data över flera regioner, tar hand om autentisering och hanterar andra säkerhetsrelaterade frågor, allt inom sin klientsida SDK. Dess nya datamodell är mer intuitiv än Firebases, liknar mer andra jämförbara NoSQL-databaslösningar som MongoDB, samtidigt som den ger en mer robust frågemotor.

Säkerhet 

Slutligen, Realtime Database, som du vet från våra tidigare tutorials, hanterar säkerheten genom kaskadregler med separata valideringsutlösare. Detta fungerar med Firebase Database Rules och validerar din data separat. Firestore, å andra sidan, tillhandahåller en enklare men kraftfullare säkerhetsmodell som drar fördel av Cloud Firestore Security Rules och Identity and Access Management (IAM), med undantag för datavalidering automatiskt.

  • MobilutvecklingFirebase-säkerhetsreglerChike Mgbemena

Firestore-datamodellen

Firestore är en NoSQL-dokumentbaserad databas, som består av samlingar av dokument, som var och en innehåller data. Eftersom det är en NoSQL-databas kommer du inte att få tabeller, rader och andra element som du hittar i en relationsdatabas, utan istället uppsättningar av nyckel/värdepar som du hittar i dokument.

Du skapar dokument och samlingar implicit genom att tilldela data till ett dokument, och om dokumentet eller samlingen inte finns skapas det automatiskt åt dig, eftersom samlingen alltid måste vara rotnoden (första). Här är ett enkelt Tasks-exempelschema för projektet du kommer att arbeta med inom kort, bestående av Tasks-samlingen, samt ett flertal dokument som innehåller två fält, namnet (strängen) och en flagga för om uppgiften är klar (boolesk) .

Låt oss dekomponera vart och ett av elementen så att du kan förstå dem bättre.

Samlingar

Synonymt med databastabeller i SQL-världen innehåller samlingar ett eller flera dokument. Samlingar måste vara rotelementen i ditt schema och kan bara innehålla dokument, inte andra samlingar. Däremot kan du hänvisa till ett dokument som i sin tur avser samlingar (delsamlingar).

I diagrammet ovan består en uppgift av två primitiva fält (namn och gjort) samt en delsamling (deluppgift) som består av två egna primitiva fält.

Dokument

Dokument består av nyckel/värdepar, där värdena har en av följande typer: 

  • primitiva fält (som strängar, tal, booleska)
  • komplexa kapslade objekt (listor eller arrayer av primitiver)
  • undersamlingar

Kapslade objekt kallas också kartor och kan representeras enligt följande, i dokumentet. Följande är ett exempel på ett kapslat objekt respektive en array:

ID: 2422892 //primitive
name: “Remember to buy milk” 
detail: //nested object
    notes: "This is a task to buy milk from the store"
	created: 2017-04-09
	due: 2017-04-10
done: false
notify: ["2F22-89R2", "L092-G623", "H00V-T4S1"]
...

Mer information om de datatyper som stöds finns i Googles dokumentation för datatyper. Därefter kommer du att skapa ett projekt för att fungera med Cloud Firestore.

Konfigurera projektet

Om du har arbetat med Firebase tidigare bör mycket av detta vara bekant för dig. Annars måste du skapa ett konto i Firebase och följa instruktionerna i avsnittet Konfigurera projektet i vår tidigare självstudie, Kom igång med Firebase-autentisering för iOS .

För att följa med i den här självstudien, klona handledningsprojektets repo. Inkludera sedan Firestore-biblioteket efter lägga till följande i din Podfile :

pod 'Firebase/Core' 
pod 'Firebase/Firestore'

Ange följande i din terminal för att bygga ditt bibliotek:

pod install

Växla sedan till Xcode och öppna .xcworkspace fil. Navigera till AppDelegate.swift  fil och ange följande i application:didFinishLaunchingWithOptions: metod:

FirebaseApp.configure()

I din webbläsare går du till Firebase-konsolen och välj Databas fliken till vänster.

Se till att du väljer alternativet Starta i testläge så att du inte har några säkerhetsproblem medan vi experimenterar, och följ säkerhetsmeddelandet när du flyttar din app till produktion. Du är nu redo att skapa en samling och några exempeldokument.

Lägga till en samling och ett exempeldokument

Börja med att skapa en första samling, Tasks genom att välja Lägg till samling knappen och namnge samlingen, som illustreras nedan:

För det första dokumentet kommer du att lämna dokument-ID tomt, vilket automatiskt genererar ett ID åt dig. Dokumentet kommer helt enkelt att bestå av två fält: name och done .

Spara dokumentet och du bör kunna bekräfta insamlingen och dokumentet tillsammans med det automatiskt genererade ID:t:

Med databasen konfigurerad med ett exempeldokument i molnet är du redo att börja implementera Firestore SDK i Xcode.

Skapa och arbeta med databasreferenser

Öppna MasterViewController.swift fil i Xcode och lägg till följande rader för att importera biblioteket:

import Firebase

class MasterViewController: UITableViewController {
    @IBOutlet weak var addButton: UIBarButtonItem!
    
    private var documents: [DocumentSnapshot] = []
    public var tasks: [Task] = []
    private var listener : ListenerRegistration!
   ...

Här skapar du helt enkelt en lyssnarvariabel som gör att du kan utlösa en anslutning till databasen i realtid när det sker en förändring. Du skapar också en DocumentSnapshot referens som kommer att hålla den tillfälliga dataögonblicksbilden.

Innan du fortsätter med vykontrollen skapar du en annan swift-fil, Task.swift , som kommer att representera din datamodell:

import Foundation

struct Task{
    var name:String
    var done: Bool
    var id: String
    
    var dictionary: [String: Any] {
        return [
            "name": name,
            "done": done
        ]
    }
}

extension Task{
    init?(dictionary: [String : Any], id: String) {
        guard   let name = dictionary["name"] as? String,
            let done = dictionary["done"] as? Bool
            else { return nil }
        
        self.init(name: name, done: done, id: id)
    }
}

Kodavsnittet ovan inkluderar en bekvämlighetsegenskap (lexikon) och metod (init) som gör det lättare att fylla i modellobjektet. Växla tillbaka till vykontrollern och deklarera en global inställningsvariabel som kommer att begränsa basfrågan till de 50 bästa posterna i uppgiftslistan. Du kommer också att ta bort lyssnaren när du har ställt in frågevariabeln, som anges i didSet egendom nedan:

fileprivate func baseQuery() -> Query {
        return Firestore.firestore().collection("Tasks").limit(to: 50)
    }
    
    fileprivate var query: Query? {
        didSet {
            if let listener = listener {
                listener.remove()
            }
        }
    }

override func viewDidLoad() {
        super.viewDidLoad()
        self.query = baseQuery()
    }

 override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        self.listener.remove()
    }

Läser data i realtid från Cloud Firestore

Med dokumentreferensen på plats, i viewWillAppear(_animated: Bool) , associera lyssnaren du skapade tidigare med resultatet av frågeöversiktsbilden och hämta en lista med dokument. Detta görs genom att anropa Firestore-metoden query?.addSnapshotListener :

self.listener =  query?.addSnapshotListener { (documents, error) in
            guard let snapshot = documents else {
                print("Error fetching documents results: \(error!)")
                return
            }
            
            let results = snapshot.documents.map { (document) -> Task in
                if let task = Task(dictionary: document.data(), id: document.documentID) {
                    return task
                } else {
                    fatalError("Unable to initialize type \(Task.self) with dictionary \(document.data())")
                }
            }
            
            self.tasks = results
            self.documents = snapshot.documents
            self.tableView.reloadData()
            
        }

Stängningen ovan tilldelar snapshot.documents genom att mappa arrayen iterativt och slå in den till en ny Task modellinstansobjekt för varje dataobjekt i ögonblicksbilden. Så med bara några rader har du framgångsrikt läst in alla uppgifter från molnet och tilldelat dem till de globala tasks   array.

För att visa resultaten, fyll i följande TableView delegera metoder:

override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return tasks.count
    }
    
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        
        let item = tasks[indexPath.row]
        
        cell.textLabel!.text = item.name
        cell.textLabel!.textColor = item.done == false ? UIColor.black : UIColor.lightGray
        
        return cell
    }

I detta skede, bygg och kör projektet och i simulatorn bör du kunna observera data som dyker upp i realtid. Lägg till data via Firebase-konsolen och du bör se den visas omedelbart i appsimulatorn.

Skapa, uppdatera och ta bort data

Efter att ha lyckats läsa innehåll från back-end kommer du att skapa, uppdatera och radera data. Nästa exempel kommer att illustrera hur man uppdaterar data, med hjälp av ett konstruerat exempel där appen bara låter dig markera ett objekt som gjort genom att trycka på cellen. Notera collection.document( item.id ).updateData(["done": !item.done]) closure-egenskap, som helt enkelt refererar till ett specifikt dokument-ID, uppdaterar vart och ett av fälten i ordboken:

override func tableView(_ tableView: UITableView,
                            didSelectRowAt indexPath: IndexPath) {

        let item = tasks[indexPath.row]
        let collection = Firestore.firestore().collection("Tasks")

        collection.document(item.id).updateData([
            "done": !item.done,
            ]) { err in
                if let err = err {
                    print("Error updating document: \(err)")
                } else {
                    print("Document successfully updated")
                }
        }

        tableView.reloadRows(at: [indexPath], with: .automatic)
        
    }

För att radera ett objekt, ring document( item.id ).delete() metod:

override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

        if (editingStyle == .delete){
            let item = tasks[indexPath.row]
            _ = Firestore.firestore().collection("Tasks").document(item.id).delete()
        }

    }

Att skapa en ny uppgift innebär att lägga till en ny knapp i din Storyboard och koppla dess IBAction till vykontrollern, skapa en addTask(_ sender:) metod. När en användare trycker på knappen visas ett varningsblad där användaren kan lägga till ett nytt uppgiftsnamn:

collection("Tasks").addDocument
    (data: ["name": textFieldReminder.text ?? 
        "empty task", "done": false])

Slutför den sista delen av appen genom att ange följande:

@IBAction func addTask(_ sender: Any) {
        
        let alertVC : UIAlertController = UIAlertController(title: "New Task", message: "What do you want to remember?", preferredStyle: .alert)
        
        alertVC.addTextField { (UITextField) in
            
        }
        
        let cancelAction = UIAlertAction.init(title: "Cancel", style: .destructive, handler: nil)
        
        alertVC.addAction(cancelAction)
        
        //Alert action closure
        let addAction = UIAlertAction.init(title: "Add", style: .default) { (UIAlertAction) -> Void in
            
            let textFieldReminder = (alertVC.textFields?.first)! as UITextField
            
            let db = Firestore.firestore()
            var docRef: DocumentReference? = nil
            docRef = db.collection("Tasks").addDocument(data: [
                "name": textFieldReminder.text ?? "empty task",
                "done": false
            ]) { err in
                if let err = err {
                    print("Error adding document: \(err)")
                } else {
                    print("Document added with ID: \(docRef!.documentID)")
                }
            }
            
        }
    
        alertVC.addAction(addAction)
        present(alertVC, animated: true, completion: nil)
        
    }

Bygg och kör appen en gång till och, när simulatorn dyker upp, prova att lägga till några uppgifter, samt markera några som klara och testa slutligen raderingsfunktionen genom att ta bort några uppgifter. Du kan bekräfta att den lagrade informationen har uppdaterats i realtid genom att byta över till din Firebase-databaskonsol och observera insamlingen och dokumenten.

Filtrering och sammansatta frågor

Hittills har du bara arbetat med en enkel fråga, utan några specifika filtreringsmöjligheter. För att skapa lite mer robusta frågor kan du filtrera efter specifika värden genom att använda ett whereField klausul:

docRef.whereField(“name”, isEqualTo: searchString)

Du kan beställa och begränsa dina frågedata genom att använda order(by: ) och limit(to: ) metoder enligt följande:

docRef.order(by: "name").limit(5)

I FirebaseDo-appen har du redan använt limit med basfrågan. I ovanstående utdrag använde du dig även av en annan funktion, sammansatta frågor, där både order och limit är sammankopplade. Du kan koppla ihop så många frågor du vill, till exempel i följande exempel:

docRef
    .whereField(“name”, isEqualTo: searchString)
	.whereField(“done”, isEqualTo: false)
	.order(by: "name")
	.limit(5)

  1. psql:kunde inte ansluta till servern:Anslutning nekades Fel vid anslutning till fjärrdatabas

  2. Apache Spark:JDBC-anslutningen fungerar inte

  3. ORA-00054:resurs upptagen och förvärv med NOWAIT specificerad eller timeout har gått ut

  4. Fatalt fel:Anrop till odefinierad funktion session_register()