Buat aplikasi seluler pertama offline tanpa kesulitan

Alexander Stigsen adalah salah satu pendiri dan CEO Realm.

Ini adalah kebenaran yang diakui secara universal bahwa pengguna yang memiliki smartphone pasti menginginkan koneksi yang lebih baik. Terlepas dari miliaran dolar investasi infrastruktur dan inovasi teknologi yang tiada henti, tidak perlu lebih dari perjalanan singkat untuk memperhatikan realitas penting dari era yang terhubung: Anda tidak dapat berasumsi bahwa koneksi jaringan akan tersedia setiap kali Anda menginginkannya. Sebagai pengembang seluler, ini adalah kebenaran yang mudah diabaikan.

Status offline di aplikasi dapat membingungkan untuk ditangani, tetapi masalahnya dimulai dengan asumsi dasar dan salah — bahwa offline, secara default, merupakan status error. Itu masuk akal ketika kami membuat aplikasi untuk komputer desktop dengan uplink ethernet khusus. Tidak masuk akal ketika menutup pintu lift membuat aplikasi sama sekali tidak berguna atau saat masuk akal untuk mengharapkan bahwa aplikasi Anda akan digunakan di tempat-tempat yang tidak memiliki infrastruktur seluler yang andal.

Kami tidak bisa menutupi dunia dalam cakupan, jadi kami harus menawarkan alternatif. Kami harus berpikir offline dulu. Kami harus merancang aplikasi agar berguna secara offline. Kami harus membangun aplikasi yang memanfaatkan internet secara maksimal jika tersedia, tetapi memahami bahwa akses internet selalu sementara. Kami harus membuat keputusan desain yang cerdas yang melibatkan status offline dan membuat status offline tersebut dapat dipahami oleh pengguna.

Banyak pekerjaan yang sedang dilakukan untuk menentukan masa depan yang mengutamakan offline. Realm, perusahaan tempat saya bekerja, telah membangun platform waktu nyata untuk aplikasi seluler yang mengutamakan offline selama beberapa waktu. Database seluler kami dan Realm Mobile Platform memudahkan pembuatan aplikasi cerdas, offline-first di hampir semua perangkat seluler. Orang-orang di A List Apart telah berkontribusi besar pada literatur yang mengutamakan offline, terutama untuk aplikasi web. Dan komunitas pengembang dari ekosistem seluler utama telah menghabiskan waktu berjam-jam untuk menawarkan solusi sumber terbuka mereka yang mengesankan.

Berikut ini adalah pengantar singkat tentang cara membuat aplikasi seluler yang mengutamakan offline. Saya akan menggambar beberapa kode sampel Swift sederhana di bagian akhir untuk menunjukkan seperti apa aplikasi offline-first yang minimal, tetapi prinsip dan masalah yang ditawarkan di sini relevan untuk siapa pun yang bekerja dalam pengembangan aplikasi seluler.

Desain untuk offline-first

Sebelum Anda membangun aplikasi offline pertama yang selalu Anda inginkan, kami harus meninjau kembali solusi desain yang masuk akal untuk desktop dengan kemungkinan yang sangat tinggi untuk online. Jika aplikasi Anda dapat menangani status offline dan online, kami memiliki pertanyaan untuk dijawab tentang apa yang dapat dilakukannya dan bagaimana kami menunjukkan kepada pengguna apa yang mungkin dilakukan.

Tentukan apa yang bisa dilakukan secara offline

Mari kita ambil Twitter sebagai contoh. Jika Anda sedang offline dan Anda memposting tweet, klien Twitter yang mengutamakan offline dapat mengambil dua jalur. Itu bisa mengantri tweet sampai mendapatkan kembali konektivitas. Atau dapat menolak untuk mengizinkan Anda menge-tweet — bahkan jika memungkinkan Anda mengantrekan tindakan lain seperti favorit, seperti yang dilakukan Tweetbot.

