Advertisement
  1. Code
  2. iOS SDK
Code

Cara yang Benar untuk Berbagi State Antara View Controller Swift

by
Difficulty:IntermediateLength:LongLanguages:

Indonesian (Bahasa Indonesia) translation by Kurniawan Sugi Purwanto (you can also view the original English article)

Final product image
What You'll Be Creating

Beberapa tahun lalu, ketika saya masih seorang karyawan di suatu perusahaan konsultan selular, saya mengerjakan suatu aplikasi untuk bank investasi besar. Perusahaan-perusahaan besar, terutama bank, biasanya memiliki proses di tempat untuk memastikan bahwa perangkat lunak mereka aman, kokoh, dan dapat dipertahankan.

Bagian dari proses ini adalah mengirimkan kode aplikasi yang saya tulis untuk ditinjau pihak ketiga. Hal ini tidak merepotkan saya karena saya berpikir kode saya sempurna dan ulasan dari perusahaan tersebut akan mengatakan hal yang sama.

Ketika mereka menyampaikan responnya, keputusannya berbeda dengan yang saya pikirkan. Meskipun mereka mengatakan bahwa kualitas kodenya tidaklah buruk, mereka menunjukkan fakta bahwa perawatan dan pengujian kodenya akan sulit (waktu itu pengujian unit di pengembangan iOS belum sepopuler sekarang).

Saya tidak memedulikan penilaian mereka, saya pikir kode saya sudah hebat dan tidak ada lagi cara untuk meningkatkannya. Orang-orang di perusahaan itu pasti tidak memahaminya!

Saya memiliki keangkuhan umum seorang pengembang aplikasi: kami sering berpikir bahwa yang kami lakukan sudah hebat dan orang lain tidak memahaminya.

Setelah melihat ke belakang, sayalah yang salah. Tidak lama kemudian, saya mulai membaca tentang beberapa praktek terbaik. Sejak saat itu, masalah di kode saya mulai terasa seperti nyeri di jempol tangan. Saya menyadari bahwa, seperti kebanyakan pengembang iOS lainnya, saya telah terjatuh ke jebakan klasik praktek yang buruk dalam menulis kode.

Apa yang Salah Pada Kebanyakan Pengembang iOS

Salah satu praktek buruk paling umum dalam pengembangan iOS muncul ketika berganti state antara view controller dalam suatu aplikasi. Saya sendiri jatuh ke perangkap ini di masa lalu.

State propagation lintas view controller adalah hal yang vital di aplikasi iOS apa saja: Begitu pengguna Anda bernavigasi di layar aplikasi Anda dan berinteraksi dengannya, Anda harus mempertahankan state global yang melacak semua perubahan yang dilakukan pengguna pada data.

Dan inilah di mana kebanyakan pengembang iOS mencapai solusi yang jelas tapi salah: pola singleton.

Pola singleton bisa diimplementasikan dengan cepat, terutama di Swift, dan bekerja dengan baik. Anda sekedar menambahkan variabel statis ke suatu kelas untuk mempertahankan instance yang dibagikan dari kelas itu sendiri, dan selesailah sudah.

Mudah saja mengakses instance yang dibagikan ini dari mana saja dalam kode Anda:

Untuk alasan ini, banyak pengembang yang berpikir bahwa mereka menemukan solusi terbaik untuk masalah state propagation. Tetapi mereka salah.

Pola singleton sebenarnya dianggap suatu anti-pola. Ada banyak pembahasan tentang hal ini di komunitas para pengembang. Sebagai contoh, lihat pertanyaan-pertanyaan Stack Overflow ini.

Singkat cerita, singleton akan mengakibatkan masalah-masalah ini:

  • Menghasilkan banyak dependensi dalam kelas Anda, menjadikannya lebih sulit untuk diubah di masa depan.
  • Menjadikan state global bisa diakses di semua bagian kode Anda. Ini bisa menyebabkan interaksi kompleks yang sulit dilacak dengan menghasilkan banyak bug yang tidak diinginkan.
  • Menjadikan kelas Anda sangat sulit diuji, karena Anda tidak bisa memisahkannya dari suatu singleton dengan mudah.

