Jeśli jesteś użytkownikiem Parse i szukasz alternatywnego rozwiązania typu Backend as a Service, Firebase może być idealnym wyborem dla Twojej aplikacji na iOS.
W tym przewodniku opisano, jak integrować określone usługi z aplikacją. Podstawowe instrukcje konfigurowania Firebase znajdziesz w przewodniku Konfigurowanie iOS+.
Google Analytics
Google Analytics to bezpłatne rozwiązanie do pomiaru skuteczności aplikacji, które dostarcza informacji o jej użytkowaniu i zaangażowaniu użytkowników. Analytics integruje się z funkcjami Firebase i zapewnia nieograniczone raportowanie nawet 500 różnych zdarzeń, które możesz definiować za pomocą pakietu SDK Firebase.
Aby dowiedzieć się więcej, zapoznaj się z dokumentacją Google Analytics.
Sugerowana strategia migracji
Korzystanie z różnych usług analitycznych to typowy scenariusz, który łatwo zastosować w przypadku Google Analytics. Wystarczy, że dodasz go do aplikacji, aby korzystać z danych o zdarzeniach i właściwościach użytkowników, które Analyticszbiera automatycznie, np. o pierwszym otwarciu, aktualizacji aplikacji, modelu urządzenia czy jego wieku.
W przypadku zdarzeń niestandardowych i właściwości użytkownika możesz zastosować strategię podwójnego zapisu, używając zarówno Parse Analytics, jak i Google Analytics do rejestrowania zdarzeń i właściwości. Dzięki temu możesz stopniowo wdrażać nowe rozwiązanie.
Porównanie kodu
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
Firebase Realtime Database to chmurowa baza danych NoSQL. Dane są przechowywane w formacie JSON i synchronizowane w czasie rzeczywistym ze wszystkimi połączonymi klientami.
Aby dowiedzieć się więcej, zapoznaj się z dokumentacją Firebase Realtime Database.
Różnice w przypadku danych z parse
Obiekty
W usłudze Parse przechowujesz obiekt PFObject
lub jego podklasę, który zawiera pary klucz-wartość danych zgodnych z formatem JSON. Dane są bezschematowe, co oznacza, że nie musisz określać, jakie klucze występują w każdym elemencie PFObject
.
Wszystkie dane Firebase Realtime Database są przechowywane jako obiekty JSON, a PFObject
nie ma odpowiednika. Wystarczy, że zapiszesz wartości typu JSON w drzewie, które odpowiadają dostępnym typom JSON.
Poniżej znajdziesz przykład zapisywania rekordów w grze.
Analizuj
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
}
}];
Relacje między danymi
PFObject
może mieć relację z innym PFObject
: każdy obiekt może używać innych obiektów jako wartości.
W Firebase Realtime Database relacje są lepiej wyrażane za pomocą płaskich struktur danych, które dzielą dane na osobne ścieżki, aby można je było efektywnie pobierać w osobnych wywołaniach.
Poniżej przedstawiamy przykład struktury relacji między postami w aplikacji do blogowania a ich autorami.
Analizuj
// 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]
Wynikiem jest poniższy układ danych.
{ // 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" } ... } }
Czytanie danych
W usłudze Parse możesz odczytywać dane, używając identyfikatora konkretnego obiektu Parse lub wykonując zapytania za pomocą PFQuery
.
W Firebase dane pobierasz, dołączając asynchronicznego odbiornika do odwołania do bazy danych. Listener jest uruchamiany raz w przypadku początkowego stanu danych i ponownie, gdy dane ulegną zmianie, więc nie musisz dodawać żadnego kodu, aby określić, czy dane się zmieniły.
Poniżej znajdziesz przykładowy sposób pobierania wyników konkretnego gracza na podstawie przykładu przedstawionego w sekcji „Obiekty”.
Analizuj
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);
}];
Sugerowana strategia migracji
Pomyśl, co jesz
Firebase Realtime Database jest zoptymalizowany pod kątem synchronizacji danych w milisekundach na wszystkich połączonych klientach, a wynikająca z tego struktura danych różni się od danych podstawowych usługi Parse. Oznacza to, że pierwszym krokiem w migracji jest rozważenie, jakich zmian wymagają Twoje dane, w tym:
- Jak mapować obiekty Parse na dane Firebase
- Jeśli masz relacje nadrzędne i podrzędne, jak podzielić dane na różne ścieżki, aby można je było efektywnie pobierać w osobnych wywołaniach.
Migracja danych
Po podjęciu decyzji o strukturze danych w Firebase musisz zaplanować, jak postąpić w okresie, w którym aplikacja musi zapisywać dane w obu bazach danych. Opcje do wyboru:
Synchronizacja w tle
W tym scenariuszu masz 2 wersje aplikacji: starą, która korzysta z Parse, oraz nową, która korzysta z Firebase. Synchronizacja między tymi bazami danych jest obsługiwana przez kod Parse Cloud (z Parse do Firebase), a Twój kod wykrywa zmiany w Firebase i synchronizuje je z Parse. Zanim zaczniesz korzystać z nowej wersji, musisz:
- Przekształć istniejące dane Parse na nową strukturę Firebase i zapisz je w pliku Firebase Realtime Database.
- Napisać funkcje kodu Parse Cloud, które korzystają z interfejsu Firebase REST API, aby zapisywać w nich Firebase Realtime Database zmiany wprowadzone w danych Parse przez starszych klientów.
- Napisać i wdrożyć kod, który będzie nasłuchiwać zmian w Firebase i synchronizować je z bazą danych Parse.
Taki scenariusz zapewnia wyraźne oddzielenie starego i nowego kodu oraz upraszcza obsługę klientów. Wyzwania związane z tym scenariuszem to obsługa dużych zbiorów danych podczas początkowego eksportu oraz zapewnienie, że synchronizacja dwukierunkowa nie spowoduje nieskończonej rekurencji.
Podwójny zapis
W tym scenariuszu piszesz nową wersję aplikacji, która korzysta zarówno z Firebase, jak i z Parse. Używasz kodu Parse Cloud do synchronizowania zmian wprowadzonych przez starych klientów z danych Parse na serwer Firebase Realtime Database. Gdy wystarczająca liczba osób przejdzie z wersji aplikacji korzystającej tylko z Parse, możesz usunąć kod Parse z wersji z podwójnym zapisem.
W tym scenariuszu nie trzeba używać kodu po stronie serwera. Wadą tego rozwiązania jest to, że dane, do których nie ma dostępu, nie są migrowane, a rozmiar aplikacji zwiększa się z powodu używania obu pakietów SDK.
Firebase Authentication
Firebase Authentication może uwierzytelniać użytkowników za pomocą haseł i popularnych dostawców tożsamości sfederowanej, takich jak Google, Facebook i Twitter. Udostępnia też biblioteki interfejsu użytkownika, aby zaoszczędzić Ci znaczne nakłady finansowe wymagane do wdrożenia i utrzymania pełnego procesu uwierzytelniania w aplikacji na wszystkich platformach.
Aby dowiedzieć się więcej, zapoznaj się z dokumentacją Firebase Authentication.
Różnice w przypadku funkcji Parse Auth
Parse udostępnia wyspecjalizowaną klasę użytkowników o nazwie PFUser
, która automatycznie obsługuje funkcje wymagane do zarządzania kontami użytkowników. PFUser
jest podklasą PFObject
, co oznacza, że dane użytkownika są dostępne w danych do analizy i można je rozszerzyć o dodatkowe pola, tak jak w przypadku dowolnego innego PFObject
.
Użytkownik FIRUser
ma stały zestaw podstawowych właściwości: unikalny identyfikator, podstawowy adres e-mail, imię i nazwisko oraz adres URL zdjęcia. Informacje te są przechowywane w oddzielnej bazie danych użytkowników danego projektu. Użytkownik może je aktualizować. Nie możesz bezpośrednio dodawać innych właściwości do obiektu FIRUser
. Zamiast tego możesz przechowywać dodatkowe właściwości w obiekcie Firebase Realtime Database.
Poniżej przedstawiamy przykład rejestracji użytkownika i dodawania dodatkowego pola numeru telefonu.
Analizuj
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"];
}
}];
Sugerowana strategia migracji
Migracja kont
Aby przenieść konta użytkowników z Parse do Firebase, wyeksportuj bazę danych użytkowników do pliku JSON lub CSV, a potem zaimportuj ten plik do projektu Firebase za pomocą polecenia auth:import
w konsoli wiersza poleceń Firebase.
Najpierw wyeksportuj bazę danych użytkowników z konsoli Parse lub z bazy hostowanej lokalnie. Plik JSON wyeksportowany z konsoli Parse może wyglądać tak:
{ // 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", ... }
Następnie przekształcaj wyeksportowany plik w taki format, którego wymaga interfejs wiersza poleceń Firebase. Użyj objectId
użytkowników Parse jako localId
użytkowników Firebase. Zakoduj też w formacie Base64 wartości bcryptPassword
z Parse i użyj ich w polu passwordHash
. Przykład:
{ "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 } ] } ] }
Na koniec zaimportuj przekształcony plik za pomocą interfejsu wiersza poleceń Firebase, określając bcrypt jako algorytm haszowania:
firebase auth:import account_file.json --hash-algo=BCRYPT
Migracja danych użytkownika
Jeśli przechowujesz dodatkowe dane użytkowników, możesz je przenieść do Firebase Realtime Database, korzystając ze strategii opisanych w sekcji Migracja danych. Jeśli zrealizujesz migrację kont za pomocą procesu opisanego w sekcji migracja kont, Twoje konta Firebase będą miały te same identyfikatory co konta Parse, co pozwoli Ci łatwo migrować i odtwarzać relacje powiązane z identyfikatorem użytkownika.
Firebase Cloud Messaging
Firebase Cloud Messaging (FCM) to wieloplatformowe rozwiązanie do przesyłania wiadomości, które umożliwia niezawodne przesyłanie wiadomości i powiadomień bez opłat. Narzędzie do tworzenia powiadomień to bezpłatna usługa oparta na Firebase Cloud Messaging, która umożliwia deweloperom aplikacji mobilnych wysyłanie ukierunkowanych powiadomień do użytkowników.
Aby dowiedzieć się więcej, zapoznaj się z dokumentacją Firebase Cloud Messaging .
Różnice w powiadomieniach push w usłudze Parse
Każda aplikacja Parse zainstalowana na urządzeniu zarejestrowanym na potrzeby powiadomień ma powiązany obiekt Installation
, w którym przechowujesz wszystkie dane potrzebne do kierowania powiadomień.
Installation
jest podklasą PFUser
, co oznacza, że możesz dodać dowolne dodatkowe dane do instancji Installation
.
Edytor powiadomień udostępnia wstępnie zdefiniowane segmenty użytkowników na podstawie takich informacji jak aplikacja, wersja aplikacji i język urządzenia. Korzystając z tych zdarzeń i właściwości, możesz tworzyć bardziej złożone segmenty użytkowników, aby tworzyć listy odbiorców.Google Analytics Więcej informacji znajdziesz w przewodniku pomocy dotyczącym odbiorców. Te informacje kierunkowe nie są widoczne w Firebase Realtime Database.
Sugerowana strategia migracji
Migracja tokenów urządzeń
Chociaż Parse używa tokenów urządzeń APN do kierowania powiadomień na instalacje, FCM używa tokenów rejestracji FCM mapowanych na tokeny urządzeń APN. Wystarczy dodać do aplikacji Apple pakiet SDK FCM, a on automatycznie pobierze token FCM.
Migracja kanałów do FCM Topics
Jeśli do wysyłania powiadomień używasz kanałów Parse, możesz przejść na tematy FCM, które zapewniają tę samą model wydawcy i subskrybenta. Aby przejść z Parse na FCM, możesz napisać nową wersję aplikacji, która używa pakietu SDK Parse do anulowania subskrypcji kanałów Parse i pakietu SDK FCM do subskrybowania odpowiednich tematów FCM.
Jeśli na przykład użytkownik subskrybuje temat „Giants”, możesz wykonać te czynności:
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
}
}];
Dzięki tej strategii możesz wysyłać wiadomości zarówno do kanału Parse, jak i do odpowiedniego FCMtematu, obsługując użytkowników zarówno starszej, jak i nowej wersji. Gdy wystarczająca liczba użytkowników przejdzie z wersji aplikacji korzystającej tylko z Parse, możesz wycofać tę wersję i rozpocząć wysyłanie tylko za pomocą FCM.
Aby dowiedzieć się więcej, zapoznaj się z dokumentami dotyczącymi FCM.
Firebase Remote Config
Firebase Remote Config to usługa w chmurze, która pozwala zmienić działanie i wygląd aplikacji bez potrzeby pobierania aktualizacji przez użytkowników. Dzięki Zdalnej konfiguracji tworzysz w aplikacji domyślne wartości, które kontrolują jej zachowanie i wygląd. Później możesz użyć konsoli Firebase, żeby nadpisywać domyślne wartości w aplikacjach wszystkich użytkowników lub ich wybranego segmentu.
Firebase Remote Config może być bardzo przydatna podczas migracji, gdy chcesz przetestować różne rozwiązania i dynamicznie przenosić większą liczbę klientów do innego dostawcy. Jeśli na przykład masz wersję aplikacji, która do przechowywania danych używa zarówno Firebase, jak i Parse, możesz użyć reguły losowego odsetka, aby określić, którzy klienci mają odczytywać dane z Firebase, a potem stopniowo zwiększać ten odsetek.
Więcej informacji o Firebase Remote Config znajdziesz w artykule Wprowadzenie do Remote Config.
Różnice w konfiguracji parsowania
Dzięki konfiguracji Parse możesz dodawać do aplikacji pary klucz-wartość na karcie Konfiguracja Parse, a potem pobierać PFConfig
na kliencie. Każda instancja PFConfig
, którą otrzymasz, jest zawsze niezmienna. Gdy w przyszłości pobierzesz nową wersję PFConfig
z sieci, nie zmieni to żadnej istniejącej instancji PFConfig
, ale utworzy nową i umieści ją w dostępnej sieci currentConfig
.
Dzięki Firebase Remote Config możesz tworzyć w aplikacji domyślne wartości par klucz-wartość, które możesz zastąpić w konsoli Firebase. Możesz też używać reguł i warunków, aby modyfikować funkcjonalność aplikacji w różnych segmentach jej użytkowników. Firebase Remote Config implementuje klasę singleton, która udostępnia pary klucz-wartość Twojej aplikacji. Początkowo singleton zwraca wartości domyślne zdefiniowane w aplikacji. Możesz pobrać nowy zestaw wartości z serwera w dowolnym dogodnym momencie. Po pomyślnym pobraniu nowego zestawu możesz wybrać, kiedy go aktywować, aby udostępnić nowe wartości aplikacji.
Sugerowana strategia migracji
Aby przejść na Firebase Remote Config, skopiuj pary klucz-wartość z konfiguracji Parse do konsoli Firebase, a potem wdróż nową wersję aplikacji, która korzysta z Firebase Remote Config.
Jeśli chcesz eksperymentować z obiema usługami – Parse Config i Firebase Remote Config – możesz wdrożyć nową wersję aplikacji, która korzysta z obu pakietów SDK, dopóki wystarczająca liczba użytkowników nie przejdzie z wersji tylko z Parse.
Porównanie kodu
Analizuj
[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;