
Dalam artikel terakhir saya, saya melihat bagaimana perubahan berbasis data SwiftUI memaksa pemisahan mendasar antara model dan tampilan. Cakupan pemisahannya terbatas dan hanya memerlukan adanya nilai status atau observasi yang mendorong pembaruan tampilan.
Jika Anda memerlukan pemisahan yang lebih jelas antara model dan tampilan, membagi aplikasi Anda menjadi beberapa modul (nama untuk perpustakaan terpisah di Swift) adalah langkah teknis terbaik yang dapat Anda ambil. Memisahkan lapisan arsitektur (seperti model, tampilan, dan lapisan opsional lainnya) ke dalam modul masing-masing memungkinkan Anda menetapkan aturan tentang bagaimana lapisan terhubung, jenis dan properti mana yang harus dapat diakses, dan terus menambahkan yang baru. Overhead lapisan lebih rendah .
Jika Anda ingin meningkatkan arsitektur aplikasi Anda, satu langkah yang harus Anda ambil sebelum memilih untuk membuat perubahan arsitektur lainnya adalah dengan memindahkan model Anda ke mod terpisah. Ini dapat mengungkap masalah yang tidak pernah Anda ketahui sebelumnya dan mencegah masalah di masa depan.
pertanyaan
Ketika saya menulis aplikasi CwlFeedReader di artikel pertama seri ini, navigator proyek Xcode terlihat seperti ini:

Status permulaan aplikasi CwlFeedReader
Aplikasi sebenarnya hanya memiliki 5 file dalam satu folder. Saya telah memasukkan ketiga “utilitas” ke dalam folder terpisah, tetapi alasan utama untuk melakukan ini adalah karena ini adalah komponen yang sebenarnya tidak saya bicarakan di artikel ini – mereka adalah dependensi dan saya tidak menganggapnya sebagai bagian dari aplikasi. .
Apakah memang ada masalah yang perlu kita selesaikan di sini? Aplikasi ini sangat kecil dan sederhana; membutuhkan Hancurkan.
Alasan saya ingin membagi proyek ini terbagi dalam tiga kategori berikut.
1. Desain antarmuka
semuanya internal
ada Model
Dapat diakses melalui tipe lapisan tampilan. Tampilan mungkin melakukan beberapa hal yang tidak bijaksana, seperti memulai pengunduhan sendiri dan menyimpannya langsung di dalamnya URLSessionDataTask
atau perbarui feed
Lacak perubahan pengguna dan simpan kembali ke lokasi yang sama – mungkin bertentangan dengan pembaruan berdasarkan model.
Ini jelas merupakan jalan pintas yang ditulis dengan buruk ketika pengembang sedang terburu-buru atau tidak berpikir jernih tentang enkapsulasi. Jika masalah serupa tidak diatasi sebelum melakukan kode, aplikasi dapat tiba-tiba dipenuhi bug yang mengejutkan. Ya, kita dapat mencegah hal ini dengan menandai properti ini sebagai sederhana private
Tapi kemudian kami menghilangkan kemampuan untuk mematahkannya Model
Kategori di beberapa file (karena semuanya perlu diakses private
harus tetap dalam file yang sama).
Kami juga memiliki file model besar dengan semua yang diberi tag private
Atau antarmuka bocor yang membutuhkan kewaspadaan terus-menerus.
Lebih baik memilikinya Model
Tinggal di mod terpisah. Jika ada sesuatu yang aman untuk dilihat, kami menandainya public
. Jika sesuatu membutuhkan kontrol yang kuat, kita bisa menggunakannya private
Tetapi internal
Akses menjadi sangat berguna – terutama karena internal
adalah nilai default.
2. Aturan koneksi
Lapisan model tidak boleh mereferensikan semua jenis lapisan tampilan, panah koneksi harus mengarah dari tampilan ke model; Dalam satu modul, kita tidak dapat mencegah model mengakses tipe tampilan. Setelah kita memindahkan model ke mod terpisah, kita dapat membiarkan sistem build menerapkannya untuk kita.
3. Pengujian unit
Ini adalah pertimbangan teknis karena cara kerja proyek Xcode, tetapi operasi apa pun untuk menambahkan rangkaian pengujian ke proyek Xcode akan menghadapi hambatan yang lebih besar daripada menambahkan target pengujian melalui Swift Suite Manager.
Misalnya, jika saya menambahkan paket unit test ke proyek, itu akan menambahkan 130 baris ke file pbxproj dan info.plist. Konten yang ditambahkan hampir sama besarnya dengan ukuran kode Swift aplikasi. Meskipun sebagian besar baris ini dibuat secara otomatis, Xcode ingin kita mempertahankan semua pengaturan ini. Kehadiran begitu banyak boilerplate dalam permintaan git merge memang sangat membingungkan.
Selain itu, jika kita ingin pengujian unit untuk macOS dan iOS (ingat, ini adalah proyek multi-platform), kita perlu menambahkan dua paket pengujian unit. Ini adalah 260 baris konfigurasi proyek Xcode.
Penghinaan terakhir adalah mencoba menjalankan pengujian unit yang dilampirkan langsung ke aplikasi tidak ada gunanya, karena selama paket pengujian dimuat, seluruh aplikasi akan berjalan. Membiarkan seluruh aplikasi berjalan di latar belakang dapat mengganggu pengujian, jika tidak mengganggu, namun tentu saja memperlambatnya.
Buat paket Swift sebaris
Secara historis, saya akan menyelesaikan beberapa masalah di atas dengan membuat subkerangka. Anda akan masuk ke menu File → Baru → Target dan membuat bingkai iOS baru. Jika proyek Anda tidak bisa Gunakan Swift Package Manager maka ini masih merupakan cara terbaik.
Namun, kerangka kerja terpisah mengalami banyak masalah yang tercantum di atas dalam “Pengujian Unit”: xcodeproj membengkak dan setiap platform memerlukan kerangka kerja terpisah.
Untungnya, kami memiliki Swift Package Manager, yang memberikan pengalaman lebih baik.
Dokumentasi untuk Swift Package Manager di Xcode hampir seluruhnya berfokus pada penambahan dependensi dari luar proyek atau membuat perpustakaan untuk dibagikan di luar proyek. Tak satu pun dari fitur-fitur ini yang menarik bagi kami di sini.
Anda tidak perlu menggunakan fitur manajemen ketergantungan Swift Package Manager agar ini berfungsi.
Untuk aplikasi CwlFeedReader, saya memilih “File → New → Swift Package…” dari menu bar. Ketika kotak dialog “Simpan Sebagai:” muncul, saya pergi ke folder yang sama yang berisi file CwlFeedReader.xcodeproj proyek, ketik “CwlFeedReaderLib” di bidang nama “Simpan Sebagai:”, dan masukkan bidang nama “Simpan Sebagai:” . Pilih proyek “CwlFeedReader”.

