Google is committed to advancing racial equity for Black communities. See how.
Эта страница была переведа с помощью Cloud Translation API.
Switch to English

Перенесите приложение Parse iOS на Firebase

Если вы являетесь пользователем Parse и ищете альтернативное решение Backend as Service, Firebase может стать идеальным выбором для вашего приложения iOS.

В этом руководстве описывается, как интегрировать определенные службы в ваше приложение. Основные инструкции по настройке Firebase см. В руководстве по установке iOS .

Гугл Аналитика

Google Analytics - это бесплатное решение для измерения приложений, которое дает представление об использовании приложений и вовлеченности пользователей. Аналитика интегрируется с функциями Firebase и предоставляет вам неограниченные отчеты по 500 различным событиям, которые вы можете определить с помощью Firebase SDK.

См. Документы Google Analytics, чтобы узнать больше.

Предлагаемая миграционная стратегия

Использование различных поставщиков аналитики является распространенным сценарием, который легко применим к Google Analytics. Просто добавьте его в свое приложение, чтобы воспользоваться преимуществами событий и пользовательских свойств, которые автоматически собирает Google Analytics, таких как первое открытие, обновление приложения, модель устройства, возраст.

Для пользовательских событий и пользовательских свойств вы можете использовать стратегию двойной записи, используя Parse Analytics и Google Analytics для регистрации событий и свойств, что позволяет постепенно развертывать новое решение.

Сравнение кода

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];
 

Гугл Аналитика

 // 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 в реальном времени

База данных Firebase Realtime - это облачная база данных NoSQL. Данные хранятся в формате JSON и синхронизируются в режиме реального времени с каждым подключенным клиентом.

Посмотрите документы базы данных Firebase Realtime, чтобы узнать больше.

Различия с данными разбора

Объекты

В Parse вы сохраняете PFObject или его подкласс, который содержит пары ключ-значение JSON-совместимых данных. Данные без схемы, что означает, что вам не нужно указывать, какие ключи существуют для каждого PFObject .

Все данные базы данных Firebase Realtime хранятся в виде объектов 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
  }
}];
 
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
  }
}];
 
Для получения более подробной информации, ознакомьтесь с Руководством по чтению и записи данных на iOS .

Отношения между данными

PFObject может иметь отношение с другим PFObject : любой объект может использовать другие объекты в качестве значений.

В базе данных Firebase Realtime отношения лучше выражаются с помощью плоских структур данных, которые разбивают данные на отдельные пути, чтобы их можно было эффективно загружать в отдельных вызовах.

Ниже приведен пример того, как вы можете структурировать отношения между постами в приложении для ведения блогов и их авторами.

Анализировать
 // 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]
 

Следующий макет данных является результатом.

{
  // 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]);
  }
}];
 
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);
}];
 
Для получения дополнительной информации о доступных типах прослушивателей событий и о том, как упорядочить и отфильтровать данные, ознакомьтесь с Руководством по чтению и записи данных на iOS .

Предлагаемая миграционная стратегия

Переосмыслите ваши данные

База данных Firebase Realtime оптимизирована для синхронизации данных в миллисекундах между всеми подключенными клиентами, и результирующая структура данных отличается от основных данных Parse. Это означает, что первым шагом вашей миграции является рассмотрение того, какие изменения требуют ваши данные, в том числе:

  • Как ваши объекты Parse должны отображаться в данные Firebase
  • Если у вас есть отношения родитель-ребенок, как разделить ваши данные по разным путям, чтобы их можно было эффективно загружать в отдельных вызовах.

Перенос ваших данных

После того, как вы решите, как структурировать ваши данные в Firebase, вам нужно спланировать, как обрабатывать период, в течение которого ваше приложение должно записывать в обе базы данных. Ваш выбор:

Фоновая синхронизация

