Saya mencari untuk membuat EnvironmentObject yang dapat diakses oleh View Model (bukan hanya tampilan).
Objek Lingkungan melacak data sesi aplikasi, misalnya login, token akses dll, data ini akan diteruskan ke model tampilan (atau kelas layanan di mana diperlukan) untuk memungkinkan panggilan API untuk meneruskan data dari Proyek Lingkungan ini.
Saya telah mencoba untuk meneruskan objek sesi ke penginisialisasi kelas model tampilan dari tampilan tetapi mendapatkan kesalahan.
bagaimana saya bisa mengakses / meneruskan EnvironmentObject ke model tampilan menggunakan SwiftUI?
Lihat tautan untuk menguji proyek: https://gofile.io/?c=vgHLVx
Jawaban:
Saya memilih untuk tidak memiliki ViewModel. (Mungkin waktu untuk pola baru?)
Saya telah mengatur proyek saya dengan
RootView
dan beberapa pandangan anak. Saya mengatur sayaRootView
denganApp
objek sebagai EnvironmentObject. Alih-alih ViewModel mengakses Model, semua pandangan saya mengakses kelas di App. Alih-alih ViewModel menentukan tata letak, hierarki tampilan menentukan tata letak. Dari melakukan ini dalam praktik untuk beberapa aplikasi, saya menemukan pandangan saya tetap kecil dan spesifik. Sebagai penyederhanaan berlebihan:Dalam preview saya, saya menginisialisasi
MockApp
yang merupakan subkelas dariApp
. MockApp menginisialisasi inisialisasi yang ditunjuk dengan objek Mocked. Di sini UserService tidak perlu diejek, tetapi sumber data (yaitu NetworkManagerProtocol) tidak.sumber
app.userService.logout()
.userService
harus bersifat pribadi dan hanya diakses dari dalam kelas aplikasi. Kode di atas akan terlihat seperti ini:Button(action: { app.logout() })
dan fungsi logout kemudian akan memanggil langsunguserService.logout()
.Anda seharusnya tidak. Ini adalah kesalahpahaman umum bahwa SwiftUI bekerja paling baik dengan MVVM.
MVVM tidak memiliki tempat di SwfitUI. Anda bertanya apakah Anda bisa mendorong persegi panjang ke
pas bentuk segitiga. Itu tidak cocok.
Mari kita mulai dengan beberapa fakta dan bekerja selangkah demi selangkah:
ViewModel adalah model dalam MVVM.
MVVM tidak mempertimbangkan tipe nilai (misalnya; tidak ada hal seperti itu di java) yang menjadi pertimbangan.
Model tipe nilai (model tanpa negara) dianggap lebih aman daripada referensi
tipe model (model with state) dalam arti immutability.
Sekarang, MVVM mengharuskan Anda untuk mengatur model sedemikian rupa sehingga setiap kali itu berubah, itu
pembaruan melihat dalam beberapa cara yang telah ditentukan sebelumnya. Ini dikenal sebagai penjilidan.
Tanpa ikatan, Anda tidak akan memiliki pemisahan kekhawatiran yang bagus, misalnya; refactoring keluar
model dan negara terkait dan menjaga mereka terpisah dari pandangan.
Ini adalah dua hal yang gagal dilakukan sebagian besar pengembang MVVM iOS:
iOS tidak memiliki mekanisme "mengikat" dalam pengertian java tradisional.
Beberapa hanya akan mengabaikan pengikatan, dan berpikir memanggil objek ViewModel
secara otomatis memecahkan segalanya; beberapa akan memperkenalkan Rx berbasis KVO, dan
mempersulit segalanya ketika MVVM seharusnya membuat segalanya lebih sederhana.
Model dengan negara terlalu berbahaya
karena MVVM terlalu menekankan pada ViewModel, terlalu sedikit pada manajemen negara
dan disiplin umum dalam mengelola Kontrol; sebagian besar pengembang berakhir
berpikir model dengan status yang digunakan untuk memperbarui tampilan dapat digunakan kembali dan
dapat diuji .
inilah mengapa Swift memperkenalkan tipe nilai sejak awal; model tanpa
negara.
Sekarang untuk pertanyaan Anda: Anda bertanya apakah ViewModel Anda dapat memiliki akses ke EnvironmentObject (EO)?
Anda seharusnya tidak. Karena dalam SwiftUI model yang sesuai dengan Lihat secara otomatis miliki
referensi ke EO. Misalnya;
Saya harap orang-orang dapat menghargai betapa kompaknya SDK dirancang.
Di SwiftUI, MVVM otomatis . Tidak perlu untuk objek ViewModel yang terpisah
yang secara manual mengikat untuk melihat yang memerlukan referensi EO diteruskan ke sana.
Kode di atas adalah MVVM. Misalnya; model dengan binding to view.
Tetapi karena model adalah tipe nilai, jadi alih-alih refactoring keluar model dan nyatakan sebagai
lihat model, Anda menolak kontrol (dalam ekstensi protokol, misalnya).
Ini adalah SDK resmi yang mengadaptasi pola desain ke fitur bahasa, bukan hanya
menegakkannya. Substansi melebihi bentuk.
Lihatlah solusi Anda, Anda harus menggunakan singleton yang pada dasarnya global. Kamu
harus tahu betapa berbahayanya mengakses global di mana saja tanpa perlindungan
kekekalan, yang tidak Anda miliki karena Anda harus menggunakan model tipe referensi!
TL; DR
Anda tidak melakukan MVVM dengan cara java di SwiftUI. Dan cara Swift-y untuk melakukannya tidak perlu
untuk melakukannya, itu sudah built-in.
Semoga lebih banyak pengembang melihat ini karena ini sepertinya pertanyaan yang populer.
sumber
Di bawah ini disediakan pendekatan yang berfungsi untuk saya. Diuji dengan banyak solusi dimulai dengan Xcode 11.1.
Masalahnya berasal dari cara EnvironmentObject disuntikkan dalam pandangan, skema umum
yaitu, pada tampilan pertama yang dibuat, pada objek lingkungan kedua dibuat, pada objek lingkungan ketiga disuntikkan ke dalam tampilan
Jadi jika saya perlu membuat / menata model tampilan dalam view constructor objek lingkungan belum ada di sana.
Solusi: pisahkan semuanya dan gunakan injeksi ketergantungan eksplisit
Berikut ini tampilannya dalam kode (skema umum)
Tidak ada trade-off di sini, karena ViewModel dan EnvironmentObject adalah, dengan desain, tipe referensi (sebenarnya,
ObservableObject
), jadi saya lulus di sini dan hanya ada referensi (alias pointer).sumber