Di titik ini, sejumlah pengembang akan berpikir, "Ah, saya punya solusi yang lebih baik. Saya akan menggunakan AppDelegate".

Masalahnya adalah kelas AppDelegate di aplikasi iOS diakses melalui instance UIApplication yang dibagikan:

Tetapi instance UIApplication yang dibagikan itu sendiri adalah suatu singleton. Jadi Anda belum memecahkan masalah apapun.

Solusi masalah ini adalah injeksi dependensi. Injeksi dependensi adalah kelas yang tidak memanggil atau membuat dependensinya sendiri, melainkan menerimanya dari luar.

Untuk melihat bagaimana menggunakan injeksi dependensi di aplikasi-aplikasi iOS dan bagaimana itu bisa memungkinkan state sharing, pertama kita harus mengunjungi lagi pola arsitektur fundamental aplikasi iOS yakni pola Model-View-Controller.

Memperluas Pola MVC

Pola MVC, secara singkat, menyatakan bahwa ada tiga lapisan di arsitektur suatu aplikasi iOS:

  • Layer model mewakili data dalam suatu aplikasi.
  • Layer view menunjukkan informasi di layar dan memungkinkan interaksi.
  • Layer controller bertindak sebagai perekat dua layer lainnya, memindahkan data antara keduanya.

Representasi umum pola MVC adalah semacam ini:

Simplistic view of the MVC pattern

Masalahnya diagram ini salah.

"Rahasianya" bersembunyi di depan mata dalam beberapa baris di dokumentasi Apple.

"Orang bisa menggabungkan peran-peran MVC yang dimainkan oleh suatu objek, misalnya dalam membuat suatu objek akan memenuhi peran sebagai controller maupun view—dalam hal ini disebut sebagai view controller. Dengan cara yang sama Anda juga bisa memiliki objek-objek model-controller."

Banyak pengembang yang berpikir bahwa view controller adalah satu-satunya controller yang ada di aplikasi iOS. Akibatnya banyak kode yang ditulis di dalamnya karena gagal menemukan tempat yang lebih baik. Inilah yang membawa para pengembang untuk menggunakan singleton ketika mereka harus melakukan propagasi state: karena inilah solusi yang kelihatannya paling mungkin.

Dari baris yang dikutip di atas, jelas bahwa kita bisa menambahkan entitas baru ke dalam pemahaman kita tentang pola MVC: model controller. Model controller berurusan dengan model aplikasinya, memenuhi peran yang tidak bisa dilakukan model itu sendiri. Seperti inilah semestinya skema di atas:

Diagram of the MVC pattern updated with view and model controllers

Contoh yang sempurna dari model controller berguna untuk menjaga state aplikasi. Model semestinya hanya merepresentasikan data aplikasi. Tidak mengurusi state aplikasi.

Penjagaan state ini biasanya berakhir di dalam view controller, tetapi sekarang ada tempat yang baru  dan lebih baik untuk meletakkannya: suatu model controller. Model controller bisa dimasukkan ke view controller karena muncul ke layar melalui injeksi dependensi.

Kita telah memecahkan anti-pola singleton. Mari kita saksikan solusi ini dalam prakteknya dengan suatu contoh.

Propagasi State Lintas View Controller Dengan Menggunakan Injeksi Dependensi

Kita akan menulis aplikasi sederhana untuk melihat contoh konkret cara kerjanya. Aplikasi ini akan menunjukkan kutipan favorit Anda dalam satu layar, dan memungkinkan Anda mengedit kutipannya di suatu layar kedua.

Ini artinya aplikasi kita akan membutuhkan dua view controller, yang akan membutuhkan berbagi state. Setelah Anda melihat bagaimana solusi ini bekerja, Anda bisa mengekspansi konsepnya ke aplikasi dengan ukuran dan kompleksitas apa saja.

Untuk memulainya, kita butuh suatu model untuk merepresentasikan datanya, dalam hal ini adalah suatu kutipan. Ini bisa dilakukan dengan satu struktur sederhana:

Model Controller

Lalu kita harus membuat suatu  model controller yang menangani state aplikasinya. Model controllernya harus berupa suatu kelas. Ini karena kita akan membutuhkan satu instance tunggal yang akan dimasukka ke semua view controller. Tipe nilai seperti struktur disalin ketika dimasukkan, jadi jelas bukan solusi yang tepat.

