Google is committed to advancing racial equity for Black communities. See how.
本頁面由 Cloud Translation API 翻譯而成。
Switch to English

將您的Parse iOS應用遷移到Firebase

如果您是Parse用戶,正在尋找替代的後端即服務解決方案,則Firebase可能是您的iOS應用的理想選擇。

本指南介紹瞭如何將特定服務集成到您的應用中。有關Firebase的基本設置說明,請參閱iOS安裝指南。

谷歌分析

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

谷歌分析

 // 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實時數據庫是NoSQL雲託管的數據庫。數據存儲為JSON,並實時同步到每個連接的客戶端。

請參閱Firebase實時數據庫文檔以了解更多信息。

解析數據的差異

對象

在Parse中,您存儲PFObject或其子類,其中包含JSON兼容數據的鍵值對。數據是無模式的,這意味著您無需指定每個PFObject上存在的鍵。

所有Firebase實時數據庫數據都存儲為JSON對象,並且PFObject沒有等效的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
  }
}];
 
有關更多詳細信息,請參閱iOS上讀取和寫入數據指南。

數據之間的關係

一個PFObject可以與另一個PFObject有關係:任何對像都可以將其他對像用作值。

在Firebase實時數據庫中,可以使用平面數據結構更好地表達關係,該結構將數據拆分為單獨的路徑,以便可以在單獨的調用中有效地下載它們。

以下是如何構建博客應用程序中的帖子與其作者之間的關係的示例。

解析
 // 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"
    }
    ...
  }
}
有關更多詳細信息,請參閱《 結構化數據庫》指南。

讀取數據

在解析中,您可以使用特定解析對象的ID讀取數據,也可以使用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);
}];
 
有關事件偵聽器的可用類型以及如何排序和過濾數據的更多詳細信息,請參閱《 iOS上的讀寫數據》指南。

建議的遷移策略

重新考慮您的數據

Firebase實時數據庫經過優化,可以在所有連接的客戶端之間以毫秒為單位同步數據,並且生成的數據結構與Parse核心數據不同。這意味著遷移的第一步是考慮數據需要進行哪些更改,包括:

  • 您的Parse對象應如何映射到Firebase數據
  • 如果您有親子關係,如何將數據拆分到不同的路徑,以便可以在單獨的調用中有效地下載數據。

遷移數據

在決定如何在Firebase中構建數據結構之後,您需要計劃如何處理應用程序需要同時寫入兩個數據庫的時間段。您的選擇是:

後台同步

在這種情況下,您有兩個版本的應用程序:使用Parse的舊版本和使用Firebase的新版本。兩個數據庫之間的同步由Parse Cloud Code(解析為Firebase)處理(您的代碼偵聽Firebase上的更改並將這些更改與Parse同步)。在開始使用新版本之前,您必須:

  • 將您現有的解析數據轉換為新的Firebase結構,並將其寫入Firebase實時數據庫。
  • 編寫使用Firebase REST API的Parse Cloud Code函數,以將舊客戶端在Parse Data中進行的更改寫入Firebase Realtime Database。
  • 編寫和部署可偵聽Firebase上的更改並將其同步到Parse數據庫的代碼。

這種情況確保了新舊代碼的清晰分離,並使客戶端保持簡單。這種情況下的挑戰是在初始導出中處理大型數據集,並確保雙向同步不會產生無限遞歸。

雙重寫

在這種情況下,您將編寫一個同時使用Firebase和Parse的應用程序的新版本,並使用Parse Cloud Code將舊客戶端所做的更改從Parse Data同步到Firebase Realtime Database。從應用程序的僅解析版本遷移了足夠多的人後,您可以從雙重寫入版本中刪除解析代碼。

這種情況不需要任何服務器端代碼。其缺點是無法遷移無法訪問的數據,並且兩個SDK的使用都會增加應用程序的大小。

Firebase身份驗證

Firebase身份驗證可以使用密碼和流行的聯合身份提供商(例如Google,Facebook和Twitter)對用戶進行身份驗證。它還提供UI庫,以節省您在所有平台上實施和維護應用程序的完整身份驗證體驗所需的大量投資。

請參閱Firebase身份驗證文檔以了解更多信息。

解析身份驗證的差異

解析提供了一個稱為PFUser的專用用戶類,該類自動處理用戶帳戶管理所需的功能。 PFUser是的一個子類PFObject ,該裝置的用戶數據可在分析數據,並且可以與像任何其他附加字段來擴展PFObject

FIRUser具有一組固定的基本屬性,即唯一的ID,主電子郵件地址,名稱和照片URL,這些屬性存儲在單獨的項目的用戶數據庫中;這些屬性可以由用戶更新。您不能將其他屬性直接添加到FIRUser對象。相反,您可以將其他屬性存儲在Firebase實時數據庫中。

