Migra la tua app Parse iOS su Firebase

Se sei un utente Parse alla ricerca di una soluzione Backend as a Service alternativa, Firebase potrebbe essere la scelta ideale per la tua app iOS.

Questa guida descrive come integrare servizi specifici nella tua app. Per le istruzioni di base sulla configurazione di Firebase, consulta la guida alla configurazione di iOS+ .

statistiche di Google

Google Analytics è una soluzione gratuita di misurazione delle app che fornisce informazioni dettagliate sull'utilizzo delle app e sul coinvolgimento degli utenti. Analytics si integra con tutte le funzionalità di Firebase e fornisce report illimitati per un massimo di 500 eventi distinti che puoi definire utilizzando l'SDK di Firebase.

Consulta i documenti di Google Analytics per saperne di più.

Strategia di migrazione suggerita

L'utilizzo di diversi fornitori di analisi è uno scenario comune che si applica facilmente a Google Analytics. Aggiungilo alla tua app per beneficiare degli eventi e delle proprietà utente che Analytics raccoglie automaticamente, come prima apertura, aggiornamento dell'app, modello del dispositivo, età.

Per gli eventi personalizzati e le proprietà utente, puoi utilizzare una strategia di doppia scrittura utilizzando sia Parse Analytics che Google Analytics per registrare eventi e proprietà, il che ti consente di implementare gradualmente la nuova soluzione.

Confronto di codici

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

statistiche di Google

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

Database in tempo reale Firebase

Il Firebase Realtime Database è un database NoSQL ospitato sul cloud. I dati vengono archiviati come JSON e sincronizzati in tempo reale su ogni client connesso.

Consulta la documentazione del Firebase Realtime Database per saperne di più.

Differenze con l'analisi dei dati

Oggetti

In Parse memorizzi un PFObject , o una sua sottoclasse, che contiene coppie chiave-valore di dati compatibili con JSON. I dati sono senza schema, il che significa che non è necessario specificare quali chiavi esistono su ciascun PFObject .

Tutti i dati del Firebase Realtime Database vengono archiviati come oggetti JSON e non esiste un equivalente per PFObject ; scrivi semplicemente nell'albero JSON i valori dei tipi che corrispondono ai tipi JSON disponibili.

Di seguito è riportato un esempio di come salvare i punteggi più alti di un gioco.

Analizzare
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 di fuoco
// 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
  }
}];
Per maggiori dettagli consultare la guida Leggere e scrivere dati su piattaforme Apple .

Relazioni tra i dati

Un PFObject può avere una relazione con un altro PFObject : qualsiasi oggetto può utilizzare altri oggetti come valori.

Nel Firebase Realtime Database, le relazioni vengono espresse meglio utilizzando strutture di dati flat che dividono i dati in percorsi separati, in modo che possano essere scaricati in modo efficiente in chiamate separate.

Di seguito è riportato un esempio di come potresti strutturare la relazione tra i post in un'app di blogging e i relativi autori.

Analizzare
// 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 di fuoco
// 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]

Il risultato è il seguente layout dei dati.

{
  // 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"
    }
    ...
  }
}
Per maggiori dettagli, consulta la guida Struttura il tuo database .

Lettura dei dati

In Parse leggi i dati utilizzando l'ID di un oggetto Parse specifico o eseguendo query utilizzando PFQuery .

In Firebase, recuperi i dati collegando un ascoltatore asincrono a un riferimento al database. Il listener viene attivato una volta per lo stato iniziale dei dati e di nuovo quando i dati cambiano, quindi non sarà necessario aggiungere alcun codice per determinare se i dati sono cambiati.

Quello che segue è un esempio di come recuperare i punteggi per un particolare giocatore, in base all'esempio presentato nella sezione "Oggetti" .

Analizzare
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 di fuoco
// 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);
}];
Per maggiori dettagli sui tipi disponibili di ascoltatori di eventi e su come ordinare e filtrare i dati, consultare la guida Leggere e scrivere dati su piattaforme Apple .

Strategia di migrazione suggerita

Ripensa i tuoi dati

Il Firebase Realtime Database è ottimizzato per sincronizzare i dati in millisecondi su tutti i client connessi e la struttura dei dati risultante è diversa dai dati principali di Parse. Ciò significa che il primo passo della migrazione è considerare quali modifiche richiedono i tuoi dati, tra cui:

  • Come gli oggetti Parse dovrebbero essere mappati ai dati Firebase
  • Se hai relazioni genitore-figlio, come dividere i tuoi dati su percorsi diversi in modo che possano essere scaricati in modo efficiente in chiamate separate.

Migra i tuoi dati

Dopo aver deciso come strutturare i dati in Firebase, devi pianificare come gestire il periodo durante il quale la tua app deve scrivere su entrambi i database. Le tue scelte sono:

Sincronizzazione in background

