Wenn Sie Parse-Nutzer sind und nach einer alternativen Backend-as-a-Service-Lösung suchen, ist Firebase möglicherweise die ideale Wahl für Ihre iOS-App.
In diesem Leitfaden wird beschrieben, wie Sie bestimmte Dienste in Ihre App einbinden. Eine grundlegende Anleitung zur Einrichtung von Firebase finden Sie im Leitfaden iOS + Einrichtung.
Google Analytics
Google Analytics ist eine kostenlose App-Analyselösung, mit der sich Informationen zur App-Nutzung und zu Interaktionen der Nutzer gewinnen lassen. Analytics lässt sich in Firebase-Funktionen einbinden und bietet unbegrenzte Berichterstellung für bis zu 500 verschiedene Ereignisse, die Sie mit dem Firebase SDK definieren können.
Weitere Informationen finden Sie in der Google Analytics-Dokumentation.
Vorgeschlagene Migrationsstrategie
Die Verwendung verschiedener Analyseanbieter ist ein gängiges Szenario, das sich leicht auf Google Analytics anwenden lässt. Fügen Sie sie einfach Ihrer App hinzu, um von Ereignissen und Nutzereigenschaften zu profitieren, die Analytics automatisch erfasst werden, z. B. „Erste Aktivierung“, „App-Update“, „Gerätemodell“ und „Alter“.
Bei benutzerdefinierten Ereignissen und Nutzereigenschaften können Sie eine Doppelschreibstrategie verwenden, bei der sowohl Parse Analytics als auch Google Analytics zum Protokollieren von Ereignissen und Eigenschaften verwendet werden. So können Sie die neue Lösung nach und nach einführen.
Codevergleich
Parse Analytics
// Start collecting data
[PFAnalytics trackAppOpenedWithLaunchOptions:launchOptions];
NSDictionary *dimensions = @{
// Define ranges to bucket data points into meaningful segments
@"priceRange": @"1000-1500",
// Did the user filter the query?
@"source": @"craigslist",
// Do searches happen more often on weekdays or weekends?
@"dayType": @"weekday"
};
// Send the dimensions to Parse along with the 'search' event
[PFAnalytics trackEvent:@"search" dimensions:dimensions];
Google Analytics
// Obtain the AppMeasurement instance and start collecting data
[FIRApp configure];
// Send the event with your params
[FIRAnalytics logEventWithName:@"search" parameters:@{
// Define ranges to bucket data points into meaningful segments
@"priceRange": @"1000-1500",
// Did the user filter the query?
@"source": @"craigslist",
// Do searches happen more often on weekdays or weekends?
@"dayType": @"weekday"
}];
Firebase Realtime Database
Die Firebase Realtime Database ist eine in der Cloud gehostete NoSQL-Datenbank. Die Daten werden als JSON gespeichert und in Echtzeit mit jedem verbundenen Client synchronisiert.
Weitere Informationen finden Sie in der Firebase Realtime Database-Dokumentation.
Unterschiede zu „Daten analysieren“
Objekte
In Parse speichern Sie eine PFObject
oder eine Unterklasse davon, die Schlüssel/Wert-Paare von JSON-kompatiblen Daten enthält. Die Daten sind schemalos, d. h., Sie müssen nicht angeben, welche Schlüssel für jede PFObject
vorhanden sind.
Alle Firebase Realtime Database-Daten werden als JSON-Objekte gespeichert. Für PFObject
gibt es kein Äquivalent. Sie schreiben einfach Werte von Typen in den JSON-Baum, die den verfügbaren JSON-Typen entsprechen.
Im folgenden Beispiel wird gezeigt, wie Sie die Highscores für ein Spiel speichern können.
Parsen
PFObject *gameScore = [PFObject objectWithClassName:@"GameScore"];
gameScore[@"score"] = @1337;
gameScore[@"playerName"] = @"Sean Plott";
gameScore[@"cheatMode"] = @NO;
[gameScore saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
// The object has been saved.
} else {
// There was a problem, check error.description
}
}];
Firebase
// Create a reference to the database
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
NSString *key = [[ref child:@"scores"] childByAutoId].key;
NSDictionary *score = @{@"score": @1337,
@"playerName": @"Sean Plott",
@"cheatMode": @NO};
[key setValue:score withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
if (error) {
// The object has been saved.
} else {
// There was a problem, check error.description
}
}];
Beziehungen zwischen Daten
Ein PFObject
kann eine Beziehung zu einem anderen PFObject
haben: Jedes Objekt kann andere Objekte als Werte verwenden.
In der Firebase Realtime Database werden Beziehungen besser mit flachen Datenstrukturen ausgedrückt, die die Daten in separate Pfade aufteilen, damit sie in separaten Aufrufen effizient heruntergeladen werden können.
Im Folgenden finden Sie ein Beispiel dafür, wie Sie die Beziehung zwischen Beiträgen in einer Blogging-App und ihren Autoren strukturieren können.
Parsen
// Create the author
PFObject *myAuthor = [PFObject objectWithClassName:@"Author"];
myAuthor[@"name"] = @"Grace Hopper";
myAuthor[@"birthDate"] = @"December 9, 1906";
myAuthor[@"nickname"] = @"Amazing Grace";
// Create the post
PFObject *myPost = [PFObject objectWithClassName:@"Post"];
myPost[@"title"] = @"Announcing COBOL, a New Programming Language";
// Add a relation between the Post and the Author
myPost[@"parent"] = myAuthor;
// This will save both myAuthor and myPost
[myPost saveInBackground];
Firebase
// Create a reference to the database
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
// Create the author
NSString *myAuthorKey = @"ghopper";
NSDictionary *author = @{@"name": @"Grace Hopper",
@"birthDate": @"December 9, 1906",
@"nickname": @"Amazing Grace"};
// Save the author
[[ref child:myAuthorKey] setValue:author]
// Create and save the post
NSString *key = [[ref child:@"posts"] childByAutoId].key;
NSDictionary *post = @{@"author": myAuthorKey,
@"title": @"Announcing COBOL, a New Programming Language"};
[key setValue:post]
Das folgende Datenlayout ist das Ergebnis.
{ // Info about the authors "authors": { "ghopper": { "name": "Grace Hopper", "date_of_birth": "December 9, 1906", "nickname": "Amazing Grace" }, ... }, // Info about the posts: the "author" fields contains the key for the author "posts": { "-JRHTHaIs-jNPLXOQivY": { "author": "ghopper", "title": "Announcing COBOL, a New Programming Language" } ... } }
Daten lesen
In Parse können Sie Daten entweder mit der ID eines bestimmten Parse-Objekts oder mithilfe von Abfragen mit PFQuery
lesen.
In Firebase rufen Sie Daten ab, indem Sie einer Datenbankreferenz einen asynchronen Listener zuordnen. Der Listener wird einmal für den anfänglichen Status der Daten und dann bei jeder Änderung der Daten ausgelöst. Sie müssen also keinen Code hinzufügen, um zu ermitteln, ob sich die Daten geändert haben.
Im folgenden Beispiel wird gezeigt, wie du anhand des Beispiels im Abschnitt Objekte Punkte für einen bestimmten Spieler abrufen kannst.
Parsen
PFQuery *query = [PFQuery queryWithClassName:@"GameScore"];
[query whereKey:@"playerName" equalTo:@"Dan Stemkoski"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for (PFObject *score in objects) {
NSString *gameScore = score[@"score"];
NSLog(@"Retrieved: %@", gameScore);
}
} else {
// Log details of the failure
NSLog(@"Error: %@ %@", error, [error userInfo]);
}
}];
Firebase
// Create a reference to the database
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
// This type of listener is not one time, and you need to cancel it to stop
// receiving updates.
[[[[ref child:@"scores"] queryOrderedByChild:@"playerName"] queryEqualToValue:@"Dan Stemkoski"]
observeEventType:FIRDataEventTypeChildAdded withBlock:^(FIRDataSnapshot *snapshot) {
// This will fire for each matching child node.
NSDictionary *score = snapshot.value;
NSString gameScore = score[@"score"];
NSLog(@"Retrieved: %@", gameScore);
}];
Vorgeschlagene Migrationsstrategie
Daten neu überdenken
Die Firebase Realtime Database ist für die Synchronisierung von Daten in Millisekunden über alle verbundenen Clients optimiert. Die resultierende Datenstruktur unterscheidet sich von den Parse-Kerndaten. Das bedeutet, dass Sie im ersten Schritt der Migration überlegen müssen, welche Änderungen an Ihren Daten erforderlich sind. Dazu gehören:
- So sollten Ihre Parse-Objekte Firebase-Daten zugeordnet werden
- Wenn Sie über Eltern-Kind-Beziehungen verfügen, können Sie Ihre Daten auf verschiedene Pfade aufteilen, damit sie in separaten Aufrufen effizient heruntergeladen werden können.
Daten migrieren
Nachdem Sie sich entschieden haben, wie Sie Ihre Daten in Firebase strukturieren möchten, müssen Sie planen, wie Sie mit dem Zeitraum umgehen, in dem Ihre App in beide Datenbanken schreiben muss. Sie haben folgende Auswahlmöglichkeiten:
Hintergrundsynchronisierung
In diesem Szenario haben Sie zwei Versionen der App: die alte Version, die Parse verwendet, und eine neue Version, die Firebase verwendet. Die Synchronisierung zwischen den beiden Datenbanken wird von Parse Cloud Code (Parse zu Firebase) verwaltet. Ihr Code überwacht Änderungen in Firebase und synchronisiert diese Änderungen mit Parse. Bevor Sie die neue Version verwenden können, müssen Sie Folgendes tun:
- Konvertieren Sie Ihre vorhandenen Parse-Daten in die neue Firebase-Struktur und schreiben Sie sie in die Firebase Realtime Database.
- Parse Cloud Code-Funktionen schreiben, die mit der Firebase REST API Änderungen an den Parse-Daten von alten Clients in die Firebase Realtime Database schreiben
- Schreiben und bereitstellen Sie Code, der Änderungen in Firebase überwacht und mit der Parse-Datenbank synchronisiert.
Dieses Szenario sorgt für eine klare Trennung von altem und neuem Code und hält die Clients einfach. Die Herausforderungen dieses Szenarios bestehen darin, große Datensätze beim ersten Export zu verarbeiten und dafür zu sorgen, dass die bidirektionale Synchronisierung keine unendliche Rekursion generiert.
Doppelschreiben
In diesem Szenario schreiben Sie eine neue Version der App, die sowohl Firebase als auch Parse verwendet. Mit Parse Cloud Code werden Änderungen, die von alten Clients an den Parse-Daten vorgenommen wurden, mit der Firebase Realtime Database synchronisiert. Wenn genügend Nutzer von der Parse-only-Version der App migriert sind, können Sie den Parse-Code aus der Version mit doppeltem Schreiben entfernen.
Für dieses Szenario ist kein serverseitiger Code erforderlich. Nachteile sind, dass Daten, auf die nicht zugegriffen wird, nicht migriert werden und dass die Größe Ihrer App durch die Verwendung beider SDKs erhöht wird.
Firebase Authentication
Firebase Authentication kann Nutzer mithilfe von Passwörtern und gängigen föderierten Identitätsanbietern wie Google, Facebook und Twitter authentifizieren. Außerdem bietet es UI-Bibliotheken, mit denen Sie die erheblichen Kosten sparen, die für die Implementierung und Pflege einer vollständigen Authentifizierung für Ihre App auf allen Plattformen erforderlich sind.
Weitere Informationen finden Sie in der Firebase Authentication-Dokumentation.
Unterschiede zu Parse Auth
Parse bietet eine spezielle Nutzerklasse namens PFUser
, die die für die Nutzerkontoverwaltung erforderlichen Funktionen automatisch verarbeitet. PFUser
ist eine Unterklasse der PFObject
. Das bedeutet, dass Nutzerdaten in „Daten analysieren“ verfügbar sind und wie bei jeder anderen PFObject
mit zusätzlichen Feldern erweitert werden können.
Ein FIRUser
hat eine feste Reihe grundlegender Properties – eine eindeutige ID, eine primäre E-Mail-Adresse, einen Namen und eine Foto-URL –, die in der Nutzerdatenbank eines separaten Projekts gespeichert und vom Nutzer aktualisiert werden können. Andere Attribute können nicht direkt zum FIRUser
-Objekt hinzugefügt werden. Stattdessen können Sie die zusätzlichen Attribute in Ihrem Firebase Realtime Database speichern.
Im folgenden Beispiel wird gezeigt, wie Sie einen Nutzer registrieren und ein zusätzliches Feld für die Telefonnummer hinzufügen.
Parsen
PFUser *user = [PFUser user];
user.username = @"my name";
user.password = @"my pass";
user.email = @"email@example.com";
// other fields can be set just like with PFObject
user[@"phone"] = @"415-392-0202";
[user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
// Hooray! Let them use the app now.
} else {
// Something went wrong
NSString *errorString = [error userInfo][@"error"];
}
}];
Firebase
[[FIRAuth auth] createUserWithEmail:@"email@example.com"
password:@"my pass"
completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
if (!error) {
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[[[ref child:@"users"] child:user.uid] child:@"phone"] setValue:@"415-392-0202"
} else {
// Something went wrong
NSString *errorString = [error userInfo][@"error"];
}
}];
Vorgeschlagene Migrationsstrategie
Konten migrieren
Wenn Sie Nutzerkonten von Parse zu Firebase migrieren möchten, exportieren Sie Ihre Nutzerdatenbank in eine JSON- oder CSV-Datei und importieren Sie die Datei dann mit dem Befehl auth:import
der Firebase-Befehlszeile in Ihr Firebase-Projekt.
Exportieren Sie zuerst Ihre Nutzerdatenbank aus der Parse Console oder Ihrer selbst gehosteten Datenbank. Eine aus der Parse Console exportierte JSON-Datei könnte beispielsweise so aussehen:
{ // Username/password user "bcryptPassword": "$2a$10$OBp2hxB7TaYZgKyTiY48luawlTuYAU6BqzxJfpHoJMdZmjaF4HFh6", "email": "user@example.com", "username": "testuser", "objectId": "abcde1234", ... }, { // Facebook user "authData": { "facebook": { "access_token": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "expiration_date": "2017-01-02T03:04:05.006Z", "id": "1000000000" } }, "username": "wXyZ987654321StUv", "objectId": "fghij5678", ... }
Transformieren Sie die exportierte Datei dann in das Format, das von der Firebase-Befehlszeile benötigt wird. Verwenden Sie die objectId
Ihrer Parse-Nutzer als localId
Ihrer Firebase-Nutzer. Codieren Sie außerdem die bcryptPassword
-Werte aus Parse mit Base64 und verwenden Sie sie im Feld passwordHash
. Beispiel:
{ "users": [ { "localId": "abcde1234", // Parse objectId "email": "user@example.com", "displayName": "testuser", "passwordHash": "JDJhJDEwJE9CcDJoeEI3VGFZWmdLeVRpWTQ4bHVhd2xUdVlBVTZCcXp4SmZwSG9KTWRabWphRjRIRmg2", }, { "localId": "fghij5678", // Parse objectId "displayName": "wXyZ987654321StUv", "providerUserInfo": [ { "providerId": "facebook.com", "rawId": "1000000000", // Facebook ID } ] } ] }
Importieren Sie die transformierte Datei abschließend mit der Firebase CLI und geben Sie bcrypt als Hash-Algorithmus an:
firebase auth:import account_file.json --hash-algo=BCRYPT
Nutzerdaten migrieren
Wenn Sie zusätzliche Daten für Ihre Nutzer speichern, können Sie diese mithilfe der im Abschnitt Datenmigration beschriebenen Strategien zu Firebase Realtime Database migrieren. Wenn Sie Konten mithilfe des im Abschnitt Migration von Konten beschriebenen Ablaufs migrieren, haben Ihre Firebase-Konten dieselben IDs wie Ihre Parse-Konten. So können Sie alle Beziehungen, die anhand der Nutzer-ID sortiert sind, ganz einfach migrieren und reproduzieren.
Firebase Cloud Messaging
Firebase Cloud Messaging (FCM) ist eine plattformübergreifende Messaging-Lösung, mit der Sie zuverlässig und kostenlos Nachrichten und Benachrichtigungen zustellen können. Der Benachrichtigungs-Editor ist ein kostenloser Dienst, der auf Firebase Cloud Messaging basiert und App-Entwicklern die Möglichkeit bietet, gezielte Nutzerbenachrichtigungen zu senden.
Weitere Informationen finden Sie in der Firebase Cloud Messaging-Dokumentation .
Unterschiede zu Parse-Push-Benachrichtigungen
Jede Parse-Anwendung, die auf einem für Benachrichtigungen registrierten Gerät installiert ist, ist mit einem Installation
-Objekt verknüpft, in dem alle Daten gespeichert werden, die für die Ausrichtung von Benachrichtigungen erforderlich sind.
Installation
ist eine abgeleitete Klasse von PFUser
. Das bedeutet, dass Sie Ihren Installation
-Instanzen beliebige zusätzliche Daten hinzufügen können.
Der Benachrichtigungs-Designer bietet vordefinierte Nutzersegmente basierend auf Informationen wie App, App-Version und Gerätesprache. Mithilfe von Google Analytics-Ereignissen und Google Analytics-Eigenschaften können Sie komplexere Nutzersegmente erstellen, um Zielgruppen zu bilden. Weitere Informationen finden Sie im Hilfeartikel zu Zielgruppen. Diese Targeting-Informationen sind in der Firebase Realtime Database nicht sichtbar.
Vorgeschlagene Migrationsstrategie
Gerätetokens migrieren
Während Parse APNs-Gerätetokens verwendet, um Installationen für Benachrichtigungen auszurichten, verwendet FCM FCM-Registrierungstokens, die den APNs-Gerätetokens zugeordnet sind. Fügen Sie Ihrer Apple-App einfach das FCM SDK hinzu. Daraufhin wird automatisch ein FCM-Token abgerufen.
Kanäle zu FCM-Themen migrieren
Wenn Sie Parse-Kanäle zum Senden von Benachrichtigungen verwenden, können Sie zu FCM-Themen migrieren, die dasselbe Publisher-Abonnenten-Modell bieten. Um die Umstellung von Parse auf FCM zu ermöglichen, können Sie eine neue Version der App erstellen, in der mit dem Parse SDK der Abostatus für Parse-Kanäle aufgehoben und mit dem FCM SDK entsprechende FCM-Themen abonniert werden.
Wenn Ihr Nutzer beispielsweise das Thema „Riesen“ abonniert hat, gehen Sie so vor:
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation removeObject:@"Giants" forKey:@"channels"];
[currentInstallation saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
[[FIRMessaging messaging] subscribeToTopic:@"/topics/Giants"];
} else {
// Something went wrong unsubscribing
}
}];
Mit dieser Strategie können Sie Nachrichten sowohl an den Parse-Kanal als auch an das entsprechende FCM-Thema senden und so Nutzer der alten und der neuen Version unterstützen. Sobald genügend Nutzer von der Version der App migriert sind, die nur zum Parsen dient, können Sie diese Version einstellen und nur noch mit FCM senden.
Weitere Informationen finden Sie in den FCM-Themendokumenten.
Firebase Remote Config
Firebase Remote Config ist ein Cloud-Dienst, mit dem Sie das Verhalten und das Design Ihrer App ändern können, ohne dass Nutzer ein App-Update herunterladen müssen. Dabei erstellen Sie In-App-Standardwerte, mit denen Verhalten und Aussehen der App festgelegt werden. Später können Sie diese Werte dann über die Firebase Console für alle oder nur für einen Teil Ihrer App-Nutzer überschreiben.
Firebase Remote Config kann bei Migrationen sehr nützlich sein, wenn Sie verschiedene Lösungen testen und mehr Kunden dynamisch zu einem anderen Anbieter migrieren möchten. Wenn Sie beispielsweise eine Version Ihrer App haben, die sowohl Firebase als auch Parse für die Daten verwendet, können Sie mit einer zufälligen Prozentregel festlegen, welche Clients Daten aus Firebase lesen, und den Prozentsatz schrittweise erhöhen.
Weitere Informationen zu Firebase Remote Config finden Sie in der Remote Config-Einführung.
Unterschiede zu „Parse Config“
Mit Parse Config können Sie Ihrer App im Parse Config-Dashboard Schlüssel/Wert-Paare hinzufügen und dann die PFConfig
auf dem Client abrufen. Jede PFConfig
-Instanz, die Sie abrufen, ist immer unveränderlich. Wenn Sie in Zukunft eine neue PFConfig
aus dem Netzwerk abrufen, wird keine vorhandene PFConfig
-Instanz geändert, sondern es wird eine neue erstellt und über currentConfig
verfügbar gemacht.
Mit Firebase Remote Config können Sie In-App-Standardwerte für Schlüssel/Wert-Paare erstellen, die Sie über die Firebase Console überschreiben können. Außerdem können Sie mithilfe von Regeln und Bedingungen Variationen der App-Nutzerumgebung für verschiedene Nutzersegmente bereitstellen. Firebase Remote Config implementiert eine Singleton-Klasse, die die Schlüssel/Wert-Paare für Ihre App verfügbar macht. Anfangs gibt das Singleton die Standardwerte zurück, die Sie in der App definieren. Sie können jederzeit einen neuen Satz von Werten vom Server abrufen. Nachdem der neue Satz erfolgreich abgerufen wurde, können Sie auswählen, wann er aktiviert werden soll, damit die neuen Werte für die App verfügbar sind.
Vorgeschlagene Migrationsstrategie
Sie können zu Firebase Remote Config wechseln, indem Sie die Schlüssel/Wert-Paare Ihrer Parse-Konfiguration in die Firebase Console kopieren und dann eine neue Version der App mit Firebase Remote Config bereitstellen.
Wenn Sie sowohl Parse Config als auch Firebase Remote Config ausprobieren möchten, können Sie eine neue Version der App bereitstellen, die beide SDKs verwendet, bis genügend Nutzer von der Version mit nur Parse migriert sind.
Codevergleich
Parsen
[PFConfig getConfigInBackgroundWithBlock:^(PFConfig *config, NSError *error) {
if (!error) {
NSLog(@"Yay! Config was fetched from the server.");
} else {
NSLog(@"Failed to fetch. Using Cached Config.");
config = [PFConfig currentConfig];
}
NSString *welcomeMessage = config[@"welcomeMessage"];
if (!welcomeMessage) {
NSLog(@"Falling back to default message.");
welcomeMessage = @"Welcome!";
}
}];
Firebase
FIRRemoteConfig remoteConfig = [FIRRemoteConfig remoteConfig];
// Set defaults from a plist file
[remoteConfig setDefaultsFromPlistFileName:@"RemoteConfigDefaults"];
[remoteConfig fetchWithCompletionHandler:^(FIRRemoteConfigFetchStatus status, NSError *error) {
if (status == FIRRemoteConfigFetchStatusSuccess) {
NSLog(@"Yay! Config was fetched from the server.");
// Once the config is successfully fetched it must be activated before newly fetched
// values are returned.
[self.remoteConfig activateFetched];
} else {
NSLog(@"Failed to fetch. Using last fetched or default.");
}
}];
// ...
// When this is called, the value of the latest fetched and activated config is returned;
// if there's none, the default value is returned.
NSString welcomeMessage = remoteConfig[@"welcomeMessage"].stringValue;