以下是如何註冊用戶並添加其他電話號碼字段的示例。

解析
 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 CLI的auth:import命令將該文件導入到Firebase項目中。

首先,從“解析”控制台或您的自託管數據庫導出用戶數據庫。例如,從Parse控制台導出的JSON文件可能如下所示:

{ // 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 CLI所需的格式。將您的Parse用戶的objectId用作Firebase用戶的localId 。另外,base64對Parse中的bcryptPassword值進行編碼,並在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帳戶具有與“解析”帳戶相同的ID,從而使您可以輕鬆地遷移和復制由用戶ID鍵入的任何關係。

Firebase雲消息傳遞

Firebase雲消息傳遞(FCM)是一種跨平台的消息傳遞解決方案,可讓您可靠地免費傳遞消息和通知。 Notifications作曲者是一項基於Firebase Cloud Messaging的免費服務,可為移動應用程序開發人員提供有針對性的用戶通知。

請參閱Firebase Cloud Messaging文檔以了解更多信息。

解析推送通知的差異

在註冊了通知的設備上安裝的每個Parse應用程序都有一個關聯的Installation對象,您可以在其中存儲定位通知所需的所有數據。 InstallationPFUser的子類,這意味著您可以將所需的任何其他數據添加到Installation實例。

Notifications作曲器根據諸如應用程序,應用程序版本和設備語言之類的信息提供預定義的用戶細分。您可以使用Google Analytics(分析)事件和屬性來建立更複雜的用戶群,以建立受眾群體。請參閱受眾幫助指南以了解更多信息。這些定位信息在Firebase實時數據庫中不可見。

建議的遷移策略

遷移設備令牌

雖然Parse使用APNs設備令牌將目標安裝用於通知,但FCM使用映射到APNs設備令牌的FCM註冊令牌。只需將FCM SDK添加到您的iOS應用中,它將自動獲取FCM令牌

將渠道遷移到FCM主題

如果使用解析通道發送通知,則可以遷移到FCM主題,它們提供相同的發布者-訂閱者模型。要處理從Parse到FCM的過渡,您可以編寫新版本的應用程序,該應用程序使用Parse SDK取消訂閱解析通道,並使用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
  }
}];
 

使用此策略,可以將消息發送到“解析”通道和相應的FCM主題,從而支持新舊版本的用戶。從應用程序的僅解析版本遷移了足夠多的用戶後,您可以停用該版本並僅使用FCM開始發送。

請參閱FCM主題文檔以了解更多信息。

Firebase遠程配置

Firebase Remote Config是一項云服務,可讓您更改應用程序的行為和外觀,而無需用戶下載應用程序更新。使用Remote Config時,您將創建應用程序內默認值來控制應用程序的行為和外觀。然後,您以後可以使用Firebase控制台為所有應用程序用戶或用戶庫的部分覆蓋應用程序內默認值。

如果您想測試不同的解決方案並能夠將更多客戶端動態轉移到其他提供商,則Firebase Remote Config在遷移期間非常有用。例如,如果您的應用程序版本同時使用Firebase和Parse來獲取數據,則可以使用隨機百分比規則來確定哪些客戶端從Firebase讀取,並逐漸增加百分比。

要了解有關Firebase Remote Config的更多信息,請參閱Remote Config簡介

解析配置的差異

使用“解析配置”,您可以在“解析配置”儀表板上向您的應用添加鍵/值對,然後在客戶端上獲取PFConfig 。您獲得的每個PFConfig實例始終是不可變的。將來當您從網絡中檢索新的PFConfig時,它不會修改任何現有的PFConfig實例,而是創建一個新的PFConfig實例,並使其通過currentConfig可用。

借助Firebase Remote Config,您可以為可從Firebase控制台覆蓋的鍵/值對創建應用程序內默認值,並且可以使用規則和條件根據應用程序的用戶體驗提供不同的用戶群。 Firebase Remote Config實現了一個單例類,該類使鍵/值對可用於您的應用程序。最初,單例返回您在應用內定義的默認值。您可以隨時為您的應用方便地從服務器獲取一組新的值。成功獲取新集合後,您可以選擇何時激活它以使新值可用於該應用程序。

建議的遷移策略

通過將解析配置的鍵/值對複製到Firebase控制台,然後部署使用Firebase Remote Config的應用程序的新版本,可以遷移到Firebase Remote Config。

如果要同時嘗試“解析配置”和“ Firebase遠程配置”,則可以部署使用兩個SDK的應用程序的新版本,直到有足夠的用戶從“僅解析”版本進行遷移。

代碼比較

解析

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