Ikuti semua informasi yang diumumkan di Firebase Summit, dan pelajari bagaimana Firebase dapat membantu Anda mempercepat pengembangan aplikasi dan menjalankan aplikasi dengan percaya diri. Pelajari Lebih Lanjut

Baca dan Tulis Data di platform Apple

(Opsional) Membuat prototipe dan menguji dengan Firebase Local Emulator Suite

Sebelum berbicara tentang bagaimana aplikasi Anda membaca dari dan menulis ke Realtime Database, mari perkenalkan seperangkat alat yang dapat Anda gunakan untuk membuat prototipe dan menguji fungsionalitas Realtime Database: Firebase Local Emulator Suite. Jika Anda mencoba model data yang berbeda, mengoptimalkan aturan keamanan, atau berupaya menemukan cara yang paling hemat biaya untuk berinteraksi dengan back-end, dapat bekerja secara lokal tanpa menggunakan layanan langsung bisa menjadi ide bagus.

Emulator Realtime Database adalah bagian dari Local Emulator Suite, yang memungkinkan aplikasi Anda berinteraksi dengan konten dan konfigurasi database yang diemulasikan, serta secara opsional sumber daya proyek yang diemulasikan (fungsi, database lain, dan aturan keamanan).

Menggunakan emulator Realtime Database hanya memerlukan beberapa langkah:

  1. Menambahkan baris kode ke konfigurasi pengujian aplikasi Anda untuk terhubung ke emulator.
  2. Dari root direktori proyek lokal Anda, jalankan firebase emulators:start .
  3. Melakukan panggilan dari kode prototipe aplikasi Anda menggunakan SDK platform Realtime Database seperti biasa, atau menggunakan REST API Realtime Database.

Panduan mendetail yang melibatkan Realtime Database dan Cloud Functions tersedia. Anda juga harus melihat pengenalan Local Emulator Suite .

Dapatkan Referensi FIRDatabase

Untuk membaca atau menulis data dari database, Anda memerlukan instance FIRDatabaseReference :

Cepat

Catatan: Produk Firebase ini tidak tersedia di target Cuplikan Aplikasi.
var ref: DatabaseReference!

ref = Database.database().reference()

Objective-C

Catatan: Produk Firebase ini tidak tersedia di target Cuplikan Aplikasi.
@property (strong, nonatomic) FIRDatabaseReference *ref;

self.ref = [[FIRDatabase database] reference];

Menulis data

Dokumen ini mencakup dasar-dasar membaca dan menulis data Firebase.

Data Firebase ditulis ke referensi Database dan diambil dengan melampirkan pendengar asinkron ke referensi. Pemroses dipicu sekali untuk status awal data dan dipicu lagi setiap kali data berubah.

Operasi tulis dasar

Untuk operasi tulis dasar, Anda dapat menggunakan setValue untuk menyimpan data ke referensi tertentu, menggantikan data yang ada di jalur tersebut. Anda dapat menggunakan metode ini untuk:

  • Jenis penerusan yang sesuai dengan jenis JSON yang tersedia sebagai berikut:
    • NSString
    • NSNumber
    • NSDictionary
    • NSArray

Misalnya, Anda dapat menambahkan pengguna dengan setValue sebagai berikut:

Cepat

Catatan: Produk Firebase ini tidak tersedia di target Cuplikan Aplikasi.
self.ref.child("users").child(user.uid).setValue(["username": username])

Objective-C

Catatan: Produk Firebase ini tidak tersedia di target Cuplikan Aplikasi.
[[[self.ref child:@"users"] child:authResult.user.uid]
    setValue:@{@"username": username}];

Menggunakan setValue dengan cara ini akan menimpa data di lokasi yang ditentukan, termasuk simpul anak mana pun. Namun, Anda masih dapat memperbarui anak tanpa menulis ulang seluruh objek. Jika Anda ingin mengizinkan pengguna memperbarui profil mereka, Anda dapat memperbarui nama pengguna sebagai berikut:

Cepat

Catatan: Produk Firebase ini tidak tersedia di target Cuplikan Aplikasi.
self.ref.child("users/\(user.uid)/username").setValue(username)

Objective-C

Catatan: Produk Firebase ini tidak tersedia di target Cuplikan Aplikasi.
[[[[_ref child:@"users"] child:user.uid] child:@"username"] setValue:username];

Membaca data

