Buka konsol

Memigrasikan Aplikasi Parse pada iOS ke Firebase

Jika Anda pengguna Parse yang mencari Backend alternatif sebagai solusi Layanan, Firebase mungkin adalah pilihan ideal untuk aplikasi iOS Anda.

Panduan ini berisi penjelasan mengenai cara menggabungkan layanan tertentu ke dalam aplikasi Anda. Untuk mengetahui petunjuk dasar penyiapan Firebase, lihat panduan Penyiapan iOS.

Google Analytics for Firebase

Google Analytics for Firebase adalah solusi gratis untuk pengukuran aplikasi, yang memberikan insight mengenai penggunaan aplikasi dan interaksi pengguna. Analytics menggabungkan berbagai fitur Firebase dan menyediakan laporan tak terbatas hingga 500 peristiwa berbeda yang dapat Anda tentukan menggunakan Firebase SDK.

Lihat dokumentasi Google Analytics for Firebase untuk mempelajari lebih lanjut.

Strategi Migrasi yang Disarankan

Menggunakan berbagai penyedia analisis merupakan skenario umum yang mudah diterapkan pada Google Analytics for Firebase. Cukup tambahkan penyedia analisis ke aplikasi Anda untuk memanfaatkan data dari peristiwa dan properti pengguna yang dikumpulkan Analytics secara otomatis, seperti pembukaan aplikasi untuk pertama kali, update aplikasi, model perangkat, usia.

Untuk peristiwa kustom dan properti pengguna, Anda dapat menggunakan strategi penulisan ganda menggunakan Analytics Parse dan Google Analytics for Firebase guna mencatat peristiwa dan properti ke dalam log, sehingga Anda dapat meluncurkan solusi baru secara bertahap.

Perbandingan Kode

Analytics Parse

// 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 for Firebase

// 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 adalah database NoSQL yang di-host di cloud. Data disimpan sebagai JSON dan disinkronkan secara real-time pada setiap klien yang terhubung.

Baca Dokumen Firebase Realtime Database untuk mempelajari lebih lanjut.

Perbedaan dengan Data Parse

Objek

Dalam Parse, Anda menyimpan PFObject, atau subclass-nya, yang berisi key-value pair dari data yang kompatibel dengan JSON. Datanya bersifat tanpa skema, sehingga Anda tidak perlu menentukan kunci yang terdapat di setiap PFObject.

Semua data Firebase Realtime Database disimpan sebagai objek JSON, dan tidak ada kesamaan untuk PFObject; Anda cukup menuliskan nilai yang terkait dengan jenis JSON yang tersedia ke pohon JSON.

Berikut adalah contoh cara menyimpan skor tertinggi dalam sebuah game.

Parse
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
  }
}];
Untuk mengetahui detail lebih lanjut, baca panduan Membaca dan Menulis Data di iOS.

Hubungan Antardata

Sebuah PFObject dapat memiliki hubungan dengan PFObject yang lain: setiap objek dapat menggunakan objek lain sebagai nilai.

Dalam Firebase Realtime Database, hubungan sebaiknya dinyatakan menggunakan struktur data rata yang membagi data ke dalam lokasi yang terpisah, sehingga dapat didownload secara efisien dalam panggilan yang terpisah.

Berikut adalah contoh cara membuat struktur hubungan antara postingan di aplikasi blog dan penulisnya.

Parse
// 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]

Tata letak data berikut ini adalah hasilnya.

{
  // 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"
    }
    ...
  }
}
Untuk mengetahui detail selengkapnya, baca panduan Membuat Struktur Database.

Membaca Data

Di dalam Parse, Anda membaca data menggunakan ID dari objek Parse tertentu, atau menjalankan kueri menggunakan PFQuery.

Dalam Firebase, Anda mengambil data dengan memasang listener asinkron ke referensi database. Listener dipicu satu kali untuk status awal data dan dipicu kembali ketika data berubah, sehingga Anda tidak perlu menambahkan kode untuk mengetahui apakah datanya berubah.