Mengapa Tweetbot mencegah Anda men-tweet secara offline? Mungkin karena pada saat Anda kembali online, tweet Anda mungkin sudah tidak relevan lagi. Memecahkan masalah itu akan melibatkan pembuatan UI baru untuk daftar tweet yang belum Anda posting, tetapi yang mungkin perlu Anda edit atau hapus sebelum mereka online. Jika Anda menyukai tweet, di sisi lain, Anda tidak akan membatalkannya jika dihadapkan dengan lebih banyak informasi — dan jauh lebih sedikit masalah untuk sekadar menunjukkan bahwa itu antri untuk dikirim.

Anda tidak dapat membuat aplikasi offline melakukan semua yang bisa dilakukan aplikasi online, tetapi Anda dapat membuatnya berguna.

Rancang konflik

Terlepas dari strategi yang Anda gunakan di bagian belakang untuk merekonsiliasi perubahan, aplikasi Anda akan menghadapi titik di mana Anda memiliki dua bagian data yang saling bertentangan. Mungkin karena server rusak atau karena Anda dan orang lain melakukan perubahan offline dan sekarang ingin menyinkronkannya. Apapun bisa terjadi!

Jadi, antisipasi konflik dan upayakan untuk menyelesaikannya dengan cara yang dapat diprediksi. Tawarkan pilihan. Dan cobalah untuk menghindari konflik sejak awal.

Dapat diprediksi berarti pengguna Anda tahu apa yang bisa terjadi. Jika konflik dapat muncul saat pengguna mengedit di dua tempat sekaligus saat offline, mereka harus diberi tahu tentang hal itu saat offline.

Menawarkan pilihan berarti tidak hanya menerima penulisan terakhir atau menggabungkan perubahan atau menghapus salinan terlama. Ini berarti membiarkan pengguna memutuskan apa yang sesuai.

Terakhir, solusi terbaik adalah jangan pernah membiarkan konflik berkembang sejak awal. Mungkin itu berarti membangun aplikasi Anda sedemikian rupa sehingga data baru dan aneh dari banyak sumber tidak menyebabkan konflik, dan malah ditampilkan persis seperti yang Anda inginkan. Itu mungkin sulit dilakukan dalam aplikasi menulis yang online dan offline, tetapi aplikasi menggambar bersama dapat dirancang untuk menambahkan jalur baru ke gambar setiap kali mereka disinkronkan.

Bersikaplah eksplisit

Menentukan apa yang dapat dilakukan pengguna secara offline adalah satu hal. Masalah lainnya melibatkan pengambilan keputusan yang dapat dipahami oleh pengguna Anda. Kegagalan untuk berhasil mengkomunikasikan status data dan konektivitas Anda, atau ketersediaan fitur yang diberikan, sama saja dengan kegagalan dalam membuat aplikasi yang mengutamakan offline di tempat pertama.

Aplikasi pencatatan bersama menggambarkan masalahnya. Jika Anda offline tetapi mengharapkan kolaborator untuk melanjutkan pengeditan di aplikasi saat Anda tidak ada, itu tidak cukup hanya dengan mengizinkan pengguna untuk terus mengetik sampai mereka senang. Saat terhubung kembali, mereka akan dikejutkan oleh konflik yang telah berkembang.

Sebaliknya, bantu pengguna Anda membuat keputusan yang tepat. Jika Anda melihat bahwa koneksi server Anda telah terputus karena bilah atas aplikasi Anda berubah warna, Anda tahu apa yang mungkin terjadi: konflik penggabungan! Itu mungkin baik-baik saja untuk sebagian besar waktu, dan UI aplikasi Anda dapat membantu mengatasi konflik tak terduga saat Anda kembali online. Tetapi jika Anda kehilangan konektivitas saat beberapa orang mengedit aplikasi Anda, bukankah akan membantu untuk mengetahui bahwa risiko konflik jauh lebih besar? “Anda kehilangan koneksi, tetapi yang lain sedang mengedit. Melanjutkan pengeditan dapat menyebabkan konflik. ” Pengguna dapat melanjutkan tetapi mengetahui risikonya.

