Viperbase เป็นการใช้สถาปัตยกรรม Viper สำหรับการใช้ในแพลตฟอร์ม iOS
โครงการนี้มีจุดมุ่งหมายเพื่อให้ การใช้ Viper และการยอมรับง่ายขึ้น อำนวยความสะดวกในการตั้งค่าที่จำเป็นทั้งหมดสำหรับการทำงานกับสถาปัตยกรรมนี้และพยายามลดงานด้วยตนเองให้มากที่สุดเท่าที่จะเป็นไปได้เนื่องจากลักษณะของมัน
Cocoapods เป็นผู้จัดการการพึ่งพาสำหรับโครงการโกโก้ หากต้องการรวม ViperBase เข้ากับโครงการ XCode ของคุณให้ระบุใน Podfile ของคุณ:
pod 'viper-base' , '~> 2.1' ประสบการณ์ที่เลวร้ายที่สุดที่คุณอาจมีเมื่อใช้ Viper คือการสร้างไฟล์คลาสและโปรโตคอลทั้งหมด ด้วยตัวเองด้วยตัวคุณเอง สิ่งนี้มีผลต่อการเพิ่มผลผลิตมากทำให้งานง่าย ๆ ต้องใช้เวลานานมาก
Xcode ช่วยให้เราสามารถสร้างเทมเพลตส่วนบุคคล ด้วยทรัพยากรนี้เราจึงตัดสินใจสร้าง เทมเพลตเป็นพิเศษสำหรับ ViperBase การใช้เทมเพลตนี้จะไม่มีการทำงานด้วยตนเองอีกต่อไป
ในการติดตั้งและใช้เทมเพลตของเราให้ตรวจสอบบทช่วยสอนนี้
โมดูลแสดงหน้าจอของแอพ
การใช้ Viper นี้มีลักษณะเฉพาะบางอย่าง:
บางวิธีพิจารณางานสร้างโมดูลเป็นความรับผิดชอบของ เราเตอร์ แต่สิ่งนี้ละเมิด หลักการความรับผิดชอบเดียว เนื่องจากมีหน้าที่รับผิดชอบในการนำทางระหว่างโมดูลแล้ว
เพื่อแก้ปัญหานี้ ผู้สร้าง ถูกสร้างขึ้น มันเป็นหน้าที่ของการสร้างส่วนประกอบทั้งหมดของโมดูลและทำการเชื่อมต่อที่เกี่ยวข้อง
ด้วยวิธีนี้เอนทิตีสามารถใช้ในหนึ่งโมดูลหรือมากกว่าเนื่องจากเป็นโครงสร้างที่เรียบง่ายโดยไม่มีตรรกะทางธุรกิจ
สถาปัตยกรรม iOS การนำทางจะดำเนินการ จาก UIViewController ไปยัง UIViewController อื่น ด้วยเหตุนี้เราเตอร์จึงต้องเป็นเจ้าของ refecence สำหรับมุมมองของโมดูลปัจจุบัน เพื่อวัตถุประสงค์ในการนำทางเท่านั้น และรับมุมมองของโมดูลปลายทางจากผู้สร้าง
สัญญากำหนดวิธีการสื่อสารระหว่างเลเยอร์ โมดูลประกอบด้วย 5 สัญญา:
คลาสมุมมอง สอดคล้องกับโปรโตคอลนี้ มันกำหนดการสื่อสารจาก ผู้นำเสนอ เพื่อ ดู
// MARK: - View Contract
protocol MyModuleViewProtocol : class {
} คลาสผู้นำเสนอ สอดคล้องกับโปรโตคอลนี้ มันกำหนดการสื่อสารจาก มุมมอง ถึง ผู้นำเสนอ
// MARK: - View Output Contract
protocol MyModuleViewOutputProtocol : class {
} คลาส Interactor สอดคล้องกับโปรโตคอลนี้ มันกำหนดการสื่อสารจาก ผู้นำเสนอ ไปสู่ การทำงานร่วมกัน
// MARK: - Interactor Contract
protocol MyModuleInteractorProtocol : class {
} คลาสผู้นำเสนอ สอดคล้องกับโปรโตคอลนี้ มันกำหนดการสื่อสารจาก การทำงานร่วมกัน ถึง ผู้นำเสนอ
// MARK: - Interactor Output Contract
protocol MyModuleInteractorOutputProtocol : class {
} คลาสเราเตอร์ สอดคล้องกับโปรโตคอลนี้ มันกำหนดการสื่อสารจาก ผู้นำเสนอ ไป เราเตอร์
// MARK: - Router Contract
protocol MyModuleRouterProtocol : class {
} final class MyModuleView : UIViewController , VIPERView {
var presenter : MyModuleViewOutputProtocol !
}
// MARK: - MyModuleViewProtocol
extension MyModuleView : MyModuleViewProtocol {
}คุณเพียงแค่ต้อง ใช้วิธีการ ที่กำหนดไว้ใน สัญญามุมมอง
final class MyModulePresenter : VIPERPresenter {
weak var view : MyModuleViewProtocol !
var interactor : MyModuleInteractorProtocol !
var router : MyModuleRouterProtocol !
}
// MARK: - MyModuleViewOutputProtocol
extension MyModulePresenter : MyModuleViewOutputProtocol {
}
// MARK: - MyModuleInteractorOutputProtocol
extension MyModulePresenter : MyModuleInteractorOutputProtocol {
}คุณเพียงแค่ต้อง ใช้วิธีการ ที่กำหนดไว้ใน สัญญาเอาท์พุทมุมมอง และ สัญญาเอาท์พุทการโต้ตอบ
final class MyModuleInteractor : VIPERInteractor {
weak var presenter : MyModuleInteractorOutputProtocol !
}
// MARK: - MyModuleInteractorProtocol
extension MyModuleInteractor : MyModuleInteractorProtocol {
}คุณเพียงแค่ต้อง ใช้วิธีการ ที่กำหนดไว้ใน สัญญาการทำงานของ Interactor
final class MyModuleRouter : VIPERRouter {
weak var viewController : UIViewController !
}
// MARK: - MyModuleRouterProtocol
extension MyModuleRouter : MyModuleRouterProtocol {
}คุณเพียงแค่ต้อง ใช้วิธีการ ที่กำหนดไว้ใน สัญญาเราเตอร์
ในเราเตอร์การนำทางสามารถทำได้สองวิธี: การนำเสนอโมดูลโมดูล หรือ ผลักโมดูลไปยังสแต็กการนำทาง ในการดำเนินการนำทางให้ใช้วิธีการด้านล่าง:
- PresentModule (WithView: EMBEDIN: Animated: เสร็จสมบูรณ์ :)
วิธีนี้นำเสนอโมดูลถัดไป ตรวจสอบรายละเอียดพารามิเตอร์ด้านล่าง:
withView : ดูโมดูลเพื่อนำทางไปembedIn : .navigationController หรือ .none ค่าเริ่มต้นคือ .noneanimated : ไม่ว่าจะแสดงภาพเคลื่อนไหวของการเปลี่ยนแปลงหรือไม่ ค่าเริ่มต้นเป็น truecompletion : Handler เรียกว่าเมื่อการเปลี่ยนผ่านเสร็จสิ้น ค่าเริ่มต้นคือ nil- PushModule (WithView: Embedin: Animated :)
วิธีนี้จะผลักโมดูลถัดไปไปยัง สแต็กการนำทาง มัน จะทำงานได้ก็ต่อ เมื่อโมดูลปัจจุบันถูก ฝังอยู่ในตัวควบคุมการนำทาง หรือเป็นส่วนหนึ่งของการนำทาง
withView : ดูโมดูลเพื่อนำทางไปembedIn : .navigationController หรือ .none ค่าเริ่มต้นคือ .noneanimated : ไม่ว่าจะแสดงภาพเคลื่อนไหวของการเปลี่ยนแปลงหรือไม่ ค่าเริ่มต้นเป็น true ในคลาส Builder คุณระบุคลาสที่เกี่ยวข้องสำหรับ View Presenter Interactor และ router สำหรับโมดูล
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 {
} นอกจากนี้คุณยังกำหนดวิธี การโหลด View UI ผ่านคุณสมบัติ defaultViewUIType มี 3 ค่าที่เป็นไปได้:
. storyboard ( name : " MyModuleView " , bundle : nil ) . nib ( name : " MyModuleView " , bundle : nil ) . none4 วิธีด้านล่างสามารถใช้ในการสร้างโมดูล นอกจากนี้ คุณสามารถสร้างวิธีการสร้างที่กำหนดเอง ตามความต้องการของโมดูล
- สร้าง() :
สร้างโมดูลและส่งคืนโครงสร้าง VIPERModule ที่มีการอ้างอิง view และ presenter คุณสามารถใช้การอ้างอิงผู้นำเสนอสำหรับ การส่งข้อมูลระหว่างโมดูล
// MARK: - MyModuleRouterProtocol
extension MyModuleRouter : MyModuleRouterProtocol {
func goToNextModule ( ) {
let module = NextModuleBuilder . build ( )
pushModule ( withView : module . view )
}
}- Build (Viewuitype :) :
วิธีนี้ใช้งานได้เหมือนวิธีการด้านบน แต่ช่วยให้คุณระบุประเภท UI ระหว่างการโทรวิธี วิธีนี้สะดวกเมื่อคุณใช้ typealias เพื่อกำหนดค่าการกำหนดค่าของตัวสร้างโมดูล
typealias MyModuleBuilder = VIPERBuilder < MyModuleView , MyModulePresenter , MyModuleInteractor , MyModuleRouter >
MyModuleBuilder . build ( viewUIType : . storyboard ( name : " MyModuleView " , bundle : nil ) )นอกจากนี้คุณยังสามารถใช้วิธีนี้ได้หากคุณตั้งใจที่จะทำการ ทดสอบหน่วย รอบ ๆ การสื่อสารของโมดูล ล้อเลียนคลาสเลเยอร์อย่างน้อยหนึ่งคลาส ตามความต้องการการทดสอบ
import XCTest
class ProjectTests : XCTestCase {
//...
func testModuleCommunication ( ) {
typealias MockModuleBuilder = VIPERBuilder < ModuleMockView , ModuleOriginalPresenter , ModuleOriginalInteractor , ModuleOriginalRouter >
let module = MockModuleBuilder . build ( )
//...
}
} - [เลิกกิจการ] buildandattachtowindow ()
นี่เป็นวิธีการสร้างพิเศษซึ่งมักจะใช้สำหรับการเริ่มต้นโมดูลเริ่มต้นของแอพที่เรียกว่าในคลาส AppDelegate :
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = InitialModuleBuilder.buildAndAttachToWindow()
return true
}
}
[อัปเดต]: ตอนนี้คุณต้อง สร้างโมดูลก่อน โดยใช้ทั้ง build() หรือ build(viewUIType:) วิธีการโทรหา attachToWindow() หรือ attachToWindow(withNavigationController:) วิธีการใหม่:
window = InitialModuleBuilder . build ( ) . attachToWindow ( ) window = InitialModuleBuilder . build ( ) . attachToWindow ( withNavigationController : true )[สำคัญ]: มีการวางแผนที่จะลบวิธีการเลิกจ้างในการเปิดตัวครั้งใหญ่ครั้งต่อไป (v3.0)
- [เลิกกิจการ] BuildandattachtonavigationController (tabbaritem :)
วิธีนี้สร้างโมดูลแนบกับตัวควบคุมการนำทางและ ส่งคืนการอ้างอิงตัวควบคุมการนำทาง หากคุณตั้งใจจะใช้โมดูล ภายในตัวควบคุมแท็บแท็บ คุณสามารถใช้พารามิเตอร์ tabBarItem เพื่อกำหนดค่ารายการแท็บแท็บสำหรับโมดูลนี้
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)
]
[อัปเดต]: ตอนนี้คุณต้อง สร้างโมดูลก่อน โดยใช้ทั้ง build() หรือ build(viewUIType:) จากนั้นเรียกใช้ attachToNavigationController() วิธีใหม่:
window = MyModuleBuilder . build ( ) . attachToNavigationController ( )[สำคัญ]: มีการวางแผนที่จะลบวิธีการเลิกจ้างในการเปิดตัวครั้งใหญ่ครั้งต่อไป (v3.0)
หากคุณมีโมดูลเฉพาะที่ คาดว่าจะได้รับข้อมูลบางอย่าง จะสะดวกในการสร้าง วิธีการสร้างที่กำหนดเอง สำหรับโมดูลนั้น ด้วยวิธีนี้ผู้สร้าง จะรับผิดชอบในการส่งข้อมูลนี้ ไปยัง presenter
เพื่อสร้างวิธีการสร้างที่กำหนดเอง:
build() ;presenter ตามการใช้งาน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
}
}เราเตอร์สามารถเรียกผู้สร้างนี้ได้เช่นนี้:
let view = NextModuleBuilder . build ( someData : " Example of data " , anotherData : " Another example of data " ) pushModule ( withView : view ) presentModule ( withView : view ) Viperbase เปิดตัวภายใต้ใบอนุญาต MIT