Berikut adalah contoh cara mengambil skor untuk pemain tertentu, berdasarkan contoh yang diberikan di bagian "Objek".

Parse
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);
}];
Untuk mengetahui detail selengkapnya mengenai jenis listener peristiwa yang tersedia, serta cara mengurutkan dan memfilter data, baca panduan Membaca dan Menulis Data di iOS.

Strategi Migrasi yang Disarankan

Pikirkan Ulang Data Anda

Firebase Realtime Database dioptimalkan untuk menyinkronkan data dalam waktu milidetik pada semua klien yang terhubung, dan struktur data yang dihasilkan berbeda dari data inti Parse. Artinya, langkah pertama saat akan melakukan migrasi adalah mempertimbangkan perubahan yang dibutuhkan data Anda, termasuk:

  • Cara memetakan objek Parse Anda ke data Firebase
  • Cara memisahkan data melalui lokasi yang berbeda, jika Anda memiliki hubungan induk-turunan, sehingga data tersebut dapat didownload secara efisien dalam panggilan yang terpisah.

Migrasikan Data Anda

Setelah menentukan cara membuat struktur data di Firebase, Anda harus merencanakan cara menangani waktu selama aplikasi harus melakukan penulisan ke kedua database tersebut. Pilihan untuk Anda adalah:

Sinkronisasi Latar Belakang

Dalam skenario ini, Anda memiliki dua versi aplikasi: versi lama yang menggunakan Parse dan versi baru yang menggunakan Firebase. Sinkronisasi antara dua database ditangani oleh Parse Cloud Code (Parse ke Firebase), beserta kode Anda yang mendeteksi perubahan pada Firebase dan menyinkronkan perubahan tersebut dengan Parse. Sebelum dapat mulai menggunakan versi yang baru, Anda harus:

  • Mengubah Data Parse yang ada ke struktur Firebase yang baru, dan menulisnya ke Firebase Realtime Database.
  • Menulis fungsi Parse Cloud Code yang menggunakan Firebase REST API untuk melakukan penulisan ke perubahan Firebase Realtime Database yang dibuat di Data Parse oleh klien lama.
  • Menulis dan menerapkan kode yang mendeteksi perubahan pada Firebase, lalu menyinkronkannya dengan database Parse.

Skenario ini memastikan pemisahan yang jelas antara kode baru dan kode lama, dan tetap menyederhanakan klien. Tantangan dari skenario ini adalah menangani set data besar dalam ekspor awal, dan memastikan bahwa sinkronisasi dua arah tidak menghasilkan perulangan yang tak terbatas.

Penulisan Ganda

Dalam skenario ini, Anda menulis versi baru dari aplikasi yang menggunakan Firebase dan Parse, menggunakan Parse Cloud Code untuk menyinkronkan perubahan yang dilakukan klien lama dari Data Parse ke Firebase Realtime Database. Jika ada cukup orang yang bermigrasi dari versi aplikasi khusus Parse, Anda dapat menghapus kode Parse dari versi penulisan ganda.

Skenario ini tidak membutuhkan kode server apa pun. Kerugiannya adalah data yang tidak diakses tidak akan dimigrasikan, dan ukuran aplikasi Anda akan meningkat karena penggunaan kedua SDK.

Firebase Authentication

Firebase Authentication dapat melakukan autentikasi pengguna menggunakan sandi dan penyedia identitas gabungan populer seperti Google, Facebook, dan Twitter. Firebase Authentication juga menyediakan library UI yang menyimpan investasi penting Anda, untuk menerapkan serta mengelola pengalaman autentikasi penuh bagi aplikasi Anda di seluruh platform.

Baca Dokumentasi Firebase Authentication untuk mempelajari lebih lanjut.

Perbedaan dengan Parse Auth

Parse menyediakan class pengguna khusus yang disebut PFUser, yang otomatis menangani fungsi yang diperlukan untuk mengelola akun pengguna. PFUser adalah subclass PFObject, yang berarti data pengguna tersedia di Data Parse dan dapat diperluas dengan kolom tambahan seperti PFObject lainnya.