Yang dibutuhkan semua model controller di contoh kita adalah suatu properti yang bisa menyimpan kutipan saat ini. Tetapi tentu saja di model controller aplikasi yang lebih besar akan lebih kompleks daripada yang ini:

Saya menetapkan nilai default untuk properti quote jadi kita sudah punya sesuatu untuk ditampilkan di layar ketika aplikasinya dibuka. Ini tidak penting, dan Anda bisa mendeklarasikan propertinya sebagai opsional yang diinisialisasi ke nil, jika Anda ingin aplikasi dibuka dalam keadaan kosong.

Membuat Antar Muka Pengguna

Sekarang kita punya model controller, yang akan menjadi wadah state aplikasi kita. Berikutnya, kita butuh view controller yang akan mewakili tampilan layar aplikasinya.

Pertama, kita buat antar muka penggunanya. Seperti inilah dua view controller tampak dalam storyboard aplikasi.

view controllers in the storyboard

Antar muka view controller pertama dibuat dari beberapa label dan tombol, disusun jadi satu dengan batasan auto layout yang sederhana. (Anda bisa membaca lebih banyak tentang auto layout di sini di Envato Tuts+).

Antarmuka view controller kedua sama saja, tetapi memiliki text view untuk mengedit teks kutipan dan isian teks untuk mengedit penulisnya.

Kedua view controller dihubungkan dengan suatu modal presentation segue tunggal yang berasal dari tombol Edit quote.

Anda bisa mengeksplorasi antar muka dan batasan view controller di repositori GitHub.

Menulis Kode View Controller dengan Injeksi Dependensi

Sekarang kita harus menulis kode view controllernya. Hal penting yang perlu dicamkan di pikiran adalah model tersebut harus menerima instance model controller dari luar, melalui injeksi dependensi. Jadi harus ada properti yang diekspos untuk tujuan ini:

Kita bisa menamai view controller pertama QuoteViewController. View controller membutuhkan sejumlah outlet untuk label kutipan dan penulisnya di antar muka.

Ketika view controller ini tampil di layar, kita mengisi antar mukanya untuk menunjukkan kutipan saat ini. Kita memasukkan kode untuk melakukan ini di metode controller viewWillAppear(_:).

Kita bisa memasukkan kode ini ke dalam metode viewDidLoad(), yang ini cukup umum. Tetapi masalahnya viewDidLoad() hanya dipanggil sekali saja ketika view controller dibuat. Di aplikasi kita, pembaruan antar QuoteViewController muka harus dilakukan setiap kali tampil ke layar. Ini karena pengguna bisa mengedit kutipan di layar kedua.

Inilah mengapa kita menggunakan metode viewWillAppear(_:) bukannya viewDidLoad(). dengan cara ini kita bisa memperbarui tampilan view controller antar muka pengguna begitu tampil di layar. Jika Anda ingin tahu lebih banyak tentang siklus hidup view controller dan semua metode yang dipanggil, saya menulis suatu artikel yang mendetailkan semuanya.

Edit View Controller

Sekarang kita harus menulis kode untuk view controller kedua. Akan kita namai ini EditViewController.

View controllernya adalah seperti yang sebelumnya:

  • View controller tersebut memiliki outlet untuk text view dan text field yang akan pengguna manfaatkan untuk menyunting kutipannya.
  • Juga memiliki properti untuk injeksi dependensi instance model controller.
  • Akan mengisi antarmuka penggunanya sebelum tampil di layar.

Dalam hal ini, saya menggunakan metode viewDidLoad() karena view controller ini hanya tampil sekali di layar.

Membagikan State

Sekarang kita harus memasukkan state antara dua view controller dan memperbaruinya ketika pengguna mengedit kutipannya.

Kita memasukkan state aplikasi di metode prepare(for:sender:) dari QuoteViewController. Metode ini dipicu dengan sambungan yang tekoneksi ketika pengguna menyentuh tombol Edit quote.

Di sini kita memasukkan instance ModelController yang mempertahankan state aplikasi. Di sinilah tejadi injeksi dependensi untuk EditViewController.

