Aan de slag met Cloud Firestore voor iOS

Mobiele codeerders maken al jaren gebruik van het Mobile Backend as a Service (MBaaS) -platform Firebase Realtime Database, zodat ze zich kunnen concentreren op het bouwen van functies voor hun apps zonder zich zorgen te hoeven maken over de back-end-infrastructuur en -database. Door het eenvoudig te maken om gegevens in de cloud op te slaan en aan te houden en te zorgen voor verificatie en beveiliging, kunnen codeers zich op de client richten met Firebase. 

Vorig jaar kondigde Google alweer een backend-databaseoplossing aan, Cloud Firestore, vanaf de basis opgebouwd met de belofte van grotere schaalbaarheid en intuïtiviteit. Dit zorgde echter voor enige verwarring over de plaats in relatie tot het reeds bestaande vlaggenschipproduct van Google, Firebase Realtime Database. In deze zelfstudie worden de verschillen tussen de twee platforms en de duidelijke voordelen van beide uiteengezet. U leert hoe te werken met Firestore documentreferenties, evenals het lezen, schrijven, bijwerken en verwijderen van gegevens in realtime, door een eenvoudige remindersapp te bouwen.

Doel van deze zelfstudie

Deze tutorial zal je blootstellen aan Cloud Firestore. U leert hoe u het platform kunt gebruiken voor real-time databasepersistentie en -synchronisatie. We behandelen de volgende onderwerpen:

  • wat Cloud Firestore is
  • het Firestore-gegevensmodel
  • opzetten van Cloud Firestore
  • Creëren en werken met Cloud Firestore-referenties
  • gegevens in realtime lezen van Cloud Firestore
  • gegevens maken, bijwerken en verwijderen
  • filteren en samengestelde query's

Aangenomen kennis

In deze zelfstudie wordt ervan uitgegaan dat je enige blootstelling hebt ondervonden aan Firebase en een achtergrond hebt ontwikkeld met Swift en Xcode.

Wat is Cloud Firestore?

Net als Firebase Realtime Database biedt Firestore mobiele en webontwikkelaars een platformonafhankelijke cloudoplossing om gegevens in realtime aan te bieden, ongeacht netwerklatentie of internetconnectiviteit, evenals naadloze integratie met de reeks producten van het Google Cloud Platform. Naast deze overeenkomsten zijn er ook duidelijke voor- en nadelen die de een van de ander onderscheiden. 

Gegevensmodel

Op een fundamenteel niveau slaat Realtime Database gegevens op als één grote, monolithische, hiërarchische JSON-boom, terwijl Firestore gegevens in documenten en collecties organiseert, evenals sub-collecties. Dit vereist minder denormalisatie. Het opslaan van gegevens in één JSON-structuur heeft de voordelen van eenvoud als het gaat om het werken met eenvoudige gegevensvereisten; het wordt echter lastiger op schaal bij het werken met meer complexe hiërarchische gegevens. 

Offline ondersteuning

Beide producten bieden offline ondersteuning, zetten gegevens in wachtrijen in cache wanneer er latente of geen netwerkconnectiviteit is - synchroniseren van lokale wijzigingen naar de achterkant wanneer mogelijk. Firestore ondersteunt naast mobiele apps ook offline synchronisatie voor webapps, terwijl de Realtime-database alleen mobiele synchronisatie mogelijk maakt.

Query's en transacties 

Realtime Database ondersteunt alleen beperkte sorteer- en filtermogelijkheden: u kunt alleen in een enkele query sorteren of filteren op een propertyniveau, maar niet beide. Queries zijn ook diep, wat betekent dat ze een grote sub-tree van resultaten terugsturen. Het product ondersteunt alleen eenvoudige schrijf- en transactiewerkzaamheden waarvoor een voltooiingscallback vereist is. 