In questo scenario, hai due versioni dell'app: la vecchia versione che utilizza Parse e una nuova versione che utilizza Firebase. Le sincronizzazioni tra i due database sono gestite da Parse Cloud Code (Parse to Firebase), con il codice che ascolta le modifiche su Firebase e sincronizza tali modifiche con Parse. Prima di poter iniziare a utilizzare la nuova versione, è necessario:

  • Converti i tuoi Parse Data esistenti nella nuova struttura Firebase e scrivili nel Firebase Realtime Database.
  • Scrivi le funzioni Parse Cloud Code che utilizzano l'API REST Firebase per scrivere nel database Firebase Realtime le modifiche apportate in Parse Data dai vecchi client.
  • Scrivi e distribuisci codice che ascolta le modifiche su Firebase e le sincronizza con il database Parse.

Questo scenario garantisce una netta separazione tra il vecchio e il nuovo codice e mantiene semplici i client. Le sfide di questo scenario sono la gestione di set di dati di grandi dimensioni nell'esportazione iniziale e la garanzia che la sincronizzazione bidirezionale non generi ricorsione infinita.

Doppia scrittura

In questo scenario, scrivi una nuova versione dell'app che utilizza sia Firebase che Parse, utilizzando Parse Cloud Code per sincronizzare le modifiche apportate dai vecchi client da Parse Data al Firebase Realtime Database. Quando un numero sufficiente di persone esegue la migrazione dalla versione di sola analisi dell'app, puoi rimuovere il codice di analisi dalla versione a doppia scrittura.

Questo scenario non richiede alcun codice lato server. Gli svantaggi sono che i dati a cui non si accede non vengono migrati e che la dimensione dell'app aumenta con l'utilizzo di entrambi gli SDK.

Autenticazione Firebase

Firebase Authentication può autenticare gli utenti utilizzando password e provider di identità federati popolari come Google, Facebook e Twitter. Fornisce inoltre librerie dell'interfaccia utente per farti risparmiare il significativo investimento necessario per implementare e mantenere un'esperienza di autenticazione completa per la tua app su tutte le piattaforme.

Consulta la documentazione sull'autenticazione Firebase per saperne di più.

Differenze con l'autenticazione dell'analisi

Parse fornisce una classe utente specializzata chiamata PFUser che gestisce automaticamente le funzionalità richieste per la gestione dell'account utente. PFUser è una sottoclasse di PFObject , il che significa che i dati utente sono disponibili in Parse Data e possono essere estesi con campi aggiuntivi come qualsiasi altro PFObject .

Un FIRUser ha un insieme fisso di proprietà di base, un ID univoco, un indirizzo email principale, un nome e un URL di foto, archiviate nel database utente di un progetto separato; tali proprietà possono essere aggiornate dall'utente. Non è possibile aggiungere direttamente altre proprietà all'oggetto FIRUser ; puoi invece archiviare le proprietà aggiuntive nel tuo Firebase Realtime Database.

Di seguito è riportato un esempio di come potresti registrare un utente e aggiungere un ulteriore campo per il numero di telefono.

Analizzare
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 di fuoco
[[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"];
  }
}];

Strategia di migrazione suggerita

Migrazione degli account

Per migrare gli account utente da Parse a Firebase, esporta il tuo database utente in un file JSON o CSV, quindi importa il file nel tuo progetto Firebase utilizzando il comando auth:import della CLI Firebase.

Innanzitutto, esporta il tuo database utente dalla console Parse o dal tuo database self-hosted. Ad esempio, un file JSON esportato dalla console Parse potrebbe essere simile al seguente:

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

Quindi, trasforma il file esportato nel formato richiesto dalla CLI Firebase. Utilizza l' objectId dei tuoi utenti Parse come localId dei tuoi utenti Firebase. Inoltre, base64 codifica i valori bcryptPassword da Parse e li utilizza nel campo passwordHash . Per esempio:

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

Infine, importa il file trasformato con la CLI Firebase, specificando bcrypt come algoritmo hash:

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

Migrazione dei dati utente

Se stai archiviando dati aggiuntivi per i tuoi utenti, puoi migrarli a Firebase Realtime Database utilizzando le strategie descritte nella sezione sulla migrazione dei dati . Se esegui la migrazione degli account utilizzando il flusso descritto nella sezione di migrazione degli account , i tuoi account Firebase avranno gli stessi ID dei tuoi account Parse, consentendoti di migrare e riprodurre facilmente qualsiasi relazione digitata dall'ID utente.

Messaggistica cloud Firebase

Firebase Cloud Messaging (FCM) è una soluzione di messaggistica multipiattaforma che ti consente di inviare messaggi e notifiche in modo affidabile e senza alcun costo. Il compositore di notifiche è un servizio gratuito basato su Firebase Cloud Messaging che consente notifiche utente mirate per gli sviluppatori di app mobili.