Sangat mudah untuk menulis tanpa henti tentang masalah desain dan solusinya, tetapi sebelum kita terlalu jauh dari alat yang harus kita gunakan, mungkin akan membantu untuk melihat bagaimana rasanya membangun aplikasi seluler yang mengutamakan offline.

Buat aplikasi offline-first dengan Realm

Arsitektur aplikasi offline-first dasar tidak mewah. Anda memerlukan cara untuk mempertahankan data dalam aplikasi (menggunakan database di perangkat), protokol untuk berkomunikasi dengan server (termasuk serialisasi dan kode deserialisasi jika perlu), dan server tempat data yang disinkronkan akan berada sehingga dapat disimpan. didistribusikan kepada siapa pun yang memiliki izin.

Pertama, saya akan memandu Anda tentang cara memulai dengan Realm Mobile Database di dalam aplikasi iOS (meskipun kodenya tidak akan terlihat jauh berbeda di aplikasi Android). Kemudian saya akan menyajikan strategi untuk membuat serial dan deserialisasi kode yang Anda dapatkan dari server dan simpan di database Realm lokal Anda. Terakhir, saya akan menunjukkan kepada Anda cara membuat semuanya bekerja bersama dalam aplikasi daftar tugas kolaboratif yang disinkronkan secara real time.

Database Realm Mobile

Sangat mudah untuk memulai dengan Realm. Anda menginstal Database Realm Mobile, lalu menentukan skema Anda dengan membuat kelas. Karena Realm adalah database objek, itu sangat sederhana seperti membuat kelas, membuat instance beberapa objek, dan meneruskan objek tersebut ke dalam writeblok untuk menyimpannya ke disk. Tidak diperlukan serialisasi atau ORM, plus lebih cepat dari Data Inti Apple.

Inilah inti dari model kami dan aplikasi daftar tugas yang paling dasar (yang harus Anda kompilasi ulang setiap kali Anda ingin membuat tugas baru):

import RealmSwift

class Task: Object {

   dynamic var name

}

class TaskList: Object {

   let tasks = List()

}

let myTask = Task()

myTask.task

let myTaskList = TaskList()

myTaskList.tasks.append(myTask)

let realm = Realm()

try! realm.write{

            realm.add([myTask, myTaskList])

}

Dari sana, tidak perlu banyak untuk membangun aplikasi yang lebih berfungsi penuh di sekitar TableViewController:

import UIKit

import RealmSwift

class TaskListTableViewController: UITableViewController {

   var realm = try! Realm()

   var taskList = TaskList()

   override func viewDidLoad() {

       super.viewDidLoad()

       print(Realm.Configuration.defaultConfiguration.fileURL!)

       // Here, you could replace self.taskList with a previously saved TaskList object

       try! realm.write {

           realm.add(self.taskList)

       }

       // add navbar +

       navigationItem.setRightBarButton(UIBarButtonItem.init(barButtonSystemItem: UIBarButtonSystemItem.add, target: self, action: #selector(displayTaskAlert)), animated: false)

   }

   func displayTaskAlert() {

       // make and display an alert that’ll take a name and make a task.

       let alert = UIAlertController(title: “Make a task”, message: “What do you want to call it?”, preferredStyle: UIAlertControllerStyle.alert)

       alert.addTextField(configurationHandler: nil)

       alert.addAction(UIAlertAction(title: “Cancel”, style: UIAlertActionStyle.cancel, handler: nil))

       alert.addAction(UIAlertAction(title: “Create Task”, style: UIAlertActionStyle.default, handler: { (action) in

           let task = Task()

           task.name = (alert.textFields?[0].text)!

           try! self.realm.write {

               self.realm.add(task)

               self.taskList.tasks.append(task)

           }

           self.tableView.reloadData()

       }))

       self.present(alert, animated: true, completion: nil)

   }

   override func didReceiveMemoryWarning() {

       super.didReceiveMemoryWarning()

   }

   override func numberOfSections(in tableView: UITableView) -> Int {

       return 1

   }

   override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

       return self.taskList.tasks.count

   }

   override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

       let cell = tableView.dequeueReusableCell(withIdentifier: “reuseIdentifier”, for: indexPath)

       cell.textLabel?.text = self.taskList.tasks[indexPath.row].name

       return cell

   }

}

