Coba Cloud Firestore: Lihat database yang skalabel dan fleksibel dari Firebase dan Google Cloud Platform. Pelajari Cloud Firestore lebih lanjut.

Membaca dan Menulis Data di iOS

Mendapatkan FIRDatabaseReference

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

Swift

var ref: DatabaseReference!

ref = Database.database().reference()

Objective-C

@property (strong, nonatomic) FIRDatabaseReference *ref;

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

Membaca dan menulis data

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

Data Firebase dituliskan ke referensi FIRDatabase dan diambil dengan menambahkan listener asinkron ke referensi tersebut. Listener dipicu satu kali 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 yang ditentukan, sehingga menggantikan data yang ada di lokasi tersebut. Anda bisa menggunakan metode ini untuk:

  • Meneruskan tipe yang cocok dengan tipe JSON yang tersedia berikut ini:
    • NSString
    • NSNumber
    • NSDictionary
    • NSArray

Misalnya, Anda dapat menambahkan pengguna dengan setValue sebagai berikut:

Swift

self.ref.child("users").child(user.uid).setValue(["username": username])

Objective-C

[[[self.ref child:@"users"] child:authResult.user.uid]
    setValue:@{@"username": username}];

Penggunaan setValue seperti ini akan menimpa data di lokasi yang ditentukan, termasuk semua node turunan. Namun, Anda masih dapat mengupdate turunan tanpa menulis ulang seluruh objek. Jika ingin mengizinkan pengguna mengupdate profil mereka, Anda dapat mengupdate nama pengguna seperti berikut:

Swift

self.ref.child("users/\(user.uid)/username").setValue(username)

Objective-C

[[[[_ref child:@"users"] child:user.uid] child:@"username"] setValue:username];

Mendeteksi peristiwa nilai

Untuk membaca data di sebuah lokasi dan mendeteksi perubahan, gunakan metode observeEventType:withBlock atau observeSingleEventOfType:withBlock pada FIRDatabaseReference untuk mengamati peristiwa FIRDataEventTypeValue.

Jenis peristiwa Penggunaan standar
FIRDataEventTypeValue Membaca dan mendeteksi perubahan pada seluruh konten di sebuah lokasi.

Anda dapat menggunakan peristiwa FIRDataEventTypeValue untuk membaca data di lokasi tertentu, sesuai kondisi pada saat terjadinya peristiwa. Metode ini dipicu 1 kali saat listener ditambahkan dan dipicu lagi setiap kali terjadi perubahan data, termasuk setiap turunannya. Peristiwa callback meneruskan snapshot yang berisi semua data di lokasi tersebut, termasuk data turunan. Jika tidak ada data, snapshot akan menampilkan false ketika Anda memanggil exists() dan nil ketika Anda membaca properti value miliknya.

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

Swift

refHandle = postRef.observe(DataEventType.value, with: { (snapshot) in
  let postDict = snapshot.value as? [String : AnyObject] ?? [:]
  // ...
})

Objective-C

_refHandle = [_postRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
  NSDictionary *postDict = snapshot.value;
  // ...
}];

Listener menerima FIRDataSnapshot yang memuat data di lokasi yang ditentukan dalam database saat terjadi peristiwa di properti value miliknya. Anda dapat menetapkan nilai ke jenis bawaan yang sesuai, seperti NSDictionary. Jika tidak ada data di lokasi tersebut, value adalah nil.

Membaca data sekali

Dalam beberapa kasus, Anda mungkin ingin agar callback dipanggil 1 kali kemudian segera dihapus, seperti ketika melakukan inisialisasi elemen UI yang tidak diharapkan untuk berubah. Anda dapat menggunakan metode observeSingleEventOfType untuk menyederhanakan skenario ini: callback peristiwa yang ditambahkan dipicu 1 kali, lalu tidak dipicu lagi.

Cara ini berguna untuk data yang hanya perlu dimuat sekali dan tidak diharapkan untuk sering berubah atau harus aktif mendeteksi. Misalnya, aplikasi blogging pada contoh sebelumnya menggunakan metode ini untuk memuat profil pengguna ketika mereka mulai membuat postingan baru:

Swift

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

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

Mengupdate atau menghapus data

Mengupdate kolom tertentu

Untuk menulis ke turunan tertentu dari sebuah node secara simultan tanpa menimpa node turunan yang lain, gunakan metode updateChildValues.

Ketika memanggil updateChildValues, Anda dapat memperbarui nilai turunan pada tingkat yang lebih rendah dengan menetapkan lokasi untuk kunci tersebut. Jika data disimpan di beberapa lokasi untuk memudahkan penyebaran, Anda dapat mengupdate semua instance dari data tersebut dengan fan-out data. Misalnya, sebuah aplikasi blogging sosial mungkin ingin membuat sebuah postingan dan sekaligus mengupdate postingan tersebut ke feed aktivitas terbaru dan feed aktivitas pengguna yang memosting. Untuk melakukannya, aplikasi blogging tersebut menggunakan kode seperti ini:

Swift

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

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 pada node yang memuat postingan untuk semua pengguna di /posts/$postid, sekaligus mengambil kunci dengan getKey(). Selanjutnya kunci tersebut dapat digunakan untuk membuat entri kedua dalam postingan pengguna di /user-posts/$userid/$postid.