Firestore daarentegen introduceert indexquery's met samengesteld sorteren en filteren, zodat u acties kunt combineren om kettingfilters en sortering te maken. U kunt ook ondiepe query's uitvoeren die subverzamelingen retourneren in plaats van de hele verzameling die u zou krijgen met Realtime Database. Transacties zijn atomair van aard, ongeacht of u een batch-bewerking of single verzendt, waarbij transacties zich automatisch herhalen tot ze zijn voltooid. Daarnaast ondersteunt Realtime Database alleen individuele schrijftransacties, terwijl Firestore batchbewerkingen op atomaire wijze biedt.

Prestaties en schaalbaarheid

De Realtime Database, zoals je zou verwachten, is vrij robuust en heeft een lage latentie. Databases zijn echter beperkt tot afzonderlijke regio's, afhankelijk van beschikbare zonale beschikbaarheid. Firestore huisvest daarentegen gegevens horizontaal over meerdere zones en regio's om te zorgen voor echte wereldwijde beschikbaarheid, schaalbaarheid en betrouwbaarheid. Google heeft zelfs beloofd dat Firestore betrouwbaarder is dan Realtime Database. 

Een andere tekortkoming van de Realtime-database is de beperking tot 100.000 gelijktijdige gebruikers (100.000 gelijktijdige verbindingen en 1.000 schrijf- / seconde in een enkele database), waarna u uw database zou moeten vernietigen (uw database opsplitsen in meerdere databases) om meer gebruikers te ondersteunen . Firestore schaalt automatisch over meerdere instanties zonder dat u hoeft in te grijpen. 

Ontworpen vanaf de basis met schaalbaarheid in het achterhoofd, heeft Firestore een nieuwe schematische architectuur die gegevens repliceert in meerdere regio's, zorgt voor authenticatie en handelt andere beveiligingsgerelateerde zaken af, allemaal binnen zijn client-side SDK. Het nieuwe datamodel is intuïtiever dan die van Firebase, wat meer lijkt op andere vergelijkbare NoSQL-databaseoplossingen zoals MongoDB, terwijl het een meer robuuste query-engine biedt. 

Veiligheid 

Ten slotte beheert Realtime-database, zoals u weet uit onze vorige zelfstudies, beveiliging via trapsgewijze regels met afzonderlijke validatietriggers. Dit werkt met Firebase-databaseregels en valideert uw gegevens afzonderlijk. Firestore biedt daarentegen een eenvoudiger maar krachtiger beveiligingsmodel dat profiteert van Cloud Firestore-beveiligingsregels en identiteits- en toegangsbeheer (IAM), met gegevensvalidatie uitgezonderd automatisch.

Het firestore-gegevensmodel

Firestore is een op NoSQL gebaseerde document-database, bestaande uit verzamelingen documenten, die elk gegevens bevatten. Omdat het een NoSQL-database is, krijgt u geen tabellen, rijen en andere elementen die u in een relationele database zou vinden, maar in plaats daarvan sets van sleutel- / waardeparen die u in documenten zou vinden. 

U maakt impliciet documenten en collecties aan door gegevens toe te wijzen aan een document en als het document of de verzameling niet bestaat, wordt het automatisch voor u gemaakt, omdat de verzameling altijd het hoofd (eerste) knooppunt moet zijn. Hier is een eenvoudig takenvoorbeeldschema van het project waar u binnenkort aan zult werken, bestaande uit de verzameling Taken, evenals talrijke documenten die twee velden bevatten, de naam (tekenreeks) en een markering voor het feit of de taak is voltooid (boolean).

Laten we elk element ontbinden zodat je het beter kunt begrijpen. 

collecties

Synoniem aan databasetabellen in de SQL-wereld bevatten collecties een of meer documenten. Collecties moeten de rootelementen in uw schema zijn en mogen alleen documenten bevatten, niet andere collecties. U kunt echter verwijzen naar een document dat op zijn beurt weer verwijst naar collecties (deelcollecties).

In het bovenstaande diagram bestaat een taak uit twee primitieve velden (naam en gereed) en een subverzameling (subtaak) die uit twee primitieve eigen velden bestaat. 

documenten