Membaca data dengan mendengarkan peristiwa nilai

Untuk membaca data di jalur dan memantau perubahan, gunakan observeEventType:withBlock dari FIRDatabaseReference untuk mengamati peristiwa FIRDataEventTypeValue .

Jenis acara Penggunaan tipikal
FIRDataEventTypeValue Baca dan dengarkan perubahan pada seluruh konten jalur.

Anda bisa menggunakan event FIRDataEventTypeValue untuk membaca data di jalur tertentu, seperti yang ada pada saat event. Metode ini dipicu sekali saat pemroses dilampirkan dan dipicu lagi setiap kali data, termasuk turunan apa pun, berubah. Panggilan balik peristiwa diteruskan snapshot yang berisi semua data di lokasi itu, termasuk data anak. Jika tidak ada data, snapshot akan mengembalikan false saat Anda memanggil exists() dan nil saat Anda membaca properti value .

Contoh berikut menunjukkan aplikasi blogging sosial yang mengambil detail postingan dari database:

Cepat

Catatan: Produk Firebase ini tidak tersedia di target Cuplikan Aplikasi.
refHandle = postRef.observe(DataEventType.value, with: { snapshot in
  // ...
})

Objective-C

Catatan: Produk Firebase ini tidak tersedia di target Cuplikan Aplikasi.
_refHandle = [_postRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
  NSDictionary *postDict = snapshot.value;
  // ...
}];

Pendengar menerima FIRDataSnapshot yang berisi data di lokasi yang ditentukan di database pada saat kejadian di properti value . Anda dapat menetapkan nilai ke jenis bawaan yang sesuai, seperti NSDictionary . Jika tidak ada data di lokasi, value nil .

Membaca data sekali

Baca sekali menggunakan getData()

SDK dirancang untuk mengelola interaksi dengan server database apakah aplikasi Anda sedang online atau offline.

Secara umum, Anda harus menggunakan teknik peristiwa nilai yang dijelaskan di atas untuk membaca data guna mendapatkan pemberitahuan tentang pembaruan data dari backend. Teknik tersebut mengurangi penggunaan dan penagihan Anda, dan dioptimalkan untuk memberikan pengalaman terbaik kepada pengguna Anda saat mereka online dan offline.

Jika Anda memerlukan data hanya sekali, Anda dapat menggunakan getData() untuk mendapatkan cuplikan data dari database. Jika karena alasan apa pun getData() tidak dapat mengembalikan nilai server, klien akan menyelidiki cache penyimpanan lokal dan mengembalikan kesalahan jika nilainya masih belum ditemukan.

Contoh berikut menunjukkan pengambilan nama pengguna yang menghadap publik pengguna satu kali dari database:

Cepat

Catatan: Produk Firebase ini tidak tersedia di target Cuplikan Aplikasi.
ref.child("users/\(uid)/username").getData(completion:  { error, snapshot in
  guard error == nil else {
    print(error!.localizedDescription)
    return;
  }
  let userName = snapshot.value as? String ?? "Unknown";
});

Objective-C

Catatan: Produk Firebase ini tidak tersedia di target Cuplikan Aplikasi.
NSString *userPath = [NSString stringWithFormat:@"users/%@/username", uid];
[[ref child:userPath] getDataWithCompletionBlock:^(NSError * _Nullable error, FIRDataSnapshot * _Nonnull snapshot) {
  if (error) {
    NSLog(@"Received an error %@", error);
    return;
  }
  NSString *userName = snapshot.value;
}];

Penggunaan getData() yang tidak perlu dapat meningkatkan penggunaan bandwidth dan menyebabkan hilangnya kinerja, yang dapat dicegah dengan menggunakan pendengar waktu nyata seperti yang ditunjukkan di atas.

Baca data sekali dengan pengamat

Dalam beberapa kasus, Anda mungkin ingin nilai dari cache lokal segera dikembalikan, alih-alih memeriksa nilai yang diperbarui di server. Dalam kasus tersebut, Anda dapat menggunakan observeSingleEventOfType untuk segera mendapatkan data dari cache disk lokal.

Ini berguna untuk data yang hanya perlu dimuat satu kali dan tidak diharapkan sering berubah atau memerlukan mendengarkan secara aktif. Misalnya, aplikasi blog pada contoh sebelumnya menggunakan metode ini untuk memuat profil pengguna saat mereka mulai membuat postingan baru:

