Если вы являетесь пользователем Parse и ищете альтернативное решение Backend as a Service, Firebase может стать идеальным выбором для вашего приложения для iOS.
В этом руководстве описывается, как интегрировать определенные сервисы в ваше приложение. Основные инструкции по настройке Firebase см. в руководстве по настройке iOS+ .
Google Analytics
Google Analytics — это бесплатное решение для измерения приложений, которое дает представление об использовании приложений и взаимодействии с пользователями. Analytics интегрируется со всеми функциями Firebase и предоставляет вам неограниченные отчеты по 500 различным событиям, которые вы можете определить с помощью Firebase SDK.
Дополнительную информацию см. в документации Google Analytics .
Предлагаемая стратегия миграции
Использование разных поставщиков аналитики — распространенный сценарий, который легко применим к Google Analytics . Просто добавьте его в свое приложение, чтобы получать выгоду от событий и свойств пользователя, которые автоматически собирает Analytics , таких как первое открытие, обновление приложения, модель устройства, возраст.
Для пользовательских событий и свойств пользователя вы можете использовать стратегию двойной записи, используя как Parse Analytics, так и Google 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 — это база данных NoSQL, размещенная в облаке. Данные хранятся в формате JSON и синхронизируются в режиме реального времени с каждым подключенным клиентом.
Дополнительную информацию см. в документации Firebase Realtime Database .
Различия с анализом данных
Объекты
В Parse вы храните PFObject
или его подкласс, который содержит пары ключ-значение JSON-совместимых данных. Данные не имеют схемы, что означает, что вам не нужно указывать, какие ключи существуют в каждом PFObject
.
Все данные Firebase Realtime Database хранятся в виде объектов JSON, и для PFObject
не существует эквивалента; вы просто записываете в дерево JSON значения типов, которые соответствуют доступным типам JSON.
Ниже приведен пример того, как можно сохранить рекорды в игре.
Разобрать
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
}
}];
Огневая база
// 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
}
}];
Отношения между данными
PFObject
может иметь связь с другим PFObject
: любой объект может использовать другие объекты в качестве значений.
В Firebase Realtime Database отношения лучше выражаются с помощью плоских структур данных, которые разбивают данные на отдельные пути, чтобы их можно было эффективно загружать отдельными вызовами.
Ниже приведен пример того, как вы можете структурировать отношения между сообщениями в приложении для ведения блога и их авторами.
Разобрать
// 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];
Огневая база
// 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]
Результатом является следующая структура данных.
{ // 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" } ... } }
Чтение данных
В Parse вы читаете данные, используя либо идентификатор конкретного объекта Parse, либо выполняя запросы с помощью PFQuery
.
В Firebase вы получаете данные, присоединяя асинхронный прослушиватель к ссылке на базу данных. Прослушиватель запускается один раз для исходного состояния данных и еще раз при изменении данных, поэтому вам не нужно добавлять какой-либо код, чтобы определить, изменились ли данные.
Ниже приведен пример того, как можно получить результаты для конкретного игрока на основе примера, представленного в разделе «Объекты» .
Разобрать
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]);
}
}];
Огневая база
// 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);
}];
Предлагаемая стратегия миграции
Переосмыслите свои данные
Firebase Realtime Database оптимизирована для синхронизации данных за миллисекунды между всеми подключенными клиентами, а результирующая структура данных отличается от основных данных Parse. Это означает, что первым шагом вашей миграции является рассмотрение того, какие изменения потребуются вашим данным, в том числе:
- Как ваши объекты Parse должны сопоставляться с данными Firebase
- Если у вас есть отношения «родитель-потомок», как разделить ваши данные по разным путям, чтобы их можно было эффективно загружать отдельными вызовами.
Перенесите свои данные
После того, как вы решите, как структурировать свои данные в Firebase, вам необходимо спланировать, как обрабатывать период, в течение которого вашему приложению необходимо выполнять запись в обе базы данных. Ваш выбор:
Фоновая синхронизация
В этом сценарии у вас есть две версии приложения: старая версия, использующая Parse, и новая версия, использующая Firebase. Синхронизация между двумя базами данных осуществляется с помощью Parse Cloud Code (Parse to Firebase), при этом ваш код прослушивает изменения в Firebase и синхронизирует эти изменения с Parse. Прежде чем вы сможете начать использовать новую версию, вам необходимо:
- Преобразуйте существующие данные анализа в новую структуру Firebase и запишите их в Firebase Realtime Database .
- Напишите функции синтаксического облачного кода, которые используют REST API Firebase для записи в Firebase Realtime Database изменений, внесенных в данные синтаксического анализа старыми клиентами.
- Напишите и разверните код, который прослушивает изменения в Firebase и синхронизирует их с базой данных Parse.
Этот сценарий обеспечивает четкое разделение старого и нового кода и упрощает работу клиентов. Проблемы этого сценария заключаются в обработке больших наборов данных при первоначальном экспорте и обеспечении того, чтобы двунаправленная синхронизация не приводила к бесконечной рекурсии.
Двойная запись
В этом сценарии вы пишете новую версию приложения, которая использует как Firebase, так и Parse, используя Parse Cloud Code для синхронизации изменений, внесенных старыми клиентами, из данных анализа в Firebase Realtime Database . Когда достаточное количество людей перейдет с версии приложения только для синтаксического анализа, вы можете удалить код синтаксического анализа из версии с двойной записью.
Этот сценарий не требует кода на стороне сервера. Его недостатки заключаются в том, что данные, к которым нет доступа, не переносятся, а размер вашего приложения увеличивается за счет использования обоих SDK.
Firebase Authentication
Firebase Authentication может аутентифицировать пользователей с помощью паролей и популярных поставщиков федеративных удостоверений, таких как Google, Facebook и Twitter. Он также предоставляет библиотеки пользовательского интерфейса, позволяющие сэкономить значительные инвестиции, необходимые для реализации и поддержки полной аутентификации вашего приложения на всех платформах.
Дополнительную информацию см. в документации Firebase Authentication .
Различия с аутентификацией синтаксического анализа
Parse предоставляет специализированный пользовательский класс под названием PFUser
, который автоматически выполняет функции, необходимые для управления учетными записями пользователей. PFUser
— это подкласс PFObject
, что означает, что пользовательские данные доступны в Parse Data и могут быть расширены дополнительными полями, как и любой другой PFObject
.
FIRUser
имеет фиксированный набор основных свойств — уникальный идентификатор, основной адрес электронной почты, имя и URL-адрес фотографии — которые хранятся в базе данных пользователей отдельного проекта; эти свойства могут быть обновлены пользователем. Вы не можете напрямую добавлять другие свойства к объекту FIRUser
; вместо этого вы можете сохранить дополнительные свойства в Firebase Realtime Database .
Ниже приведен пример того, как можно зарегистрировать пользователя и добавить дополнительное поле номера телефона.
Разобрать
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"];
}
}];
Огневая база
[[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"];
}
}];
Предлагаемая стратегия миграции
Перенос учетных записей
Чтобы перенести учетные записи пользователей из Parse в Firebase, экспортируйте базу данных пользователей в файл JSON или CSV, а затем импортируйте файл в свой проект Firebase с помощью команды auth:import
Firebase CLI.
Сначала экспортируйте свою пользовательскую базу данных из консоли Parse или из собственной базы данных. Например, файл JSON, экспортированный из консоли Parse, может выглядеть следующим образом:
{ // 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", ... }
Затем преобразуйте экспортированный файл в формат, требуемый интерфейсом командной строки Firebase. Используйте objectId
ваших пользователей Parse в качестве localId
ваших пользователей Firebase. Кроме того, base64 кодирует значения bcryptPassword
из Parse и использует их в поле passwordHash
. Например:
{ "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 } ] } ] }
Наконец, импортируйте преобразованный файл с помощью Firebase CLI, указав bcrypt в качестве алгоритма хеширования:
firebase auth:import account_file.json --hash-algo=BCRYPT
Перенос пользовательских данных
Если вы храните дополнительные данные для своих пользователей, вы можете перенести их в Firebase Realtime Database используя стратегии, описанные в разделе переноса данных . Если вы переносите учетные записи, используя процедуру, описанную в разделе «Миграция учетных записей» , ваши учетные записи Firebase будут иметь те же идентификаторы, что и ваши учетные записи Parse, что позволяет вам легко переносить и воспроизводить любые отношения, связанные с идентификатором пользователя.
Firebase Cloud Messaging
Firebase Cloud Messaging ( FCM ) — это кроссплатформенное решение для обмена сообщениями, которое позволяет надежно доставлять сообщения и уведомления бесплатно. Компоновщик уведомлений – это бесплатная служба, созданная на базе Firebase Cloud Messaging , которая позволяет отправлять целевые пользовательские уведомления разработчикам мобильных приложений.
Дополнительную информацию см . в документации Firebase Cloud Messaging .
Различия с анализом push-уведомлений
Каждое приложение Parse, установленное на устройстве, зарегистрированном для уведомлений, имеет связанный объект Installation
, в котором хранятся все данные, необходимые для таргетирования уведомлений. Installation
является подклассом PFUser
, что означает, что вы можете добавлять любые дополнительные данные в свои экземпляры Installation
.
Компоновщик уведомлений предоставляет предварительно определенные сегменты пользователей на основе такой информации, как приложение, версия приложения и язык устройства. Вы можете создавать более сложные сегменты пользователей, используя события и свойства Google Analytics для создания аудитории. Чтобы узнать больше, ознакомьтесь со справочным руководством по аудиториям . Эта информация о таргетинге не отображается в Firebase Realtime Database .
Предлагаемая стратегия миграции
Миграция токенов устройств
В то время как Parse использует токены устройств APN для целевых установок для уведомлений, FCM использует токены регистрации FCM , сопоставленные с токенами устройств APN. Просто добавьте FCM SDK в свое приложение Apple, и оно автоматически получит токен FCM .
Перенос каналов в темы FCM
Если вы используете каналы Parse для отправки уведомлений, вы можете перейти на темы FCM , которые предоставляют ту же модель издатель-подписчик. Чтобы обработать переход от Parse к FCM , вы можете написать новую версию приложения, которая использует Parse SDK для отказа от подписки на каналы Parse и FCM SDK для подписки на соответствующие темы FCM .
Например, если ваш пользователь подписан на тему «Гиганты», вы должны сделать что-то вроде:
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
}
}];
Используя эту стратегию, вы можете отправлять сообщения как на канал Parse, так и на соответствующую тему FCM , поддерживая пользователей как старых, так и новых версий. Когда достаточное количество пользователей перейдет из версии приложения только для Parse, вы можете закрыть эту версию и начать отправку только с использованием FCM .
Дополнительную информацию см . в документации по темам FCM .
Firebase Remote Config
Firebase Remote Config — это облачный сервис, который позволяет изменять поведение и внешний вид вашего приложения, не требуя от пользователей загрузки обновления приложения. При использовании Remote Config вы создаете в приложении значения по умолчанию, которые управляют поведением и внешним видом вашего приложения. Затем вы можете позже использовать консоль Firebase, чтобы переопределить значения по умолчанию в приложении для всех пользователей приложения или для сегментов вашей пользовательской базы.
Firebase Remote Config может быть очень полезен во время миграции в тех случаях, когда вы хотите протестировать различные решения и иметь возможность динамически переводить больше клиентов к другому провайдеру. Например, если у вас есть версия вашего приложения, которая использует для данных как Firebase, так и Parse, вы можете использовать правило случайного процентиля, чтобы определить, какие клиенты читают из Firebase, и постепенно увеличивать процент.
Чтобы узнать больше о Firebase Remote Config , см. введение Remote Config .
Различия с конфигурацией синтаксического анализа
С помощью конфигурации Parse вы можете добавить пары ключ/значение в свое приложение на панели управления Parse Config, а затем получить PFConfig
на клиенте. Каждый экземпляр PFConfig
, который вы получаете, всегда является неизменяемым. Когда вы в будущем получите новый PFConfig
из сети, он не изменит ни один существующий экземпляр PFConfig
, а вместо этого создаст новый и сделает его доступным через currentConfig
.
С помощью Firebase Remote Config вы создаете в приложении значения по умолчанию для пар ключ/значение, которые можно переопределить из консоли Firebase, а также можете использовать правила и условия, чтобы предоставлять варианты взаимодействия с пользователем вашего приложения для разных сегментов вашей пользовательской базы. Firebase Remote Config реализует одноэлементный класс, который делает пары ключ/значение доступными для вашего приложения. Первоначально синглтон возвращает значения по умолчанию, которые вы определяете в приложении. Вы можете получить новый набор значений с сервера в любой удобный для вашего приложения момент; после того, как новый набор будет успешно получен, вы можете выбрать, когда его активировать, чтобы новые значения были доступны приложению.
Предлагаемая стратегия миграции
Вы можете перейти к Firebase Remote Config , скопировав пары ключ/значение вашей конфигурации Parse в консоль Firebase, а затем развернув новую версию приложения, использующую Firebase Remote Config .
Если вы хотите поэкспериментировать как с Parse Config, так и Firebase Remote Config , вы можете развернуть новую версию приложения, использующую оба SDK, до тех пор, пока достаточное количество пользователей не перейдет с версии только с Parse.
Сравнение кода
Разобрать
[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!";
}
}];
Огневая база
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;