В этом сценарии у вас есть две версии приложения: старая версия, которая использует Parse, и новая версия, которая использует Firebase. Синхронизация между двумя базами данных обрабатывается Parse Cloud Code (Parse to Firebase), при этом ваш код прослушивает изменения в Firebase и синхронизирует эти изменения с Parse. Прежде чем вы сможете начать использовать новую версию, вы должны:

  • Преобразуйте существующие данные анализа в новую структуру Firebase и запишите их в базу данных Firebase Realtime.
  • Напишите функции Parse Cloud Code, которые используют API-интерфейс Firebase REST для записи в базу данных Firebase Realtime изменений, внесенных в данные анализа старыми клиентами.
  • Напишите и разверните код, который прослушивает изменения в Firebase и синхронизирует их с базой данных Parse.

Этот сценарий обеспечивает четкое разделение старого и нового кода и делает клиентов простыми. Задачами этого сценария являются обработка больших наборов данных при первоначальном экспорте и обеспечение того, чтобы двунаправленная синхронизация не генерировала бесконечную рекурсию.

Двойная запись

В этом сценарии вы пишете новую версию приложения, которая использует Firebase и Parse, используя код Parse Cloud для синхронизации изменений, внесенных старыми клиентами, из данных Parse в базу данных реального времени Firebase. Когда достаточное количество людей мигрировало из версии приложения только для Parse, вы можете удалить код Parse из версии с двойной записью.

Этот сценарий не требует никакого кода на стороне сервера. Его недостатками является то, что данные, к которым нет доступа, не переносятся, а размер вашего приложения увеличивается за счет использования обоих SDK.

Firebase Authentication

Аутентификация Firebase может аутентифицировать пользователей, используя пароли и популярные федеративные провайдеры идентификации, такие как Google, Facebook и Twitter. Он также предоставляет библиотеки пользовательского интерфейса, чтобы сэкономить значительные средства, необходимые для реализации и поддержки полной аутентификации вашего приложения на всех платформах.

Посмотрите документы Аутентификации Firebase, чтобы узнать больше.

Отличия от Parse Auth

Parse предоставляет специализированный пользовательский класс PFUser который автоматически обрабатывает функции, необходимые для управления учетными записями пользователей. PFUser является подклассом PFObject , что означает, что пользовательские данные доступны в Parse Data и могут быть расширены дополнительными полями, как любой другой PFObject .

FIRUser имеет фиксированный набор основных свойств - уникальный идентификатор, основной адрес электронной почты, имя и URL-адрес фотографии - хранящиеся в базе данных пользователей отдельного проекта; эти свойства могут быть обновлены пользователем. Вы не можете добавлять другие свойства к объекту FIRUser напрямую; вместо этого вы можете сохранить дополнительные свойства в вашей базе данных Firebase Realtime.

Ниже приведен пример того, как вы можете зарегистрировать пользователя и добавить дополнительное поле номера телефона.

Анализировать
 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"];
  }
}];
 

Предлагаемая миграционная стратегия

Перенос аккаунтов

Чтобы перенести учетные записи пользователей из Parse в Firebase, экспортируйте свою пользовательскую базу данных в файл JSON или CSV, а затем импортируйте файл в свой проект Firebase с помощью командной строки Firebase auth:import .

Сначала экспортируйте свою пользовательскую базу данных из консоли 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, используя стратегии, описанные в разделе переноса данных . Если вы переносите учетные записи, используя процесс, описанный в разделе переноса учетных записей , ваши учетные записи 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.

Предлагаемая миграционная стратегия

Токены устройства миграции

В то время как Parse использует токены устройства APN для назначения установок для уведомлений, FCM использует маркеры регистрации FCM, сопоставленные с токенами устройства APN. Просто добавьте FCM SDK в приложение для iOS, и он автоматически получит токен 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 (succedeed) {
    [[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 Config

С помощью Parse config вы можете добавить пары ключ / значение в свое приложение на панели мониторинга Parse Config, а затем получить PFConfig на клиенте. Каждый экземпляр 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 only.

Сравнение кода

Анализировать

 [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;