Cepat

Catatan: Produk Firebase ini tidak tersedia di target Cuplikan Aplikasi.
let userID = Auth.auth().currentUser?.uid
ref.child("users").child(userID!).observeSingleEvent(of: .value, with: { snapshot in
  // Get user value
  let value = snapshot.value as? NSDictionary
  let username = value?["username"] as? String ?? ""
  let user = User(username: username)

  // ...
}) { error in
  print(error.localizedDescription)
}

Objective-C

Catatan: Produk Firebase ini tidak tersedia di target Cuplikan Aplikasi.
NSString *userID = [FIRAuth auth].currentUser.uid;
[[[_ref child:@"users"] child:userID] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
  // Get user value
  User *user = [[User alloc] initWithUsername:snapshot.value[@"username"]];

  // ...
} withCancelBlock:^(NSError * _Nonnull error) {
  NSLog(@"%@", error.localizedDescription);
}];

Memperbarui atau menghapus data

Perbarui bidang tertentu

Untuk secara bersamaan menulis ke node anak tertentu tanpa menimpa node anak lainnya, gunakan metode updateChildValues .

Saat memanggil updateChildValues ​​, Anda bisa memperbarui nilai anak tingkat rendah dengan menentukan jalur untuk kunci. Jika data disimpan di beberapa lokasi untuk menskalakan dengan lebih baik, Anda dapat memperbarui semua contoh data tersebut menggunakan data fan-out . Misalnya, aplikasi blog sosial mungkin ingin membuat postingan dan secara bersamaan memperbaruinya ke umpan aktivitas terbaru dan umpan aktivitas pengguna postingan. Untuk melakukan ini, aplikasi blogging menggunakan kode seperti ini:

Cepat

Catatan: Produk Firebase ini tidak tersedia di target Cuplikan Aplikasi.
guard let key = ref.child("posts").childByAutoId().key else { return }
let post = ["uid": userID,
            "author": username,
            "title": title,
            "body": body]
let childUpdates = ["/posts/\(key)": post,
                    "/user-posts/\(userID)/\(key)/": post]
ref.updateChildValues(childUpdates)

Objective-C

Catatan: Produk Firebase ini tidak tersedia di target Cuplikan Aplikasi.
NSString *key = [[_ref child:@"posts"] childByAutoId].key;
NSDictionary *post = @{@"uid": userID,
                       @"author": username,
                       @"title": title,
                       @"body": body};
NSDictionary *childUpdates = @{[@"/posts/" stringByAppendingString:key]: post,
                               [NSString stringWithFormat:@"/user-posts/%@/%@/", userID, key]: post};
[_ref updateChildValues:childUpdates];

Contoh ini menggunakan childByAutoId untuk membuat postingan di node yang berisi postingan untuk semua pengguna di /posts/$postid dan sekaligus mengambil kunci dengan getKey() . Kunci tersebut kemudian dapat digunakan untuk membuat entri kedua di postingan pengguna di /user-posts/$userid/$postid .

Dengan menggunakan jalur ini, Anda dapat melakukan pembaruan serentak ke beberapa lokasi di pohon JSON dengan satu panggilan ke updateChildValues ​​, seperti bagaimana contoh ini membuat postingan baru di kedua lokasi. Pembaruan simultan yang dilakukan dengan cara ini bersifat atomik: semua pembaruan berhasil atau semua pembaruan gagal.

Tambahkan Blok Penyelesaian

Jika Anda ingin mengetahui kapan data Anda telah dikomit, Anda dapat menambahkan blok penyelesaian. Baik setValue dan updateChildValues ​​menggunakan blok penyelesaian opsional yang dipanggil saat penulisan telah dilakukan ke database. Pendengar ini dapat berguna untuk melacak data mana yang telah disimpan dan data mana yang masih disinkronkan. Jika panggilan tidak berhasil, pendengar diberikan objek kesalahan yang menunjukkan mengapa kegagalan terjadi.

Cepat

Catatan: Produk Firebase ini tidak tersedia di target Cuplikan Aplikasi.
ref.child("users").child(user.uid).setValue(["username": username]) {
  (error:Error?, ref:DatabaseReference) in
  if let error = error {
    print("Data could not be saved: \(error).")
  } else {
    print("Data saved successfully!")
  }
}

Objective-C