Documenten bestaan ​​uit sleutel / waarde-paren, waarbij de waarden een van de volgende typen hebben: 

  • primitieve velden (zoals strings, getallen, boolean)
  • complexe geneste objecten (lijsten of arrays van primitieven)
  • deelcollecties

Geneste objecten worden ook kaarten genoemd en kunnen als volgt in het document worden weergegeven. Hieronder volgt een voorbeeld van een genest object en een array, respectievelijk:

ID: 2422892 // primitieve naam: "Vergeet niet om melk te kopen" detail: // geneste objectnotities: "Dit is een taak om melk uit de winkel te kopen" gemaakt: 2017-04-09 te wijten: 2017-04-10 gedaan: false notify: ["2F22-89R2", "L092-G623", "H00V-T4S1"] ... 

Raadpleeg de gegevenstypen-documentatie van Google voor meer informatie over de ondersteunde gegevenstypen. Vervolgens gaat u een project opzetten om met Cloud Firestore te werken.

Het project opzetten

Als je al eerder met Firebase hebt gewerkt, moet dit voor veel bekend zijn. Anders moet u een account maken in Firebase en de instructies volgen in het gedeelte 'Het project instellen' van onze vorige zelfstudie, Aan de slag met Firebase-verificatie voor iOS . 

Volg deze tutorial om de repository van het zelfstudieproject te klonen. Voeg vervolgens de Firestore-bibliotheek toe doorhet toevoegen van het volgende aan uw Podfile:

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

Voer het volgende in uw terminal in om uw bibliotheek te bouwen:

pod installeren

Schakel vervolgens over naar Xcode en open de .xcworkspace het dossier. Navigeer naar de AppDelegate.swift bestand en voer het volgende in de toepassing: didFinishLaunchingWithOptions: methode:

FirebaseApp.configure ()

Ga in uw browser naar de Firebase-console en selecteer de Database tab aan de linkerkant. 

Zorg ervoor dat je de optie selecteert voor Start in de testmodus zodat u tijdens het experiment geen beveiligingsproblemen ondervindt en aandacht besteedt aan de beveiligingskennisgeving wanneer u uw app in productie neemt. U bent nu klaar om een ​​verzameling en enkele voorbeelddocumenten te maken.

Een verzameling en voorbeelddocument toevoegen

Maak om te beginnen een eerste verzameling, taken, door de. te selecteren Collectie toevoegen knop en naamgeving van de collectie, zoals hieronder geïllustreerd:

Voor het eerste document laat u de document-ID leeg, die automatisch een ID voor u genereert. Het document bestaat eenvoudigweg uit twee velden: naam en gedaan.

Sla het document op en u kunt de verzameling en het document samen met de automatisch gegenereerde ID bevestigen:

Als de database is ingesteld met een voorbeelddocument in de cloud, bent u klaar om de Firestore SDK in Xcode te implementeren.

Creëren en werken met database-verwijzingen

Open de MasterViewController.swift bestand in Xcode en voeg de volgende regels toe om de bibliotheek te importeren:

import Firebase-klasse MasterViewController: UITableViewController @IBOutlet zwakke var addButton: UIBarButtonItem! privé var-documenten: [DocumentSnapshot] = [] public var tasks: [Task] = [] private var listener: ListenerRegistration! ... 

Hier maakt u eenvoudig een luisteraarvariabele aan waarmee u in realtime een verbinding met de database kunt activeren wanneer er een wijziging optreedt. Je maakt ook een DocumentSnapshot verwijzing die de tijdelijke momentopname van gegevens kan bevatten.

Voordat u doorgaat met de view controller, maakt u nog een snel bestand, Task.swift, welke uw gegevensmodel zal vertegenwoordigen:

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"] als? Bool else return nil self.init (naam: naam, gereed: voltooid, id: id)