Sebuah FIRUser memiliki kumpulan tetap properti dasar—ID unik, alamat email utama, nama, dan URL foto—yang tersimpan dalam database pengguna project secara terpisah. Properti tersebut dapat diupdate oleh pengguna. Anda tidak dapat menambahkan properti lain ke objek FIRUser secara langsung. Sebagai gantinya, Anda dapat menyimpan properti tambahan di Firebase Realtime Database.

Berikut adalah contoh cara mendaftarkan pengguna dan menambahkan kolom nomor ponsel tambahan.

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

Strategi Migrasi yang Disarankan

Migrasi Akun

Untuk memigrasikan akun pengguna dari Parse ke Firebase, ekspor database pengguna Anda ke file JSON atau CSV, lalu impor file tersebut ke dalam project Firebase Anda menggunakan perintah auth:import Firebase CLI.

Pertama-tama, ekspor database pengguna Anda dari konsol Parse atau database yang Anda host sendiri. Misalnya, file JSON yang diekspor dari konsol Parse mungkin akan terlihat seperti berikut:

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

Selanjutnya, ubah file yang diekspor tersebut menjadi format yang diperlukan oleh Firebase CLI. Gunakan objectId pengguna Parse Anda sebagai localId pengguna Firebase Anda. Selain itu, base64 mengenkode nilai bcryptPassword dari Parse dan menggunakannya di kolom passwordHash. Misalnya:

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

Terakhir, impor file yang diubah tersebut dengan Firebase CLI, dengan menentukan bcrypt sebagai algoritme hash:

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

Migrasikan Data Pengguna

Jika akan menyimpan data tambahan untuk pengguna, Anda dapat memigrasikannya ke Firebase Realtime Database menggunakan strategi yang dijelaskan di bagian migrasi data. Jika Anda melakukan migrasi akun menggunakan alur yang dijelaskan di bagian migrasi akun, akun Firebase Anda akan memiliki ID yang sama dengan akun Parse Anda, sehingga memudahkan Anda melakukan migrasi dan memproduksi kembali setiap hubungan yang terkunci dengan ID pengguna.

Firebase Cloud Messaging

Firebase Cloud Messaging (FCM) adalah solusi pengiriman pesan lintas platform, yang memungkinkan Anda untuk mengirim pesan dan notifikasi secara tepercaya tanpa biaya. Notifications Composer adalah layanan gratis yang dikembangkan di Firebase Cloud Messaging, memungkinkan developer aplikasi seluler membuat notifikasi ke pengguna target.

Untuk mempelajari lebih lanjut, silakan lihat dokumentasi Firebase Cloud Messaging.

Perbedaan dengan Parse Push Notification

Setiap aplikasi Parse yang terinstal pada perangkat yang terdaftar untuk menerima notifikasi memiliki objek Installation yang terkait, tempat Anda menyimpan semua data yang diperlukan untuk menargetkan notifikasi. Installation adalah subclass PFUser, yang berarti Anda dapat menambahkan data lain yang diinginkan ke instance Installation.

Notifications composer menyediakan segmen pengguna yang telah ditentukan berdasarkan informasi seperti aplikasi, versi aplikasi, dan bahasa perangkat. Anda dapat membangun segmen pengguna yang lebih kompleks menggunakan peristiwa dan properti Google Analytics for Firebase untuk membangun audience. Baca panduan bantuan audience untuk mempelajari lebih lanjut. Informasi penargetan ini tidak terlihat dalam Firebase Realtime Database.

Strategi Migrasi yang Disarankan

Melakukan Migrasi Token Perangkat

Meskipun Parse menggunakan token perangkat APN untuk menargetkan penginstalan untuk notifikasi, FCM menggunakan token pendaftaran FCM yang dipetakan ke token perangkat APN. Cukup tambahkan FCM SDK ke aplikasi iOS Anda, dan FCM SDK ini akan mengambil token FCM secara otomatis.

Melakukan Migrasi Saluran ke Topik FCM