Catatan: Produk Firebase ini tidak tersedia di target Cuplikan Aplikasi.
[[[_ref child:@"users"] child:user.uid] setValue:@{@"username": username} withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
  if (error) {
    NSLog(@"Data could not be saved: %@", error);
  } else {
    NSLog(@"Data saved successfully.");
  }
}];

Hapus data

Cara termudah untuk menghapus data adalah dengan memanggil removeValue pada referensi ke lokasi data tersebut.

Anda juga dapat menghapus dengan menentukan nil sebagai nilai untuk operasi tulis lainnya seperti setValue atau updateChildValues ​​. Anda dapat menggunakan teknik ini dengan updateChildValues ​​untuk menghapus banyak turunan dalam satu panggilan API.

Pisahkan pendengar

Pengamat tidak secara otomatis menghentikan sinkronisasi data saat Anda meninggalkan ViewController . Jika pengamat tidak dihapus dengan benar, data akan terus disinkronkan ke memori lokal. Saat pengamat tidak lagi diperlukan, hapus dengan meneruskan FIRDatabaseHandle terkait ke metode removeObserverWithHandle .

Saat Anda menambahkan blok panggilan balik ke referensi, FIRDatabaseHandle dikembalikan. Pegangan ini dapat digunakan untuk menghapus blok panggilan balik.

Jika banyak pendengar telah ditambahkan ke referensi basis data, setiap pendengar dipanggil saat suatu peristiwa dimunculkan. Untuk menghentikan sinkronisasi data di lokasi tersebut, Anda harus menghapus semua pengamat di suatu lokasi dengan memanggil metode removeAllObservers .

Memanggil removeObserverWithHandle atau removeAllObservers pada pendengar tidak secara otomatis menghapus pendengar yang terdaftar pada node anaknya; Anda juga harus melacak referensi atau pegangan tersebut untuk menghapusnya.

Simpan data sebagai transaksi

Saat bekerja dengan data yang dapat dirusak oleh modifikasi bersamaan, seperti penghitung inkremental, Anda dapat menggunakan operasi transaksi . Anda memberikan operasi ini dua argumen: fungsi update dan callback penyelesaian opsional. Fungsi pembaruan mengambil keadaan data saat ini sebagai argumen dan mengembalikan keadaan baru yang diinginkan yang ingin Anda tulis.

Misalnya, dalam contoh aplikasi blog sosial, Anda dapat mengizinkan pengguna untuk memberi bintang dan menghapus postingan serta melacak jumlah bintang yang diterima postingan sebagai berikut:

Cepat

Catatan: Produk Firebase ini tidak tersedia di target Cuplikan Aplikasi.
ref.runTransactionBlock({ (currentData: MutableData) -> TransactionResult in
  if var post = currentData.value as? [String: AnyObject],
    let uid = Auth.auth().currentUser?.uid {
    var stars: [String: Bool]
    stars = post["stars"] as? [String: Bool] ?? [:]
    var starCount = post["starCount"] as? Int ?? 0
    if let _ = stars[uid] {
      // Unstar the post and remove self from stars
      starCount -= 1
      stars.removeValue(forKey: uid)
    } else {
      // Star the post and add self to stars
      starCount += 1
      stars[uid] = true
    }
    post["starCount"] = starCount as AnyObject?
    post["stars"] = stars as AnyObject?

    // Set value and report transaction success
    currentData.value = post

    return TransactionResult.success(withValue: currentData)
  }
  return TransactionResult.success(withValue: currentData)
}) { error, committed, snapshot in
  if let error = error {
    print(error.localizedDescription)
  }
}

Objective-C

Catatan: Produk Firebase ini tidak tersedia di target Cuplikan Aplikasi.
[ref runTransactionBlock:^FIRTransactionResult * _Nonnull(FIRMutableData * _Nonnull currentData) {
  NSMutableDictionary *post = currentData.value;
  if (!post || [post isEqual:[NSNull null]]) {
    return [FIRTransactionResult successWithValue:currentData];
  }

  NSMutableDictionary *stars = post[@"stars"];
  if (!stars) {
    stars = [[NSMutableDictionary alloc] initWithCapacity:1];
  }
  NSString *uid = [FIRAuth auth].currentUser.uid;
  int starCount = [post[@"starCount"] intValue];
  if (stars[uid]) {
    // Unstar the post and remove self from stars
    starCount--;
    [stars removeObjectForKey:uid];
  } else {
    // Star the post and add self to stars
    starCount++;
    stars[uid] = @YES;
  }
  post[@"stars"] = stars;
  post[@"starCount"] = @(starCount);

  // Set value and report transaction success
  currentData.value = post;
  return [FIRTransactionResult successWithValue:currentData];
} andCompletionBlock:^(NSError * _Nullable error,
                       BOOL committed,
                       FIRDataSnapshot * _Nullable snapshot) {
  // Transaction completed
  if (error) {
    NSLog(@"%@", error.localizedDescription);
  }
}];