Het codefragment hierboven bevat een geriefseigenschap (woordenboek) en methode (init) die het invullen van het modelobject eenvoudiger maakt. Schakel terug naar de weergavecontroller en declareer een globale settervariabele die de basisquery zal beperken tot de top 50-vermeldingen in de takenlijst. U zult ook de luisteraar verwijderen zodra u de queryvariabele instelt, zoals aangegeven in de didSet eigendom hieronder:

fileprivate func baseQuery () -> Query retourneer Firestore.firestore (). collection ("Tasks"). limit (to: 50) fileprivate var query: Query? didSet if let listener = listener listener.remove () overschrijven func viewDidLoad () super.viewDidLoad () self.query = baseQuery () overschrijven func viewWillDisappear (_ geanimeerd: Bool) super.viewWillDisappear ( geanimeerd) self.listener.remove ()

Gegevens in realtime lezen van Cloud Firestore

Met de documentverwijzing op zijn plaats, in viewWillAppear (_animated: Bool), associeer de luisteraar die u eerder hebt gemaakt met de resultaten van de momentopname van de query en haal een lijst met documenten op. Dit wordt gedaan door de Firestore-methode aan te roepen te vragen? .addSnapshotListener:

self.listener = query? .addSnapshotListener (documenten, fouten) in bewaker laten snapshot = documenten anders print ("Fout bij ophalen van resultaten van documenten: \ (error!)") return let results = snapshot.documents.map (document ) -> Taak in als let task = Task (woordenboek: document.data (), id: document.documentID) return task else fatalError ("Kan type \ (Task.self) niet initialiseren met dictionary \ (document. data ()) ") self.tasks = resultaten self.documents = snapshot.documents self.tableView.reloadData ()

De sluiting hierboven wijst de snapshot.documents door de array iteratief in kaart te brengen en naar een nieuwe toe te voegen Taak model instance-object voor elk gegevensitem in de momentopname. Dus met slechts enkele regels heeft u alle taken uit de cloud met succes ingelezen en aan de globale toegewezen takenrangschikking. 

Om de resultaten weer te geven, vult u het volgende inTableViewdelegeren methoden:

override func numberOfSections (in tableView: UITableView) -> Int return 1 override func tableView (_ tableView: UITableView, numberOfRowsInSection sectie: Int) -> Int return tasks.count negeer func tableView (_ tableView: UITableView, cellForRowAt indexPath : IndexPath) -> UITableViewCell let cell = tableView.dequeueReusableCell (withIdentifier: "Cell", voor: indexPath) let item = tasks [indexPath.row] cell.textLabel! .Text = item.name cell.textLabel! .TextColor = item.done == false? UIColor.black: UIColor.lightGray-retourcel

In dit stadium moet u het project bouwen en uitvoeren en in de Simulator moet u gegevens kunnen bekijken die in realtime worden weergegeven. Voeg gegevens toe via de Firebase-console en u zou deze onmiddellijk in de app-simulator moeten zien verschijnen. 

Gegevens maken, bijwerken en verwijderen

Na het succesvol lezen van de inhoud van de back-end, maakt u vervolgens gegevens aan, werkt u deze bij en verwijdert u deze. Het volgende voorbeeld illustreert hoe u gegevens kunt bijwerken, met behulp van een gekunsteld voorbeeld waarbij de app u alleen een item laat markeren als voltooid door op de cel te tikken. Merk op collection.document (item ID) .updateData (["done":! item.done]) eigenschap closure, die eenvoudigweg verwijst naar een specifieke document-ID, waarbij elk van de velden in het woordenboek wordt bijgewerkt:

override func tableView (_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) let item = tasks [indexPath.row] laat collection = Firestore.firestore (). collection ("Tasks") collection.document (item.id) .updateData ( ["klaar":! item.done,]) err in if let err = err print ("Fout bij bijwerken van document: \ (err)") else print ("Document succesvol bijgewerkt") tableView.reloadRows (op: [indexPath], met: .automatic)

Om een ​​item te verwijderen, belt u het document(item ID) .Delete () methode:

override func tableView (_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool return true overschrijf 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 ()

Het aanmaken van een nieuwe taak houdt in dat je een nieuwe knop toevoegt aan je Storyboard en deze aansluit IBAction naar de view controller, waardoor een addTask (_ afzender :) methode. Wanneer een gebruiker op de knop drukt, verschijnt er een waarschuwingspagina waarin de gebruiker een nieuwe taaknaam kan toevoegen:

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

Vul het laatste deel van de app in door het volgende in te voeren:

@IBAction func addTask (_ sender: Any) let alertVC: UIAlertController = UIAlertController (titel: "Nieuwe taak", bericht: "Wat wilt u onthouden?", PreferredStyle: .alert) alertVC.addTextField (UITextField) in  laat cancelAction = UIAlertAction.init (titel: "Annuleren", stijl: .destructive, handler: nil) alertVC.addAction (cancelAction) // Alarmactie afsluiten laat addAction = UIAlertAction.init (titel: "Toevoegen", stijl:. standaard) (UIAlertAction) -> Void in laat textFieldReminder = (alertVC.textFields? .first)! als UITextField laten db = Firestore.firestore () var docRef: DocumentReference? = nul docRef = db.collection ("Taken"). addDocument (data: ["name": textFieldReminder.text ?? "lege taak", "done": false]) err in if let err = err print () "Fout bij toevoegen van document: \ (err)") else print ("Document toegevoegd met ID: \ (docRef! .DocumentID)") alertVC.addAction (addAction) present (alertVC, geanimeerd: true, completion: nul)

Bouw en voer de app opnieuw uit en probeer, wanneer de simulator verschijnt, een paar taken toe te voegen, een paar taken te markeren als voltooid en ten slotte de verwijderfunctie te testen door een aantal taken te verwijderen. U kunt bevestigen dat de opgeslagen gegevens in realtime zijn bijgewerkt door over te schakelen naar uw Firebase-databaseconsole en de verzameling en documenten te observeren.

Filteren en samengestelde query's

Tot nu toe hebt u alleen met een eenvoudige query gewerkt, zonder specifieke filtermogelijkheden. Als u iets meer robuuste query's wilt maken, kunt u filteren op specifieke waarden door gebruik te maken van a whereField clausule:

docRef.whereField ("name", isEqualTo: searchString)

U kunt uw querygegevens bestellen en beperken door gebruik te maken van de bestellen (door:) en grens aan: ) methoden als volgt:

docRef.order (door: "naam"). limit (5)

In de FirebaseDo-app, waar je al gebruik van hebt gemaakt begrenzing met de basisquery. In het bovenstaande fragment maakte u ook gebruik van een andere functie, samengestelde query's, waarbij zowel de volgorde als de limiet aan elkaar zijn gekoppeld. U kunt zoveel zoekopdrachten koppelen als u wilt, zoals in het volgende voorbeeld:

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

Conclusie

In deze zelfstudie hebt u het nieuwe MBaaS-product van Google, Cloud Firestore, onderzocht en daarbij een eenvoudige app voor taakherinnering gemaakt die aantoont hoe eenvoudig het is om uw gegevens in de cloud aan te houden, te synchroniseren en te ondervragen. U hebt informatie ontvangen over de dataschema-structuur van Firestore in vergelijking met Firebase Realtime Database, en hoe u gegevens in realtime kunt lezen en schrijven, en gegevens kunt bijwerken en verwijderen. U hebt ook geleerd hoe u eenvoudige en samengestelde query's kunt uitvoeren en hoe u gegevens filtert. 

Cloud Firestore is gemaakt met het doel om de robuustheid van Firebase Realtime Database te bieden zonder veel van de beperkingen die mobiele ontwikkelaars moesten hebben, vooral wat betreft schaalbaarheid en query's. We hebben alleen het oppervlak bekrast van wat u kunt bereiken met Firestore en het is zeker de moeite waard om enkele van de meer geavanceerde concepten te verkennen, zoals Pagineren van gegevens met querycursors, indexen beheren en gegevens beveiligen.