Viperbase adalah implementasi arsitektur Viper untuk digunakan di platform iOS.
Proyek ini bertujuan untuk membuat penggunaan viper dan adopsi lebih mudah , memfasilitasi semua pengaturan yang diperlukan untuk bekerja dengan arsitektur ini dan mencoba mengurangi sebanyak mungkin pekerjaan manual yang timbul karena karakteristiknya.
Cocoapods adalah manajer ketergantungan untuk proyek kakao. Untuk mengintegrasikan viperbase ke dalam proyek XCODE Anda, tentukan di Podfile Anda:
pod 'viper-base' , '~> 2.1' Pengalaman paling buruk yang mungkin Anda miliki saat menggunakan Viper adalah membuat, sendirian , semua file, kelas, dan protokol yang dibutuhkan oleh arsitektur ini. Ini mempengaruhi banyak produktivitas, membuat tugas sederhana membutuhkan banyak waktu.
Xcode memungkinkan kami untuk membuat templat yang dipersonalisasi. Dengan sumber daya ini tersedia, kami memutuskan untuk membuat templat khusus untuk Viperbase . Menggunakan template ini, tidak akan ada pekerjaan manual lagi.
Untuk menginstal dan menggunakan templat kami, periksa tutorial ini.
Modul mewakili layar aplikasi.
Implementasi Viper ini, memiliki beberapa kekhasan:
Beberapa pendekatan menganggap tugas penciptaan modul sebagai tanggung jawab router . Tetapi ini melanggar prinsip tanggung jawab tunggal , karena sudah bertanggung jawab atas navigasi antar modul.
Untuk mengatasi masalah ini, pembangun dibuat. Adalah bertanggung jawab untuk membuat semua komponen modul dan membuat koneksi masing -masing.
Dengan cara ini, entitas dapat digunakan dalam satu atau lebih modul, karena mereka adalah struktur sederhana tanpa logika bisnis.
Arsitektur iOS, navigasi dilakukan dari UIViewController ke UIViewController lain . Karena itu, router harus memiliki refecence untuk tampilan modul saat ini, hanya untuk tujuan navigasi , dan menerima pandangan modul tujuan, dari pembangunnya.
Kontrak menentukan bagaimana komunikasi antara lapisan akan dibuat. Modul terdiri dari 5 kontrak:
Kelas tampilan sesuai dengan protokol ini. Ini mendefinisikan komunikasi dari presenter untuk dilihat
// MARK: - View Contract
protocol MyModuleViewProtocol : class {
} Kelas presenter sesuai dengan protokol ini. Ini mendefinisikan komunikasi dari pandangan ke presenter
// MARK: - View Output Contract
protocol MyModuleViewOutputProtocol : class {
} Kelas Interactor sesuai dengan protokol ini. Itu mendefinisikan komunikasi dari presenter ke interaksi
// MARK: - Interactor Contract
protocol MyModuleInteractorProtocol : class {
} Kelas presenter sesuai dengan protokol ini. Ini mendefinisikan komunikasi dari interaktor ke presenter
// MARK: - Interactor Output Contract
protocol MyModuleInteractorOutputProtocol : class {
} Kelas router sesuai dengan protokol ini. Itu mendefinisikan komunikasi dari presenter ke router
// MARK: - Router Contract
protocol MyModuleRouterProtocol : class {
} final class MyModuleView : UIViewController , VIPERView {
var presenter : MyModuleViewOutputProtocol !
}
// MARK: - MyModuleViewProtocol
extension MyModuleView : MyModuleViewProtocol {
}Anda hanya perlu mengimplementasikan metode yang ditentukan dalam kontrak tampilan .
final class MyModulePresenter : VIPERPresenter {
weak var view : MyModuleViewProtocol !
var interactor : MyModuleInteractorProtocol !
var router : MyModuleRouterProtocol !
}
// MARK: - MyModuleViewOutputProtocol
extension MyModulePresenter : MyModuleViewOutputProtocol {
}
// MARK: - MyModuleInteractorOutputProtocol
extension MyModulePresenter : MyModuleInteractorOutputProtocol {
}Anda hanya perlu mengimplementasikan metode yang ditentukan dalam kontrak output tampilan dan kontrak output interaksi .
final class MyModuleInteractor : VIPERInteractor {
weak var presenter : MyModuleInteractorOutputProtocol !
}
// MARK: - MyModuleInteractorProtocol
extension MyModuleInteractor : MyModuleInteractorProtocol {
}Anda hanya perlu mengimplementasikan metode yang ditentukan dalam kontrak Interactor .
final class MyModuleRouter : VIPERRouter {
weak var viewController : UIViewController !
}
// MARK: - MyModuleRouterProtocol
extension MyModuleRouter : MyModuleRouterProtocol {
}Anda hanya perlu menerapkan metode yang ditentukan dalam kontrak router .
Di router, navigasi dapat dilakukan dengan dua cara: menyajikan modul secara modul atau mendorong modul ke tumpukan navigasi . Untuk melakukan navigasi, gunakan metode di bawah ini:
- PresentModule (WithView: Embedin: Animasi: Penyelesaian :)
Metode ini menyajikan modul berikutnya secara modal. Periksa detail parameter di bawah ini:
withView : Tampilan modul untuk menavigasi.embedIn : .navigationController atau .none . Nilai standarnya adalah .noneanimated : Apakah akan melakukan animasi transisi atau tidak. Nilai defaultnya truecompletion : Handler menelepon saat transisi selesai. Nilai defaultnya adalah nil- PushModule (WithView: embedin: animasi :)
Metode ini mendorong modul berikutnya ke tumpukan navigasi . Ini hanya berfungsi jika modul saat ini disematkan dalam pengontrol navigasi atau merupakan bagian dari tumpukan navigasi.
withView : Tampilan modul untuk menavigasi.embedIn : .navigationController atau .none . Nilai standarnya adalah .noneanimated : Apakah akan melakukan animasi transisi atau tidak. Nilai defaultnya true Di kelas pembangun, Anda menentukan kelas masing -masing untuk View , Presenter , Interactor dan lapisan router untuk modul.
final class MyModuleBuilder : VIPERBuilder < MyModuleView , MyModulePresenter , MyModuleInteractor , MyModuleRouter > {
override class var defaultViewUIType : VIPERViewUIType {
return . storyboard ( name : " MyModuleView " , bundle : nil )
}
}
// MARK: - Builder custom methods
extension MyModuleBuilder {
} Anda juga menentukan cara tampilan UI akan dimuat , melalui properti defaultViewUIType . Ada 3 kemungkinan nilai:
. storyboard ( name : " MyModuleView " , bundle : nil ) . nib ( name : " MyModuleView " , bundle : nil ) . none4 metode di bawah ini dapat digunakan untuk membangun modul. Selain itu, Anda dapat membuat metode pembuatan khusus , sesuai dengan kebutuhan modul.
- membangun() :
Membuat modul dan mengembalikan struktur VIPERModule yang berisi view dan referensi presenter . Anda dapat menggunakan referensi presenter untuk menyampaikan data antara modul .
// MARK: - MyModuleRouterProtocol
extension MyModuleRouter : MyModuleRouterProtocol {
func goToNextModule ( ) {
let module = NextModuleBuilder . build ( )
pushModule ( withView : module . view )
}
}- build (viewuitype :) :
Metode ini berfungsi seperti metode di atas tetapi memungkinkan Anda untuk menentukan tipe UI selama panggilan metode. Metode ini lebih mudah ketika Anda menggunakan typealias untuk menentukan konfigurasi pembuat modul.
typealias MyModuleBuilder = VIPERBuilder < MyModuleView , MyModulePresenter , MyModuleInteractor , MyModuleRouter >
MyModuleBuilder . build ( viewUIType : . storyboard ( name : " MyModuleView " , bundle : nil ) )Anda juga dapat menggunakan metode ini jika Anda bermaksud melakukan tes unit di sekitar komunikasi modul, mengejek satu atau lebih kelas lapisan , sesuai dengan kebutuhan tes.
import XCTest
class ProjectTests : XCTestCase {
//...
func testModuleCommunication ( ) {
typealias MockModuleBuilder = VIPERBuilder < ModuleMockView , ModuleOriginalPresenter , ModuleOriginalInteractor , ModuleOriginalRouter >
let module = MockModuleBuilder . build ( )
//...
}
} - [dicabut] buildandattachtowindow ()
Ini adalah metode pembuatan khusus, biasanya digunakan untuk memulai modul awal aplikasi, yang disebut di kelas AppDelegate :
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = InitialModuleBuilder.buildAndAttachToWindow()
return true
}
}
[Diperbarui]: Sekarang Anda harus membangun modul terlebih dahulu , menggunakan baik build() atau build(viewUIType:) Metode, lalu hubungi attachToWindow() atau attachToWindow(withNavigationController:) Metode baru:
window = InitialModuleBuilder . build ( ) . attachToWindow ( ) window = InitialModuleBuilder . build ( ) . attachToWindow ( withNavigationController : true )[PENTING]: Direncanakan untuk menghapus metode yang sudah usang dalam rilis besar berikutnya (v3.0)
- [DiscreCated] buildAndattachtonavigationController (tabbaritem :)
Metode ini membuat modul, lampirkan ke pengontrol navigasi dan mengembalikan referensi pengontrol navigasi . Jika Anda bermaksud menggunakan modul di dalam pengontrol bar tab , Anda dapat menggunakan parameter tabBarItem untuk mengonfigurasi item bilah tab untuk modul ini.
let tabBarController = UITabBarController()
let bookmarksItem = UITabBarItem(tabBarSystemItem: .bookmarks, tag: 0)
let contactsItem = UITabBarItem(tabBarSystemItem: .contacts, tag: 1)
let downloadsItem = UITabBarItem(tabBarSystemItem: .downloads, tag: 2)
tabBarController.viewControllers = [
BookmarksBuilder.buildAndAttachToNavigationController(tabBarItem: bookmarksItem),
ContactsBuilder.buildAndAttachToNavigationController(tabBarItem: contactsItem),
DownloadsBuilder.buildAndAttachToNavigationController(tabBarItem: downloadsItem)
]
[Diperbarui]: Sekarang Anda harus membangun modul terlebih dahulu , menggunakan baik build() atau build(viewUIType:) Metode, lalu hubungi attachToNavigationController() Metode baru:
window = MyModuleBuilder . build ( ) . attachToNavigationController ( )[PENTING]: Direncanakan untuk menghapus metode yang sudah usang dalam rilis besar berikutnya (v3.0)
Jika Anda memiliki modul spesifik yang mengharapkan untuk menerima beberapa data , lebih mudah untuk membuat metode pembuatan khusus untuk modul itu. Dengan begitu, pembangun akan bertanggung jawab atas memberikan data ini kepada presenter .
Untuk membuat metode pembuatan khusus:
build() ;presenter , sesuai dengan implementasi;view . // MARK: - Builder custom methods
extension NextModuleBuilder {
static func build ( someData : Any , anotherData : Any ) -> MyModuleView {
let module = build ( )
module . presenter . someData = someData
module . presenter . anotherData = anotherData
return module . view
}
}Router dapat menyebut pembangun ini seperti ini:
let view = NextModuleBuilder . build ( someData : " Example of data " , anotherData : " Another example of data " ) pushModule ( withView : view ) presentModule ( withView : view ) Viperbase dirilis di bawah lisensi MIT.