Menggunakan transaksi mencegah kesalahan jumlah bintang jika banyak pengguna membintangi posting yang sama pada waktu yang sama atau klien memiliki data basi. Nilai yang terkandung dalam kelas FIRMutableData awalnya adalah nilai terakhir yang diketahui klien untuk jalur tersebut, atau nil jika tidak ada. Server membandingkan nilai awal dengan nilai saat ini dan menerima transaksi jika nilainya cocok, atau menolaknya. Jika transaksi ditolak, server mengembalikan nilai saat ini ke klien, yang menjalankan transaksi lagi dengan nilai yang diperbarui. Ini berulang sampai transaksi diterima atau terlalu banyak upaya telah dilakukan.

Penambahan sisi server atomik

Dalam kasus penggunaan di atas, kami menulis dua nilai ke database: ID pengguna yang memberi bintang/menghapus bintang pada kiriman, dan jumlah bintang yang bertambah. Jika kita sudah mengetahui bahwa pengguna membintangi postingan tersebut, kita dapat menggunakan operasi peningkatan atom alih-alih transaksi.

Cepat

Catatan: Produk Firebase ini tidak tersedia di target Cuplikan Aplikasi.
let updates = [
  "posts/\(postID)/stars/\(userID)": true,
  "posts/\(postID)/starCount": ServerValue.increment(1),
  "user-posts/\(postID)/stars/\(userID)": true,
  "user-posts/\(postID)/starCount": ServerValue.increment(1)
] as [String : Any]
Database.database().reference().updateChildValues(updates);

Objective-C

Catatan: Produk Firebase ini tidak tersedia di target Cuplikan Aplikasi.
NSDictionary *updates = @{[NSString stringWithFormat: @"posts/%@/stars/%@", postID, userID]: @TRUE,
                        [NSString stringWithFormat: @"posts/%@/starCount", postID]: [FIRServerValue increment:@1],
                        [NSString stringWithFormat: @"user-posts/%@/stars/%@", postID, userID]: @TRUE,
                        [NSString stringWithFormat: @"user-posts/%@/starCount", postID]: [FIRServerValue increment:@1]};
[[[FIRDatabase database] reference] updateChildValues:updates];

Kode ini tidak menggunakan operasi transaksi, sehingga tidak dijalankan kembali secara otomatis jika ada pembaruan yang bertentangan. Namun, karena operasi penambahan terjadi langsung di server basis data, tidak ada kemungkinan konflik.

Jika Anda ingin mendeteksi dan menolak konflik khusus aplikasi, seperti pengguna yang membintangi kiriman yang telah mereka bintangi sebelumnya, Anda harus menulis aturan keamanan khusus untuk kasus penggunaan tersebut.

Bekerja dengan data luring

Jika klien kehilangan koneksi jaringannya, aplikasi Anda akan terus berfungsi dengan benar.

Setiap klien yang terhubung ke database Firebase mempertahankan versi internalnya sendiri dari setiap data aktif. Saat data ditulis, data ditulis ke versi lokal ini terlebih dahulu. Klien Firebase kemudian menyinkronkan data tersebut dengan server basis data jarak jauh dan dengan klien lain berdasarkan "upaya terbaik".

Akibatnya, semua penulisan ke database segera memicu peristiwa lokal, sebelum data apa pun ditulis ke server. Ini berarti aplikasi Anda tetap responsif terlepas dari latensi atau konektivitas jaringan.

Setelah konektivitas dibangun kembali, aplikasi Anda menerima rangkaian peristiwa yang sesuai sehingga klien menyinkronkan dengan status server saat ini, tanpa harus menulis kode khusus apa pun.

Kami akan berbicara lebih banyak tentang perilaku offline di Pelajari lebih lanjut tentang kemampuan online dan offline .

Langkah selanjutnya