Jika Anda menggunakan saluran Parse untuk mengirim notifikasi, Anda dapat melakukan migrasi ke topik FCM, yang menyediakan mode penayang-pelanggan yang sama. Untuk menangani transisi dari Parse ke FCM, Anda dapat menulis versi baru aplikasi yang menggunakan Parse SDK untuk berhenti berlangganan dari saluran Parse, dan FCM SDK untuk berlangganan ke topik FCM yang terkait.

Misalnya, jika pengguna Anda berlangganan ke topik "Giants", Anda akan melakukan hal seperti:

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

Dengan menggunakan strategi ini, Anda dapat mengirimkan pesan ke saluran Parse dan topik FCM yang sesuai, sehingga mendukung pengguna versi lama maupun baru. Jika sudah ada cukup pengguna yang bermigrasi dari versi aplikasi khusus Parse, Anda dapat mengakhiri masa berlaku versi tersebut dan mulai melakukan pengiriman menggunakan FCM saja.

Lihat dokumentasi topik FCM untuk mempelajari lebih lanjut.

Firebase Remote Config

Firebase Remote Config adalah layanan cloud yang dapat digunakan untuk mengubah perilaku dan tampilan aplikasi tanpa mengharuskan pengguna mendownload update aplikasi. Ketika menggunakan Remote Config, Anda membuat nilai default dalam aplikasi yang mengontrol perilaku dan tampilan aplikasi. Anda nantinya dapat menggunakan Firebase console untuk mengganti nilai default dalam aplikasi untuk semua pengguna aplikasi, atau untuk segmen basis pengguna Anda.

Firebase Remote Config dapat sangat berguna selama migrasi jika Anda ingin menguji solusi yang berbeda, dan dapat mengalihkan lebih banyak klien ke penyedia lain secara dinamis. Misalnya, jika Anda memiliki versi aplikasi yang menggunakan Firebase dan Parse untuk datanya, Anda dapat menggunakan aturan persentil acak untuk menentukan klien mana yang membaca dari Firebase, dan secara bertahap meningkatkan persentase.

Untuk mempelajari lebih lanjut mengenai Firebase Remote Config, baca Pengantar Remote Config.

Perbedaan dengan Parse Config

Dengan Parse Config, Anda dapat menambahkan key/value pair ke aplikasi di Dasbor Parse Config, lalu mengambil PFConfig pada klien. Setiap instance PFConfig yang Anda dapatkan akan selalu tetap. Bila Anda mengambil PFConfig di masa mendatang dari jaringan, instance PFConfig yang ada tidak akan berubah. Namun instance yang baru akan dibuat, dan tersedia melalui currentConfig.

Dengan Firebase Remote Config, Anda membuat key/value pair default dalam aplikasi yang dapat diganti dari Firebase console. Anda juga dapat menggunakan berbagai aturan dan condition, guna menyediakan variasi terhadap pengalaman pengguna aplikasi untuk berbagai segmen basis pengguna Anda. Firebase Remote Config menerapkan class tunggal yang menyediakan key-value pair untuk aplikasi Anda. Mulanya, class tunggal tersebut menampilkan nilai default yang Anda tetapkan dalam aplikasi. Anda dapat mengambil kumpulan nilai yang baru dari server kapan saja untuk aplikasi Anda secara mudah. Setelah kumpulan baru tersebut berhasil diambil, Anda dapat memilih waktu pengaktifannya agar nilai yang baru tersedia pada aplikasi.

Strategi Migrasi yang Disarankan

Anda dapat melakukan pemindahan ke Firebase Remote Config dengan menyalin key-value pair Parse config Anda ke Firebase console, kemudian menerapkan versi baru aplikasi yang menggunakan Firebase Remote Config.

Jika ingin bereksperimen dengan Parse Config dan Firebase Remote Config, Anda dapat menerapkan versi baru aplikasi yang menggunakan kedua SDK tersebut hingga ada cukup pengguna yang bermigrasi dari versi khusus-Parse.

Perbandingan Kode

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

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;