Di EditViewController, kita harus memperbarui state untuk kutipan yang baru dimasukkan sebelum kembali ke viewcontroller sebelumnya. Ini bisa dilakukan dengan tindakan yang terhubung dengan tombol Save:

Menginisialisasi Model Controller

Kita hampir selesai, tetapi Anda mungkin sudah melihat bahwa kita masih melewatkkan sesuatu: QuoteViewController melewati ModelController menuju EditViewController melalui injeksi dependensi. Tetapi siapa yang memberikan instance ini ke QuoteViewController? Ingatlah bahwa ketika menggunakan injeksi dependensi, view controller tidak boleh membuat dependensinya sendiri. Harus datang dari luar.

Tetapi tidak ada view controller sebelum QuoteViewController, karena inilah view controller pertama di aplikasi kita. Kita butuh objek lain untuk membuat instance ModelController dan melewatkannya ke QuoteViewController.

Objek yang dimaksud adalah AppDelegate. Peran delegasi aplikasi adalah merespon metode siklus hidup aplikasi dan kemudian mengonfigurasi aplikasinya.  Salah satu metodenya adalah application(_:didFinishLaunchingWithOptions:) yang dipanggil seketika itu juga apabila aplikasinya diluncurkan. Yaitu ketika kita membuat instance ModelController dan memasukkannya ke QuoteViewController:

Aplikasinya searang lengkap. Tiap view controller memiliki akses ke state global aplikasi, tetapi kita sama sekali tidak menggunakan singleton di bagian manapun dari kode kita.

Anda bisa mengunduh proyek Xcode untuk contoh aplikasi ini di repositori tutorial GitHub.

Kesimpulan

Di artikel ini Anda sudah melihat bahwa menggunakan singleton untuk mempropagasi state di aplikasi iOS adalah praktek yang buruk. Singleton mengakibatkan banyak masalah, meskipun sangat mudah dibuat dan digunakan.

Kita memecahkan masalah ini dengan melihat pola MVC lebih dekat lagi dan memahami berbagai kemungkinan yang tersembunyi di dalamnya. Melalui penggunaan model controller dan injeksi dependensi, kita mampu melakukan propagasi state di aplikasi lintas semua view controller tanpa menggunakan singleton.

Ini adalah contoh aplikasi yang sederhana, tetapi konsepnya bisa digeneralisasi ke aplikasi dengan berbagai kompleksitas. Ini adalah praktek terbaik untuk propagasi state di aplikasi iOS, Sekarang saya menggunakannya di semua aplikasi yang saya tullis untuk para klien saya.

Beberapa hal yang harus diperhatikan ketika Anda memperluas konsep ini ke aplikasi yang lebih besar:

  • Model controller bisa menghemat state aplikasi, misalnya dalam suatu file. Dengan cara ini, data kita akan diingat setiap kali kita menutup aplikasinya. Anda juga bisa menggunakan solusi penyimpanan yang lebih kompleks, misalnya Core Data. Rekomendasi adalah menjaga fungsionalitasnya di model controller terpisah yang hanya menangani penyimpanan. Controller ini bisa digunakan oleh model controller yang menangani state aplikasi.
  • Di aplikasi dengan alur yang lebih kompleks, Anda akan punya banyak wadah di alur aplikasinya. Biasanya ini adalah controller navigasi, dengan tab bar controller biasa. Konsep injeksi dependensi masih berlaku, tetapi Anda harus memperhitungkan wadahnya. Anda bisa memilih untuk menggali ke dalam view controller di dalam wadah ketika melakukan injeksi dependensi, atau membuat sub kelas container kustom yang meneruskan model controllernya.
  • Jika Anda menambahkan jaringan ke aplikasi Anda, ini harus dijalankan di model controller yang terpisah. Suatu view controller bisa melakukan permintaan jaringan melalui network controller lalu meneruskan data yang dihasilkan ke model controller yang mempertahankan statenya. Ingat bahwa peran view controller tepatnya adalah: bertindak sebagai objek perekat yang meneruskan data antar berbagai objek.

Tetaplah di sini untuk mendapatkan berbagai tips dan praktek terbaik lebih lanjut untuk pengembangan aplikasi iOS!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.