Data formulir hilang saat pengguna berpindah aplikasi karena iOS bisa menghentikan proses di latar belakang kapan saja. Ada tiga pilihan penyimpanan yang paling sering dipakai untuk kasus ini: UserDefaults, file JSON di direktori Documents, dan SwiftData. Perbedaannya terletak pada ukuran data, kerumitan struktur, serta seberapa sering isian berubah.
Menentukan kriteria sebelum memilih penyimpanan
Sebelum menulis kode, tentukan dulu bentuk data formulir. Formulir dengan lima kolom teks pendek berbeda kebutuhan penyimpanannya dengan formulir unggah dokumen berisi banyak entri. Empat pertanyaan berikut membantu mempersempit pilihan:
- Berapa ukuran data maksimum yang mungkin tersimpan, termasuk lampiran?
- Apakah data perlu dicari atau difilter, misalnya daftar draf berdasarkan tanggal?
- Seberapa sering isian berubah, setiap ketikan atau hanya saat pengguna menekan tombol simpan?
- Apakah data mengandung informasi pribadi yang sebaiknya dienkripsi?
Jawaban atas pertanyaan itu menentukan apakah cukup memakai penyimpanan ringan atau perlu basis data. Jika formulir hanya berisi teks pendek dan selalu satu draf aktif, UserDefaults sudah memadai. Jika pengguna bisa menyimpan banyak draf dan mencarinya kembali, SwiftData lebih tepat. File JSON berada di tengah, cocok untuk struktur bersarang yang tidak butuh kueri.
Langkah 1: Menyimpan draf formulir dengan UserDefaults
UserDefaults menyimpan pasangan kunci-nilai di penyimpanan preferensi aplikasi. Data yang disimpan di sini dimuat cepat dan tidak perlu menangani direktori sendiri. Batas praktisnya sekitar beberapa ratus kilobita; untuk formulir panjang dengan banyak teks, ukuran itu cepat terlampaui.
Langkah pertama, buat struktur yang sesuai dengan isian formulir dan pastikan bisa dikodekan. Simpan sebagai Data agar tipe seperti tanggal dan angka tetap utuh.
struct DraftFormulir: Codable {
var judul: String
var catatan: String
var diperbarui: Date
}
enum PenyimpananDraf {
static let kunci = "draf_formulir_aktif"
static func simpan(_ draf: DraftFormulir) {
guard let data = try? JSONEncoder().encode(draf) else { return }
UserDefaults.standard.set(data, forKey: kunci)
}
static func muat() -> DraftFormulir? {
guard let data = UserDefaults.standard.data(forKey: kunci) else { return nil }
return try? JSONDecoder().decode(DraftFormulir.self, from: data)
}
}
Panggil simpan saat pengguna mengetik dengan jeda, misalnya lewat task(id:) di SwiftUI atau debounce dua detik. Menyimpan setiap ketikan langsung ke UserDefaults membuat proses tulis terlalu sering dan memperlambat antarmuka saat formulir panjang. Cara mengelola state formulir seperti ini berkaitan dengan pola yang dibahas di perbandingan cara mengelola state di SwiftUI, khususnya saat memilih antara sumber kebenaran tunggal dan beberapa sumber terpisah.
Langkah 2: Menyimpan beberapa draf sebagai file JSON
Ketika pengguna boleh menyimpan lebih dari satu draf, satu kunci UserDefaults tidak cukup. Simpan setiap draf sebagai berkas terpisah di direktori Documents, lalu muat daftarnya dari nama berkas yang ada. Pendekatan ini menjaga ukuran preferensi tetap kecil dan memudahkan penghapusan per draf.
- Buat URL direktori Documents melalui
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask). - Beri nama berkas memakai pengenal unik draf, misalnya UUID, agar tidak bentrok.
- Tulis data dengan
Data.write(to:options:)dan opsi.atomicsupaya berkas tidak rusak jika aplikasi ditutup di tengah proses. - Saat memuat, baca isi direktori, filter ekstensi, lalu dekode tiap berkas dan urutkan berdasarkan tanggal pembaruan.
Opsi .atomic menulis ke berkas sementara lebih dulu, baru mengganti berkas asli. Tanpa opsi ini, pemutusan proses di tengah penulisan bisa menyisakan berkas kosong. Untuk data pribadi, aktifkan perlindungan berkas dengan atribut .completeFileProtection agar isi terenkripsi saat perangkat terkunci.
Langkah 3: Memakai SwiftData untuk draf yang perlu dicari
Jika draf perlu difilter berdasarkan status, dicari lewat teks, atau dihubungkan dengan entitas lain seperti pelanggan dan lampiran, SwiftData menghemat banyak kode. Model dideklarasikan dengan makro @Model, dan kueri di SwiftUI cukup memakai @Query dengan predikat.
Buat model draf, lalu simpan lewat modelContext dari environment. Perubahan yang belum disimpan tetap ada di memori sampai konteks disimpan, sehingga pengguna bisa membatalkan isian dengan mengembalikan objek ke nilai semula. Perbandingan lebih rinci antara SwiftData dan Core Data, termasuk urusan migrasi skema, ada di artikel SwiftData vs Core Data.
Satu hal yang sering terlewat: simpan konteks saat aplikasi masuk ke latar belakang. SwiftData tidak otomatis menyimpan setiap perubahan, dan penghentian proses oleh sistem bisa membuang isian terakhir. Amati notifikasi scenePhase dan panggil penyimpanan saat nilainya berubah menjadi .background.
Perbandingan singkat ketiga pilihan
| Aspek | UserDefaults | File JSON | SwiftData |
|---|---|---|---|
| Ukuran data | Kecil, teks pendek | Sedang, termasuk lampiran kecil | Besar, banyak entri |
| Jumlah draf | Satu draf aktif | Banyak, satu berkas per draf | Banyak, dengan relasi |
| Pencarian dan filter | Tidak didukung | Manual di memori | Predikat dan pengurutan bawaan |
| Kerumitan kode | Paling sedikit | Sedang | Paling banyak, tetapi terstruktur |
Menangani pemulihan dan konflik data
Saat aplikasi dibuka kembali, muat draf terakhir dan bandingkan tanggal pembaruannya dengan versi yang mungkin tersimpan di server. Jika formulir juga dikirim ke backend, tentukan aturan mana yang menang: draf lokal atau data server. Untuk formulir yang hanya disimpan di perangkat, cukup tampilkan pemberitahuan bahwa draf dipulihkan, lalu beri tombol untuk membuangnya.
Jika data formulir memuat informasi pribadi pengguna, sediakan cara menghapusnya secara menyeluruh, termasuk berkas cadangan di iCloud Drive bila ada. Prinsip pembersihan yang sama berlaku seperti saat menghapus jejak digital dengan aman: hapus dari semua lokasi penyimpanan, bukan hanya dari tampilan aplikasi.
Untuk tim yang menguji alur formulir di perangkat nyata, pantau juga ketersediaan layanan pendukung seperti sinkronisasi atau unggahan berkas. Layanan pemantauan sederhana bisa membantu mendeteksi gangguan lebih awal, misalnya lewat cara memantau uptime situs dengan layanan gratis jika formulir bergantung pada endpoint tertentu.
Ringkasan
UserDefaults cocok untuk satu draf pendek yang berubah sering, file JSON pas untuk beberapa draf tanpa kebutuhan kueri, dan SwiftData dipilih ketika draf perlu dicari, difilter, atau dihubungkan dengan entitas lain. Apa pun pilihannya, simpan saat aplikasi masuk latar belakang dan saat pengguna berhenti mengetik, bukan hanya ketika tombol kirim ditekan.
Pertanyaan yang Sering Diajukan
Apakah UserDefaults aman untuk menyimpan data formulir berisi informasi pribadi?
UserDefaults tidak dirancang untuk data sensitif karena isinya tersimpan sebagai plist di dalam kontainer aplikasi tanpa enkripsi tambahan. Untuk data pribadi, gunakan Keychain bila hanya berupa kredensial, atau file dengan perlindungan berkas dan SwiftData dengan enkripsi bawaan perangkat untuk isian yang lebih besar.
Kapan sebaiknya memakai file JSON daripada SwiftData?
Pilih file JSON jika data berbentuk struktur bersarang yang jarang diubah dan tidak perlu dicari dengan predikat. File juga memudahkan ekspor data ke luar aplikasi karena formatnya terbaca langsung. Jika kebutuhan mulai mencakup filter, relasi, atau pembaruan sebagian, pindah ke SwiftData lebih hemat waktu dalam jangka panjang.
Bagaimana mencegah draf hilang saat aplikasi ditutup paksa?
Simpan data secara berkala saat pengguna mengetik dengan jeda, lalu simpan sekali lagi ketika scenePhase berubah menjadi .background atau .inactive. Penghentian paksa oleh pengguna tidak memberi kesempatan menyimpan, jadi periode simpan otomatis harus cukup pendek agar kehilangan data maksimal hanya beberapa detik isian.
More Stories
SwiftData vs Core Data: Perbandingan untuk Aplikasi iOS
Navigasi SwiftUI: Panduan Langkah demi Langkah