Pembuat kode seluler telah memanfaatkan platform Backend Seluler sebagai Layanan (MBaaS) Google Firebase Realtime Database selama bertahun-tahun, membantu mereka fokus dalam membangun fitur untuk aplikasi mereka tanpa harus mengkhawatirkan infrastruktur dan database back-end. Dengan mempermudah penyimpanan dan penyimpanan data di cloud serta menjaga autentikasi dan keamanan, Firebase memungkinkan pembuat kode untuk fokus pada sisi klien.
Tahun lalu, Google mengumumkan solusi database back-end lainnya, Cloud Firestore, yang dibangun dari awal dengan janji skalabilitas dan intuitif yang lebih besar. Namun, hal ini menimbulkan kebingungan terkait posisinya dengan produk unggulan Google yang sudah ada, Firebase Realtime Database. Tutorial ini akan menjelaskan perbedaan antara kedua platform dan keuntungan yang berbeda dari masing-masing platform. Anda akan mempelajari cara bekerja dengan referensi dokumen Firestore, serta membaca, menulis, memperbarui, dan menghapus data secara waktu nyata, dengan membuat aplikasi pengingat sederhana.
Tujuan Tutorial Ini
Tutorial ini akan memaparkan Anda ke Cloud Firestore. Anda akan mempelajari cara memanfaatkan platform untuk persistensi dan sinkronisasi database real-time. Kami akan membahas topik berikut:
- apa itu Cloud Firestore
- model data Firestore
- menyiapkan Cloud Firestore
- membuat dan bekerja dengan referensi Cloud Firestore
- membaca data secara real time dari Cloud Firestore
- membuat, memperbarui, dan menghapus data
- pemfilteran dan kueri gabungan
Asumsi Pengetahuan
Tutorial ini mengasumsikan Anda telah memiliki beberapa eksposur ke Firebase dan latar belakang yang berkembang dengan Swift dan Xcode.
Apa itu Cloud Firestore?
Seperti Firebase Realtime Database, Firestore memberi pengembang seluler dan web solusi cloud lintas platform untuk menyimpan data secara real time, terlepas dari latensi jaringan atau konektivitas internet, serta integrasi tanpa batas dengan rangkaian produk Google Cloud Platform. Seiring dengan kesamaan ini, ada kelebihan dan kekurangan yang membedakan satu dari yang lain.
Model Data
Pada tingkat dasar, Realtime Database menyimpan data sebagai satu pohon JSON yang besar, monolitik, dan hierarkis, sedangkan Firestore mengatur data dalam dokumen dan koleksi, serta sub-koleksi. Ini membutuhkan lebih sedikit denormalisasi. Menyimpan data dalam satu pohon JSON memiliki manfaat kesederhanaan dalam hal bekerja dengan persyaratan data sederhana; namun, ini menjadi lebih rumit dalam skala saat bekerja dengan data hierarkis yang lebih kompleks.
Dukungan Offline
Kedua produk menawarkan dukungan offline, secara aktif menyimpan data dalam antrean saat ada konektivitas jaringan laten atau tidak ada—mensinkronisasikan perubahan lokal kembali ke bagian belakang jika memungkinkan. Firestore mendukung sinkronisasi offline untuk aplikasi web selain aplikasi seluler, sedangkan Realtime Database hanya mengaktifkan sinkronisasi seluler.
Permintaan dan Transaksi
Realtime Database hanya mendukung kemampuan pengurutan dan pemfilteran terbatas—Anda hanya dapat mengurutkan atau memfilter pada tingkat properti, namun tidak keduanya, dalam satu kueri. Kueri juga dalam, artinya mereka mengembalikan sub-pohon hasil yang besar. Produk hanya mendukung operasi tulis dan transaksi sederhana yang memerlukan panggilan balik penyelesaian.
Firestore, di sisi lain, memperkenalkan kueri indeks dengan pengurutan dan pemfilteran gabungan, memungkinkan Anda untuk menggabungkan tindakan untuk membuat filter dan pengurutan berantai. Anda juga dapat menjalankan kueri dangkal yang mengembalikan sub-koleksi sebagai pengganti seluruh koleksi yang akan Anda dapatkan dengan Realtime Database. Transaksi bersifat atomik, baik Anda mengirim operasi batch atau tunggal, dengan transaksi berulang secara otomatis hingga selesai. Selain itu, Realtime Database hanya mendukung transaksi tulis individu, sedangkan Firestore menyediakan operasi batch secara atom.
Kinerja dan Skalabilitas
Database Realtime, seperti yang Anda harapkan, cukup kuat dan memiliki latensi rendah. Namun, database dibatasi untuk satu wilayah, tergantung pada ketersediaan zona. Firestore, di sisi lain, menyimpan data secara horizontal di berbagai zona dan wilayah untuk memastikan ketersediaan, skalabilitas, dan keandalan global yang sebenarnya. Bahkan, Google telah berjanji bahwa Firestore akan lebih dapat diandalkan daripada Realtime Database.
Kelemahan lain dari Realtime Database adalah batasan untuk 100.000 pengguna secara bersamaan (100.000 koneksi bersamaan dan 1.000 penulisan/detik dalam satu database) setelah itu Anda harus melakukan shard database Anda (membagi database Anda menjadi beberapa database) untuk mendukung lebih banyak pengguna . Firestore secara otomatis menskalakan di beberapa instance tanpa Anda harus campur tangan.
Dirancang dari awal dengan mempertimbangkan skalabilitas, Firestore memiliki arsitektur skematik baru yang mereplikasi data di beberapa wilayah, menangani autentikasi, dan menangani masalah terkait keamanan lainnya, semuanya dalam SDK sisi kliennya. Model data barunya lebih intuitif daripada Firebase, lebih mirip dengan solusi database NoSQL lain yang sebanding seperti MongoDB, sambil menyediakan mesin kueri yang lebih kuat.
Keamanan
Terakhir, Realtime Database, seperti yang Anda ketahui dari tutorial kami sebelumnya, mengelola keamanan melalui aturan berjenjang dengan pemicu validasi terpisah. Ini berfungsi dengan Aturan Database Firebase, yang memvalidasi data Anda secara terpisah. Firestore, di sisi lain, menyediakan model keamanan yang lebih sederhana namun lebih kuat dengan memanfaatkan Aturan Keamanan Cloud Firestore serta Identity and Access Management (IAM), dengan validasi data dikecualikan secara otomatis.
- Pengembangan SelulerFirebase Security RulesChike Mgbemena
Model Data Firestore
Firestore adalah database berbasis dokumen NoSQL, terdiri dari kumpulan dokumen, yang masing-masing berisi data. Karena ini adalah database NoSQL, Anda tidak akan mendapatkan tabel, baris, dan elemen lain yang akan Anda temukan di database relasional, melainkan kumpulan pasangan kunci/nilai yang akan Anda temukan di dalam dokumen.
Anda membuat dokumen dan koleksi secara implisit dengan menetapkan data ke dokumen, dan jika dokumen atau koleksi tidak ada, itu akan secara otomatis dibuat untuk Anda, karena koleksi harus selalu menjadi simpul akar (pertama). Berikut adalah contoh skema Tasks sederhana dari proyek yang akan segera Anda kerjakan, terdiri dari kumpulan Tasks, serta banyak dokumen yang berisi dua bidang, nama (string), dan tanda apakah tugas telah selesai (boolean) .
Mari kita menguraikan setiap elemen sehingga Anda dapat memahaminya dengan lebih baik.
Koleksi
Identik dengan tabel database di dunia SQL, koleksi berisi satu atau lebih dokumen. Koleksi harus menjadi elemen utama dalam skema Anda dan hanya dapat berisi dokumen, bukan koleksi lainnya. Namun, Anda dapat merujuk ke dokumen yang pada gilirannya merujuk pada koleksi (sub-koleksi).
Dalam diagram di atas, tugas terdiri dari dua bidang primitif (nama dan selesai) serta sub-kumpulan (sub-tugas) yang terdiri dari dua bidang primitifnya sendiri.
Dokumen
Dokumen terdiri dari pasangan kunci/nilai, dengan nilai yang memiliki salah satu jenis berikut:
- bidang primitif (seperti string, angka, boolean)
- objek bersarang kompleks (daftar atau larik primitif)
- subkoleksi
Objek bersarang juga disebut peta dan dapat direpresentasikan sebagai berikut, di dalam dokumen. Berikut ini adalah contoh dari objek dan array bersarang, masing-masing:
ID: 2422892 //primitive name: “Remember to buy milk” detail: //nested object notes: "This is a task to buy milk from the store" created: 2017-04-09 due: 2017-04-10 done: false notify: ["2F22-89R2", "L092-G623", "H00V-T4S1"] ...
Untuk informasi selengkapnya tentang jenis data yang didukung, lihat dokumentasi Jenis Data Google. Selanjutnya, Anda akan menyiapkan proyek untuk bekerja dengan Cloud Firestore.
Menyiapkan Proyek
Jika Anda pernah bekerja dengan Firebase sebelumnya, pasti banyak yang sudah familiar bagi Anda. Jika tidak, Anda harus membuat akun di Firebase dan mengikuti petunjuk di bagian 'Menyiapkan Proyek' dari tutorial kami sebelumnya, Memulai Firebase Authentication untuk iOS .
Untuk mengikuti tutorial ini, clone repo project tutorial. Selanjutnya, sertakan perpustakaan Firestore dengan menambahkan yang berikut ke Podfile your Anda :
pod 'Firebase/Core' pod 'Firebase/Firestore'
Masukkan yang berikut di terminal Anda untuk membangun perpustakaan Anda:
pod install
Selanjutnya, alihkan ke Xcode dan buka .xcworkspace mengajukan. Navigasikan ke AppDelegate.swift file dan masukkan berikut ini dalam application:didFinishLaunchingWithOptions:
metode:
FirebaseApp.configure()
Di browser Anda, buka Firebase console dan pilih Database tab di sebelah kiri.
Pastikan Anda memilih opsi untuk Mulai dalam Mode Uji sehingga Anda tidak mengalami masalah keamanan saat kami bereksperimen, dan memperhatikan pemberitahuan keamanan saat Anda memindahkan aplikasi ke produksi. Anda sekarang siap untuk membuat koleksi dan beberapa contoh dokumen.
Menambahkan Koleksi dan Contoh Dokumen
Untuk memulai, buat koleksi awal, Tasks
, dengan memilih Tambahkan Koleksi dan beri nama koleksi, seperti yang diilustrasikan di bawah ini:
Untuk dokumen pertama, Anda akan membiarkan ID Dokumen kosong, yang akan membuat ID secara otomatis untuk Anda. Dokumen hanya akan terdiri dari dua bidang: name
dan done
.
Simpan dokumen, dan Anda akan dapat mengonfirmasi koleksi dan dokumen bersama dengan ID yang dibuat secara otomatis:
Dengan database yang disiapkan dengan dokumen sampel di cloud, Anda siap untuk mulai mengimplementasikan Firestore SDK di Xcode.
Membuat &Bekerja Dengan Referensi Basis Data
Buka MasterViewController.swift file di Xcode dan tambahkan baris berikut untuk mengimpor perpustakaan:
import Firebase class MasterViewController: UITableViewController { @IBOutlet weak var addButton: UIBarButtonItem! private var documents: [DocumentSnapshot] = [] public var tasks: [Task] = [] private var listener : ListenerRegistration! ...
Di sini Anda cukup membuat variabel pendengar yang memungkinkan Anda untuk memicu koneksi ke database secara real time ketika ada perubahan. Anda juga membuat DocumentSnapshot
referensi yang akan menyimpan snapshot data sementara.
Sebelum melanjutkan dengan pengontrol tampilan, buat file swift lain, Task.swift , yang akan mewakili model data Anda:
import Foundation struct Task{ var name:String var done: Bool var id: String var dictionary: [String: Any] { return [ "name": name, "done": done ] } } extension Task{ init?(dictionary: [String : Any], id: String) { guard let name = dictionary["name"] as? String, let done = dictionary["done"] as? Bool else { return nil } self.init(name: name, done: done, id: id) } }
Cuplikan kode di atas menyertakan properti convenience (kamus) dan metode (init) yang akan mempermudah pengisian objek model. Beralih kembali ke pengontrol tampilan, dan nyatakan variabel penyetel global yang akan membatasi kueri dasar ke 50 entri teratas dalam daftar tugas. Anda juga akan menghapus listener setelah Anda menyetel variabel kueri, seperti yang dilambangkan dalam didSet
properti di bawah ini:
fileprivate func baseQuery() -> Query { return Firestore.firestore().collection("Tasks").limit(to: 50) } fileprivate var query: Query? { didSet { if let listener = listener { listener.remove() } } } override func viewDidLoad() { super.viewDidLoad() self.query = baseQuery() } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) self.listener.remove() }
Membaca Data Secara Waktu Nyata Dari Cloud Firestore
Dengan referensi dokumen di tempat, di viewWillAppear(_animated: Bool)
, kaitkan listener yang Anda buat sebelumnya dengan hasil snapshot kueri, dan ambil daftar dokumen. Ini dilakukan dengan memanggil metode Firestore query?.addSnapshotListener
:
self.listener = query?.addSnapshotListener { (documents, error) in guard let snapshot = documents else { print("Error fetching documents results: \(error!)") return } let results = snapshot.documents.map { (document) -> Task in if let task = Task(dictionary: document.data(), id: document.documentID) { return task } else { fatalError("Unable to initialize type \(Task.self) with dictionary \(document.data())") } } self.tasks = results self.documents = snapshot.documents self.tableView.reloadData() }
Penutupan di atas menetapkan snapshot.documents
dengan memetakan larik secara iteratif dan menggabungkannya ke Tasks
new baru model objek instance untuk setiap item data dalam snapshot. Jadi hanya dengan beberapa baris, Anda telah berhasil membaca semua tugas dari cloud dan menetapkannya ke tasks
global Himpunan.
Untuk menampilkan hasil, isi berikut ini TableView
metode delegasi:
override func numberOfSections(in tableView: UITableView) -> Int { return 1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return tasks.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) let item = tasks[indexPath.row] cell.textLabel!.text = item.name cell.textLabel!.textColor = item.done == false ? UIColor.black : UIColor.lightGray return cell }
Pada tahap ini, bangun dan jalankan proyek dan di Simulator Anda harus dapat mengamati data yang muncul secara real time. Tambahkan data melalui konsol Firebase dan Anda akan melihatnya muncul secara instan di simulator aplikasi.
Membuat, Memperbarui, dan Menghapus Data
Setelah berhasil membaca konten dari back-end, selanjutnya Anda akan membuat, memperbarui, dan menghapus data. Contoh berikutnya akan mengilustrasikan cara memperbarui data, menggunakan contoh yang dibuat-buat di mana aplikasi hanya akan membiarkan Anda menandai item sebagai selesai dengan mengetuk sel. Perhatikan collection.document(
item.id
).updateData(["done": !item.done])
penutupan properti, yang hanya mereferensikan ID dokumen tertentu, memperbarui setiap bidang dalam kamus:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let item = tasks[indexPath.row] let collection = Firestore.firestore().collection("Tasks") collection.document(item.id).updateData([ "done": !item.done, ]) { err in if let err = err { print("Error updating document: \(err)") } else { print("Document successfully updated") } } tableView.reloadRows(at: [indexPath], with: .automatic) }
Untuk menghapus item, panggil document(
item.id
).delete()
metode:
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { return true } override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { if (editingStyle == .delete){ let item = tasks[indexPath.row] _ = Firestore.firestore().collection("Tasks").document(item.id).delete() } }
Membuat tugas baru akan melibatkan penambahan tombol baru di Storyboard Anda dan menghubungkan IBAction
ke pengontrol tampilan, membuat addTask(_ sender:)
metode. Ketika pengguna menekan tombol, itu akan memunculkan lembar peringatan di mana pengguna dapat menambahkan nama tugas baru:
collection("Tasks").addDocument (data: ["name": textFieldReminder.text ?? "empty task", "done": false])
Selesaikan bagian akhir aplikasi dengan memasukkan yang berikut:
@IBAction func addTask(_ sender: Any) { let alertVC : UIAlertController = UIAlertController(title: "New Task", message: "What do you want to remember?", preferredStyle: .alert) alertVC.addTextField { (UITextField) in } let cancelAction = UIAlertAction.init(title: "Cancel", style: .destructive, handler: nil) alertVC.addAction(cancelAction) //Alert action closure let addAction = UIAlertAction.init(title: "Add", style: .default) { (UIAlertAction) -> Void in let textFieldReminder = (alertVC.textFields?.first)! as UITextField let db = Firestore.firestore() var docRef: DocumentReference? = nil docRef = db.collection("Tasks").addDocument(data: [ "name": textFieldReminder.text ?? "empty task", "done": false ]) { err in if let err = err { print("Error adding document: \(err)") } else { print("Document added with ID: \(docRef!.documentID)") } } } alertVC.addAction(addAction) present(alertVC, animated: true, completion: nil) }
Buat dan jalankan aplikasi sekali lagi dan, saat simulator muncul, coba tambahkan beberapa tugas, serta tandai beberapa sebagai selesai, dan terakhir uji fungsi hapus dengan menghapus beberapa tugas. Anda dapat mengonfirmasi bahwa data yang disimpan telah diperbarui secara real time dengan beralih ke konsol database Firebase dan mengamati koleksi serta dokumen.
Pemfilteran dan Kueri Gabungan
Sejauh ini, Anda hanya bekerja dengan kueri sederhana, tanpa kemampuan pemfilteran khusus. Untuk membuat kueri yang sedikit lebih kuat, Anda dapat memfilter menurut nilai tertentu dengan menggunakan whereField
klausa:
docRef.whereField(“name”, isEqualTo: searchString)
Anda dapat memesan dan membatasi data kueri Anda, dengan menggunakan order(by: )
dan limit(to: )
metode sebagai berikut:
docRef.order(by: "name").limit(5)
Di aplikasi FirebaseDo, Anda telah menggunakan limit
dengan kueri dasar. Dalam cuplikan di atas, Anda juga menggunakan fitur lain, kueri gabungan, di mana urutan dan batas dirantai bersama. Anda dapat membuat rantai kueri sebanyak yang Anda inginkan, seperti dalam contoh berikut:
docRef .whereField(“name”, isEqualTo: searchString) .whereField(“done”, isEqualTo: false) .order(by: "name") .limit(5)