Migra tu aplicación Parse para iOS a Firebase

Si es un usuario de Parse y busca una solución alternativa de backend como servicio, Firebase podría ser la opción ideal para su aplicación de iOS.

Esta guía describe cómo integrar servicios específicos en su aplicación. Para obtener instrucciones básicas de configuración de Firebase, consulta la guía de configuración de iOS+ .

Google analitico

Google Analytics es una solución gratuita de medición de aplicaciones que proporciona información sobre el uso de las aplicaciones y la participación de los usuarios. Analytics se integra con todas las funciones de Firebase y le proporciona informes ilimitados para hasta 500 eventos distintos que puede definir mediante el SDK de Firebase.

Consulte los documentos de Google Analytics para obtener más información.

Estrategia de migración sugerida

El uso de diferentes proveedores de análisis es un escenario común que se aplica fácilmente a Google Analytics. Simplemente agréguelo a su aplicación para beneficiarse de los eventos y las propiedades del usuario que Analytics recopila automáticamente, como la primera apertura, la actualización de la aplicación, el modelo del dispositivo y la edad.

Para eventos personalizados y propiedades de usuario, puede emplear una estrategia de doble escritura utilizando Parse Analytics y Google Analytics para registrar eventos y propiedades, lo que le permite implementar gradualmente la nueva solución.

Comparación de códigos

Análisis de análisis

// 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 analitico

// 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"
}];

Base de datos en tiempo real de Firebase

Firebase Realtime Database es una base de datos NoSQL alojada en la nube. Los datos se almacenan como JSON y se sincronizan en tiempo real con cada cliente conectado.

Consulte los documentos de Firebase Realtime Database para obtener más información.

Diferencias con los datos de análisis

Objetos

En Parse almacena un PFObject , o una subclase del mismo, que contiene pares clave-valor de datos compatibles con JSON. Los datos no tienen esquema, lo que significa que no es necesario especificar qué claves existen en cada PFObject .

Todos los datos de Firebase Realtime Database se almacenan como objetos JSON y no existe un equivalente para PFObject ; simplemente escribe en el árbol JSON los valores de los tipos que corresponden a los tipos JSON disponibles.

El siguiente es un ejemplo de cómo puedes guardar las puntuaciones más altas de un juego.

Analizar gramaticalmente
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
  }
}];
base de fuego
// 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
  }
}];
Para obtener más detalles, consulte la guía Leer y escribir datos en plataformas Apple .

Relaciones entre datos

Un PFObject puede tener una relación con otro PFObject : cualquier objeto puede utilizar otros objetos como valores.

En Firebase Realtime Database, las relaciones se expresan mejor utilizando estructuras de datos planas que dividen los datos en rutas separadas, de modo que se puedan descargar de manera eficiente en llamadas separadas.

El siguiente es un ejemplo de cómo podría estructurar la relación entre las publicaciones en una aplicación de blogs y sus autores.

Analizar gramaticalmente
// 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];
base de fuego
// 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]

El resultado es el siguiente diseño de datos.

{
  // 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"
    }
    ...
  }
}
Para obtener más detalles, consulte la guía Estructurar su base de datos .

Lectura de datos

En Parse, lees datos usando el ID de un objeto Parse específico o ejecutando consultas usando PFQuery .

En Firebase, puedes recuperar datos adjuntando un detector asincrónico a una referencia de base de datos. El oyente se activa una vez para el estado inicial de los datos y nuevamente cuando los datos cambian, por lo que no necesitará agregar ningún código para determinar si los datos cambiaron.

El siguiente es un ejemplo de cómo puedes recuperar puntuaciones de un jugador en particular, basado en el ejemplo presentado en la sección "Objetos" .

Analizar gramaticalmente
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]);
  }
}];
base de fuego
// 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);
}];
Para obtener más detalles sobre los tipos de detectores de eventos disponibles y sobre cómo ordenar y filtrar datos, consulte la guía Leer y escribir datos en plataformas Apple .

Estrategia de migración sugerida

Reconsidere sus datos

Firebase Realtime Database está optimizada para sincronizar datos en milisegundos en todos los clientes conectados, y la estructura de datos resultante es diferente de los datos principales de Parse. Esto significa que el primer paso de su migración es considerar qué cambios requieren sus datos, incluidos:

  • Cómo deben asignarse sus objetos Parse a los datos de Firebase
  • Si tiene relaciones entre padres e hijos, cómo dividir sus datos en diferentes rutas para que puedan descargarse de manera eficiente en llamadas separadas.

Migra tus datos

Después de decidir cómo estructurar tus datos en Firebase, debes planificar cómo manejar el período durante el cual tu aplicación necesita escribir en ambas bases de datos. Tus opciones son:

Sincronización en segundo plano