Menggunakan lokasi tersebut, Anda dapat menjalankan update simultan ke beberapa lokasi di pohon JSON dengan sekali panggilan ke updateChildValues, seperti yang digunakan pada contoh ini untuk membuat postingan baru di kedua lokasi. Update simultan menjadikan proses ini berjalan menyeluruh: entah semua update berhasil atau semua update gagal.

Menambahkan Blok Penyelesaian

Jika Anda ingin tahu kapan data diubah, Anda bisa menambahkan blok penyelesaian. setValue maupun updateChildValues membawa blok penyelesaian opsional yang dipanggil ketika operasi tulis telah diterapkan ke database. Listener ini dapat berguna untuk memantau data mana yang sudah disimpan dan data mana yang masih disinkronkan. Jika panggilan tidak berhasil, listener akan diberikan objek error yang menunjukkan penyebab terjadinya kegagalan.

Swift

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

[[[_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.");
  }
}];

Menghapus data

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

Penghapusan juga dapat dilakukan dengan menentukan nil sebagai nilai untuk operasi tulis lainnya, seperti setValue atau updateChildValues. Teknik ini dapat digunakan dengan updateChildValues untuk menghapus beberapa turunan dengan 1 panggilan API.

Melepas listener

Observer tidak secara otomatis menghentikan sinkronisasi data saat Anda meninggalkan ViewController. Jika tidak dihapus dengan benar, observer akan terus menyinkronkan data ke memori lokal. Jika observer tidak lagi diperlukan, hapuslah dengan meneruskan FIRDatabaseHandle yang terkait ke metode removeObserverWithHandle.

Saat pemblokiran callback ditambahkan ke referensi, FIRDatabaseHandle akan ditampilkan. Handle ini dapat digunakan untuk menghapus pemblokiran callback.

Jika beberapa listener ditambahkan ke referensi database, setiap listener akan dipanggil ketika terjadi peristiwa. Untuk menghentikan sinkronisasi data di lokasi tersebut, Anda harus menghapus semua observer di sebuah lokasi dengan memanggil metode removeAllObservers.

Memanggil removeObserverWithHandle atau removeAllObservers pada listener tidak secara otomatis menghapus listener yang terdaftar pada node turunannya; Anda juga harus melacak referensi atau handle tersebut untuk menghapusnya.

Menyimpan data sebagai transaksi

Ketika bekerja dengan data yang bisa rusak karena perubahan serentak, seperti penghitung tambahan, Anda dapat menggunakan operasi transaksi. Operasi ini menggunakan 2 argumen: fungsi update dan callback penyelesaian opsional. Fungsi update mengambil kondisi data saat ini sebagai argumen dan menampilkan kondisi baru yang diinginkan untuk Anda tuliskan.

Misalnya, pada contoh aplikasi blogging sosial ini, Anda dapat mengizinkan pengguna memberi atau menghapus bintang pada postingan, serta mengetahui berapa banyak bintang yang telah diterima suatu postingan dengan cara berikut ini:

Swift

ref.runTransactionBlock({ (currentData: MutableData) -> TransactionResult in
  if var post = currentData.value as? [String : AnyObject], let uid = Auth.auth().currentUser?.uid {
    var stars: Dictionary<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

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

Penggunaan transaksi dapat mencegah kesalahan penghitungan jumlah bintang jika beberapa pengguna memberi bintang pada postingan yang sama secara bersamaan atau jika klien memiliki data yang sudah usang. Nilai yang dimuat pada kelas FIRMutableData pada awalnya adalah nilai terakhir klien yang diketahui untuk lokasi tersebut, atau nil jika nilainya tidak ada. Server membandingkan nilai awal tersebut dengan nilai saat ini dan menerima transaksi jika nilainya cocok, atau menolaknya. Jika transaksi ditolak, server akan menampilkan nilai saat ini ke klien, yang akan mengulangi transaksi tersebut dengan nilai yang telah diupdate. Proses ini berulang hingga transaksi diterima atau terlalu banyak percobaan dilakukan.

Menulis data offline

Jika koneksi jaringan klien terputus, aplikasi Anda akan tetap berfungsi dengan baik.

Setiap klien yang terhubung ke database Firebase menyimpan versi internalnya sendiri dari setiap data aktif. Ketika ditulis, data akan dituliskan ke versi lokal ini terlebih dahulu. Selanjutnya, klien Firebase menyinkronkan data tersebut dengan server database di tempat lain, dan dengan klien lain berdasarkan "upaya terbaik".

Akibatnya, semua operasi tulis ke database akan segera memicu peristiwa lokal, sebelum ada data yang dituliskan ke server. Ini berarti aplikasi Anda akan tetap responsif, apa pun kondisi latensi atau konektivitas jaringannya.

Setelah konektivitas pulih, aplikasi Anda akan menerima kumpulan peristiwa yang tepat, sehingga klien sinkron dengan kondisi server saat ini, tanpa perlu menulis kode khusus apa pun.

Langkah Berikutnya

Kirim masukan tentang...

Firebase Realtime Database
Butuh bantuan? Kunjungi halaman dukungan kami.