Buat paket Swift sebaris
Setelah membangun paket, saya pergi ke target aplikasi CwlFeedReader dan menambahkan CwlFeedReaderLib di bawah manifes Kerangka, Perpustakaan, dan Konten Tertanam.
Buat struktur yang diperlukan
Sekarang saya dapat mengatur ulang file di browser proyek, menempatkan “Model.swift” di bawah “CwlFeedReaderLib/Sources/Model”, “IdentifyingError.swift” di bawah “CwlFeedReaderLib/Sources/Toolbox” dan “View+ PlatformCompatibility” .swift” dan “WebView .swift” terletak di bawah “CwlFeedReaderLib/Sources/ViewToolbox”.

Status aplikasi CwlFeedReader yang diinginkan
Memperbarui file Package.swift untuk menyertakan lokasi ini menghasilkan hasil berikut:
// swift-tools-version:5.3
import PackageDescription
let package = Package(
name: "CwlFeedReaderLib",
platforms: [.iOS(.v14), .macOS(.v11)],
products: [
.library(
name: "CwlFeedReaderLib",
targets: ["Model", "Toolbox", "ViewToolbox"]
),
],
targets: [
.target(
name: "Model",
dependencies: ["Toolbox"]
),
.target(
name: "Toolbox",
dependencies: []
),
.target(
name: "ViewToolbox",
dependencies: ["Toolbox"]
)
]
)
Harap dicatat bahwa saya telah membuat beberapa modul tambahan menggunakan Swift Package Manager, di mana “Toolbox” dan “ViewToolbox” menyimpan dependensi yang dapat digunakan kembali di luar komponen “Model” dan “View”.
Alasan pemisahan jenis ini adalah untuk mengidentifikasi file yang tidak boleh berisi logika khusus aplikasi – ini memberi tahu pembaca kode apa yang diharapkan dan memfasilitasi penggunaan kembali kode. Tak satu pun dari hal ini diwajibkan, namun keduanya layak untuk dipromosikan karena beban kerja yang lebih rendah.
Impor dan akses pengubah
Setelah kita benar-benar memisahkan dependensi model dan toolbox, semuanya menjadi berantakan.
Diperlukan file “Model.swift”. import Toolbox
. Keempat file yang tersisa di folder Aplikasi diperlukan import Model
dan “DetailView.swift” akan diperlukan import ViewToolbox
.
Setelah selesai, semua tampilan yang perlu diakses dalam modul model perlu ditandai public
.
Saya tidak akan mencantumkan semuanya di sini, tetapi Anda dapat memeriksa perbedaan dalam penerapan terakhir di cabang “Bagian 3” dari repositori kode CwlFeedReader.
Submodul?
Saya mengambil proyek Xcode dalam satu repositori dan memecahnya menjadi beberapa modul internal.
Submodul adalah fitur bahasa yang diusulkan yang memungkinkan efek serupa tanpa Swift Package Manager. Semua kode akan berada dalam satu modul Swift, tetapi Anda dapat “namespace” bagian tertentu untuk berada di submodul terpisah. Ada rancangan proposal untuk menambahkannya ke bahasa tersebut. Namun, submodul ini tidak pernah melampaui tahap rancangan proposal, dan tidak ada kemajuan yang terlihat.
Submod mungkin tidak akan pernah diimplementasikan, tapi itu tidak membuat saya khawatir. Saya biasanya tidak suka menyentuh sistem build, tapi menurut saya masalah ini mungkin diselesaikan dengan lebih rapi di tingkat sistem build daripada di level bahasa. Membangun sistem untuk menerapkan isolasi akan mempersulit pelanggaran enkapsulasi secara tidak sengaja, sehingga pemisahan menjadi lebih bersih.
Namun, potensi perubahan pada Swift atau Xcode membuat saya bertanya-tanya berapa lama umur artikel ini. Nasihat “lapisan isolasi” tidak pernah salah, namun penerapannya mungkin bisa digantikan.
Submodul dapat diimplementasikan di Swift, tetapi hal ini menimbulkan kebingungan apakah submodul tersebut lebih baik atau lebih buruk untuk pekerjaan tersebut.
Format xcodeproj di Xcode dapat diganti dengan Paket Swift, di mana aplikasi dibangun sebagai satu Paket Swift, dengan modul Model dan Tampilan dasar segera dibuat dari template aplikasi Proyek Baru. Tentu saja, saya berharap xcodeproj tidak digunakan lagi sejak tahun 2015; saya tidak punya alasan untuk percaya bahwa hal itu akan berubah dalam beberapa tahun mendatang.
sebagai kesimpulan
Kode lengkap untuk artikel ini tersedia di cabang Bagian III dari repositori CwlFeedReader.
Seperti kebanyakan seri “Dasar-Dasar Arsitektur Aplikasi di SwiftUI”, saya tidak melakukan sesuatu yang revolusioner di sini. Saya menghabiskan sekitar setengah dari artikel ini untuk memberikan tutorial tentang cara menambahkan suite Swift ke proyek Xcode.
Pesan lebih besar yang ingin saya sampaikan adalah bahwa isolasi lapisan memerlukan sedikit pekerjaan awal, namun memiliki manfaat yang jelas. Ketika jenis pemisahan ini diterapkan pada proyek yang sudah mapan, biasanya ditemukan sejumlah besar bug dan perilaku yang tidak diinginkan karena model dan tampilan saling mengakses. internal
detail. Perubahan ini membuat kita menyadari kemalasan kita.
Ini juga merupakan perubahan yang dapat meningkatkan produktivitas. Menambahkan modul baru kini mudah. Ingat 130 baris dikalikan 2 platform saat Anda menambahkan target pengujian baru? Sekarang dengan total 4 baris, tidak mungkin aplikasi berjalan di latar belakang dan mengganggu.
mengharapkan…
Meskipun saya menyebutkan “tes” di artikel ini, saya tidak pernah menulis tes apa pun. Ini akan berubah di artikel berikutnya, karena saya akan menyoroti inklusi arsitektur terpenting yang masih belum dimiliki aplikasi ini.