En este escenario, tienes dos versiones de la aplicación: la versión anterior que usa Parse y una versión nueva que usa Firebase. Las sincronizaciones entre las dos bases de datos son manejadas por Parse Cloud Code (Parse to Firebase), y su código escucha los cambios en Firebase y los sincroniza con Parse. Antes de poder comenzar a utilizar la nueva versión, debe:

  • Convierta sus datos de análisis existentes a la nueva estructura de Firebase y escríbalos en Firebase Realtime Database.
  • Escriba funciones de Parse Cloud Code que utilicen la API REST de Firebase para escribir en los cambios de Firebase Realtime Database realizados en Parse Data por clientes antiguos.
  • Escriba e implemente código que escuche los cambios en Firebase y los sincronice con la base de datos de Parse.

Este escenario garantiza una separación clara del código antiguo y nuevo y mantiene a los clientes simples. Los desafíos de este escenario son manejar grandes conjuntos de datos en la exportación inicial y garantizar que la sincronización bidireccional no genere una recursividad infinita.

Doble escritura

En este escenario, escribe una nueva versión de la aplicación que usa Firebase y Parse, usando Parse Cloud Code para sincronizar los cambios realizados por clientes antiguos desde Parse Data con Firebase Realtime Database. Cuando suficientes personas hayan migrado desde la versión de la aplicación solo de Parse, puede eliminar el código de Parse de la versión de doble escritura.

Este escenario no requiere ningún código del lado del servidor. Sus desventajas son que los datos a los que no se accede no se migran y que el tamaño de su aplicación aumenta mediante el uso de ambos SDK.

Autenticación de base de fuego

Firebase Authentication puede autenticar a los usuarios mediante contraseñas y proveedores de identidades federados populares como Google, Facebook y Twitter. También proporciona bibliotecas de interfaz de usuario para ahorrarle la importante inversión necesaria para implementar y mantener una experiencia de autenticación completa para su aplicación en todas las plataformas.

Consulte los documentos de autenticación de Firebase para obtener más información.

Diferencias con la autenticación de análisis

Parse proporciona una clase de usuario especializada llamada PFUser que maneja automáticamente la funcionalidad requerida para la administración de cuentas de usuario. PFUser es una subclase de PFObject , lo que significa que los datos del usuario están disponibles en Parse Data y se pueden ampliar con campos adicionales como cualquier otro PFObject .

Un FIRUser tiene un conjunto fijo de propiedades básicas (una identificación única, una dirección de correo electrónico principal, un nombre y una URL de foto) almacenadas en una base de datos de usuarios de un proyecto independiente; esas propiedades pueden ser actualizadas por el usuario. No puede agregar otras propiedades al objeto FIRUser directamente; en su lugar, puede almacenar las propiedades adicionales en su Firebase Realtime Database.

El siguiente es un ejemplo de cómo puede registrar un usuario y agregar un campo de número de teléfono adicional.

Analizar gramaticalmente
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"];
  }
}];
base de fuego
[[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"];
  }
}];

Estrategia de migración sugerida

Migrar cuentas

Para migrar cuentas de usuario de Parse a Firebase, exporte su base de datos de usuario a un archivo JSON o CSV, luego importe el archivo a su proyecto de Firebase usando el comando auth:import de Firebase CLI.

Primero, exporte su base de datos de usuarios desde la consola Parse o su base de datos autohospedada. Por ejemplo, un archivo JSON exportado desde la consola de Parse podría tener el siguiente aspecto:

{ // 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",
  ...
}

Luego, transforme el archivo exportado al formato requerido por Firebase CLI. Utilice el objectId de sus usuarios de Parse como el localId de sus usuarios de Firebase. Además, base64 codifica los valores bcryptPassword de Parse y los utiliza en el campo passwordHash . Por ejemplo:

{
  "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
        }
      ]
    }
  ]
}

Finalmente, importe el archivo transformado con Firebase CLI, especificando bcrypt como algoritmo hash:

firebase auth:import account_file.json --hash-algo=BCRYPT

Migrar datos de usuario

Si almacena datos adicionales para sus usuarios, puede migrarlos a Firebase Realtime Database usando las estrategias descritas en la sección de migración de datos . Si migra cuentas utilizando el flujo descrito en la sección de migración de cuentas , sus cuentas de Firebase tienen los mismos identificadores que sus cuentas de Parse, lo que le permite migrar y reproducir fácilmente cualquier relación ingresada por el identificador de usuario.

Mensajería en la nube de Firebase

Firebase Cloud Messaging (FCM) es una solución de mensajería multiplataforma que le permite enviar mensajes y notificaciones de manera confiable y sin costo. El compositor de notificaciones es un servicio sin costo integrado en Firebase Cloud Messaging que permite notificaciones de usuario específicas para desarrolladores de aplicaciones móviles.