Hanya itu yang diperlukan untuk memulai! Anda bisa menjadi jauh lebih pintar dengan koleksi Realm dan pemberitahuan objek, sehingga Anda dapat dengan cerdas memuat ulang tableViewsaat objek ditambahkan atau dihapus, tetapi untuk saat ini kami memiliki ketekunan — dasar dari aplikasi offline-first.

Serialisasi dan deserialisasi

Aplikasi yang memprioritaskan offline bukanlah aplikasi yang mengutamakan offline kecuali jika dapat juga online, dan mendapatkan data ke dan dari Realm bisa sedikit rumit.

Pertama-tama, mencocokkan skema klien Anda sedekat mungkin dengan skema server Anda sangat penting. Mengingat cara kerja sebagian besar database back-end, itu kemungkinan akan melibatkan penambahan bidang kunci utama ke kelas Realm Anda, karena objek Realm tidak secara default memiliki kunci utama.

Setelah skema Anda cocok dengan baik, Anda memerlukan cara untuk melakukan deserialisasi data yang berasal dari server ke Realm dan melakukan serialisasi data ke JSON untuk dikirim kembali ke server. Metode termudah untuk melakukannya adalah dengan memilih pustaka pemetaan model favorit Anda dan membiarkannya melakukan pekerjaan berat. Swift memiliki Argo, Decodable, ObjectMapper, dan Mapper. Sekarang ketika Anda mendapatkan respons dari server Anda, Anda cukup membiarkan model mapper mendekodekannya menjadi RealmObject asli.

Tetap saja, ini bukanlah solusi yang bagus. Anda masih harus menulis banyak sekali kode jaringan untuk mendapatkan JSON ke dan dari server Anda dengan aman di tempat pertama, dan kode mapper model Anda akan perlu ditulis ulang dan di-debug setiap kali skema Anda berubah. Seharusnya ada cara yang lebih baik, dan menurut kami Realm Mobile Platform memang seperti itu.

Bekerja dengan Platform Realm Mobile

Realm Mobile Platform (RMP) memberi Anda sinkronisasi waktu nyata sehingga Anda dapat fokus membangun aplikasi seluler, bukan berjuang untuk membuat server dan aplikasi berbicara. Anda cukup mengambil model Realm Anda di atas, menambahkan otentikasi pengguna RMP, dan membiarkan RMP menangani sinkronisasi data antara server dan ranah aplikasi Anda. Kemudian Anda terus bekerja dengan objek Swift asli.

Untuk memulai, unduh dan instal bundel Realm Mobile Platform MacOS, yang memungkinkan Anda menjalankan instance Realm Object Server di Mac Anda dengan sangat cepat. Kemudian kami akan menambahkan beberapa item ke aplikasi daftar tugas kami untuk membuatnya terhubung ke Realm Object Server.

Setelah Anda selesai mengikuti petunjuk instalasi di atas, Anda harus menjalankan server dan pengguna admin di //127.0.0.1:9080. Ingat kredensial itu, dan kami akan kembali ke kode Swift kami.

Sebelum kita menulis kode lagi, kita perlu membuat dua perubahan kecil pada proyek. Pertama, kita perlu pergi ke editor target aplikasi kita di Xcode, dan di tab Capabilities, aktifkan saklar Keychain Sharing.

Kemudian, kami harus mengizinkan permintaan jaringan non-TLS. Buka file Info.plist proyek dan tambahkan yang berikut ini di dalam tag:

NSAppTransportSecurity

   NSAllowsArbitraryLoads