Per ulteriori informazioni, consulta la documentazione di Firebase Cloud Messaging .

Differenze con le notifiche push di analisi

A ogni applicazione Parse installata su un dispositivo registrato per le notifiche è associato un oggetto Installation , in cui vengono archiviati tutti i dati necessari per indirizzare le notifiche. Installation è una sottoclasse di PFUser , il che significa che puoi aggiungere tutti i dati aggiuntivi che desideri alle tue istanze Installation .

Il compositore di notifiche fornisce segmenti di utenti predefiniti in base a informazioni come app, versione dell'app e lingua del dispositivo. Puoi creare segmenti di utenti più complessi utilizzando gli eventi e le proprietà di Google Analytics per creare segmenti di pubblico. Consulta la guida di aiuto per il pubblico per saperne di più. Queste informazioni sul targeting non sono visibili nel Firebase Realtime Database.

Strategia di migrazione suggerita

Migrazione dei token del dispositivo

Mentre Parse utilizza i token del dispositivo APN per indirizzare le installazioni per le notifiche, FCM utilizza i token di registrazione FCM mappati sui token del dispositivo APN. Aggiungi semplicemente l'SDK FCM alla tua app Apple e recupererà automaticamente un token FCM .

Migrazione dei canali agli argomenti FCM

Se utilizzi i canali di analisi per inviare notifiche, puoi migrare agli argomenti FCM, che forniscono lo stesso modello editore-abbonato. Per gestire la transizione da Parse a FCM, puoi scrivere una nuova versione dell'app che utilizza Parse SDK per annullare l'iscrizione ai canali Parse e FCM SDK per iscriversi agli argomenti FCM corrispondenti.

Ad esempio, se il tuo utente è iscritto all'argomento "Giganti", dovresti fare qualcosa del tipo:

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

Usando questa strategia, puoi inviare messaggi sia al canale Parse che al corrispondente argomento FCM, supportando gli utenti sia della vecchia che della nuova versione. Quando un numero sufficiente di utenti ha effettuato la migrazione dalla versione di sola analisi dell'app, puoi disattivare tale versione e iniziare a inviare utilizzando solo FCM.

Consulta la documentazione sugli argomenti FCM per saperne di più.

Configurazione remota Firebase

Firebase Remote Config è un servizio cloud che ti consente di modificare il comportamento e l'aspetto della tua app senza richiedere agli utenti di scaricare un aggiornamento dell'app. Quando utilizzi Remote Config, crei valori predefiniti in-app che controllano il comportamento e l'aspetto della tua app. Successivamente, potrai utilizzare la console Firebase per sovrascrivere i valori predefiniti in-app per tutti gli utenti dell'app o per segmenti della tua base utenti.

Firebase Remote Config può essere molto utile durante le migrazioni nei casi in cui desideri testare soluzioni diverse ed essere in grado di spostare dinamicamente più client a un provider diverso. Ad esempio, se disponi di una versione della tua app che utilizza sia Firebase che Parse per i dati, potresti utilizzare una regola percentile casuale per determinare quali client leggono da Firebase e aumentare gradualmente la percentuale.

Per ulteriori informazioni su Firebase Remote Config, consulta l' introduzione a Remote Config .

Differenze con la configurazione di analisi

Con Parse config puoi aggiungere coppie chiave/valore alla tua app nella dashboard di Parse Config e quindi recuperare PFConfig sul client. Ogni istanza PFConfig che ottieni è sempre immutabile. Quando recupererai un nuovo PFConfig in futuro dalla rete, non modificherà alcuna istanza PFConfig esistente, ma ne creerà invece una nuova e la renderà disponibile tramite currentConfig .

Con Firebase Remote Config crei impostazioni predefinite in-app per coppie chiave/valore che puoi sovrascrivere dalla console Firebase e puoi utilizzare regole e condizioni per fornire variazioni sull'esperienza utente della tua app a diversi segmenti della tua base utenti. Firebase Remote Config implementa una classe singleton che rende disponibili le coppie chiave/valore per la tua app. Inizialmente il singleton restituisce i valori predefiniti definiti nell'app. Puoi recuperare un nuovo set di valori dal server in qualsiasi momento conveniente per la tua app; dopo che il nuovo set è stato recuperato con successo, puoi scegliere quando attivarlo per rendere disponibili i nuovi valori all'app.

Strategia di migrazione suggerita

Puoi passare a Firebase Remote Config copiando le coppie chiave/valore della configurazione Parse nella console Firebase e quindi distribuendo una nuova versione dell'app che utilizza Firebase Remote Config.

Se desideri sperimentare sia Parse Config che Firebase Remote Config, puoi distribuire una nuova versione dell'app che utilizza entrambi gli SDK finché un numero sufficiente di utenti non avrà eseguito la migrazione dalla versione solo Parse.

Confronto di codici

Analizzare

[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 di fuoco

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;