Consulte los documentos de Firebase Cloud Messaging para obtener más información.

Diferencias con las notificaciones push de Parse

Cada aplicación Parse instalada en un dispositivo registrado para recibir notificaciones tiene un objeto Installation asociado, donde se almacenan todos los datos necesarios para recibir notificaciones. Installation es una subclase de PFUser , lo que significa que puede agregar cualquier dato adicional que desee a sus instancias Installation .

El compositor de notificaciones proporciona segmentos de usuarios predefinidos basados ​​en información como la aplicación, la versión de la aplicación y el idioma del dispositivo. Puede crear segmentos de usuarios más complejos utilizando eventos y propiedades de Google Analytics para crear audiencias. Consulte la guía de ayuda para audiencias para obtener más información. Esta información de orientación no es visible en Firebase Realtime Database.

Estrategia de migración sugerida

Migrar tokens de dispositivo

Mientras que Parse usa tokens de dispositivo APN para dirigir las instalaciones a las notificaciones, FCM usa tokens de registro de FCM asignados a los tokens de dispositivo APN. Simplemente agregue el SDK de FCM a su aplicación de Apple y obtendrá un token de FCM automáticamente .

Migración de canales a temas de FCM

Si utiliza canales de Parse para enviar notificaciones, puede migrar a temas de FCM, que proporcionan el mismo modelo de editor-suscriptor. Para manejar la transición de Parse a FCM, puede escribir una nueva versión de la aplicación que utilice el SDK de Parse para cancelar la suscripción a los canales de Parse y el SDK de FCM para suscribirse a los temas de FCM correspondientes.

Por ejemplo, si tu usuario está suscrito al tema "Gigantes", harías algo como:

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

Con esta estrategia, puede enviar mensajes tanto al canal Parse como al tema FCM correspondiente, lo que admite usuarios de versiones antiguas y nuevas. Cuando suficientes usuarios hayan migrado desde la versión de solo Parse de la aplicación, puede desactivar esa versión y comenzar a enviar usando FCM únicamente.

Consulte los documentos de temas de FCM para obtener más información.

Configuración remota de Firebase

Firebase Remote Config es un servicio en la nube que te permite cambiar el comportamiento y la apariencia de tu aplicación sin necesidad de que los usuarios descarguen una actualización de la aplicación. Cuando usas Remote Config, creas valores predeterminados en la aplicación que controlan el comportamiento y la apariencia de tu aplicación. Luego, podrás usar Firebase console para anular los valores predeterminados en la aplicación para todos los usuarios de la aplicación o para segmentos de tu base de usuarios.

Firebase Remote Config puede ser muy útil durante sus migraciones en los casos en que desee probar diferentes soluciones y poder cambiar dinámicamente más clientes a un proveedor diferente. Por ejemplo, si tiene una versión de su aplicación que usa Firebase y Parse para los datos, puede usar una regla de percentil aleatoria para determinar qué clientes leen desde Firebase y aumentar gradualmente el porcentaje.

Para obtener más información sobre Firebase Remote Config, consulta la introducción a Remote Config .

Diferencias con la configuración de Parse

Con la configuración de Parse, puede agregar pares clave/valor a su aplicación en el Panel de configuración de Parse y luego recuperar PFConfig en el cliente. Cada instancia PFConfig que obtienes es siempre inmutable. Cuando recupere un nuevo PFConfig en el futuro de la red, no modificará ninguna instancia PFConfig existente, sino que creará una nueva y la pondrá a disposición a través de currentConfig .

Con Firebase Remote Config, creas valores predeterminados en la aplicación para pares clave/valor que puedes anular desde Firebase console, y puedes usar reglas y condiciones para proporcionar variaciones en la experiencia de usuario de tu aplicación a diferentes segmentos de tu base de usuarios. Firebase Remote Config implementa una clase singleton que hace que los pares clave/valor estén disponibles para tu aplicación. Inicialmente, el singleton devuelve los valores predeterminados que usted define en la aplicación. Puede recuperar un nuevo conjunto de valores del servidor en cualquier momento conveniente para su aplicación; Una vez que el nuevo conjunto se haya obtenido correctamente, puede elegir cuándo activarlo para que los nuevos valores estén disponibles para la aplicación.

Estrategia de migración sugerida

Puede pasar a Firebase Remote Config copiando los pares clave/valor de su configuración de Parse en Firebase console y luego implementando una nueva versión de la aplicación que usa Firebase Remote Config.

Si desea experimentar con Parse Config y Firebase Remote Config, puede implementar una nueva versión de la aplicación que use ambos SDK hasta que suficientes usuarios hayan migrado desde la versión exclusiva de Parse.

Comparación de códigos

Analizar gramaticalmente

[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!";
  }
}];

base de fuego

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;