ViperBase هو تطبيق بنية Viper لاستخدام منصة iOS.
يهدف هذا المشروع إلى جعل استخدام Viper واعتماده أسهل ، مما يسهل جميع الإعدادات اللازمة للعمل مع هذه البنية ومحاولة تقليل العمل اليدوي قدر الإمكان بسبب خصائصه.
Cocoapods هو مدير التبعية لمشاريع الكاكاو. لدمج ViperBase في مشروع Xcode الخاص بك ، حدده في Podfile :
pod 'viper-base' , '~> 2.1' تتمثل تجربة Baddest التي قد تتمتع بها عند استخدام Viper في إنشاء جميع الملفات والفئات والبروتوكولات التي تحتاجها هذه البنية بنفسك . هذا يؤثر على الإنتاجية كثيرًا ، مما يجعل مهمة بسيطة يستغرق الكثير من الوقت.
يسمح لنا Xcode بإنشاء قوالب مخصصة. مع توفر هذا المورد ، قررنا إنشاء قالب خصيصًا لـ ViperBase . باستخدام هذا القالب ، لن يكون هناك عمل يدوي بعد الآن.
لتثبيت القالب واستخدامه ، تحقق من هذا البرنامج التعليمي.
تمثل الوحدة شاشة التطبيق.
هذا التنفيذ لـ Viper ، له بعض الخصائص:
تعتبر بعض الأساليب مهمة إنشاء الوحدة النمطية كمسؤولية لجهاز التوجيه . لكن هذا ينتهك مبدأ المسؤولية الفردية ، لأنه مسؤول بالفعل عن التنقل بين الوحدات النمطية.
لحل هذه المشكلة ، تم إنشاء الباني . إنه مسؤول عن إنشاء جميع مكونات الوحدة ونطق الاتصالات المعنية.
وبهذه الطريقة ، يمكن استخدام الكيانات في وحدة واحدة أو أكثر ، لأنها هياكل بسيطة بدون منطق عمل.
IOS Architecture ، يتم تنفيذ التنقل من uiviewController إلى uiviewController آخر . ولهذا السبب ، يجب على جهاز التوجيه امتلاك نشأة عرض الوحدة الحالية ، فقط لغرض التنقل ، وتلقي عرض الوحدة النمطية للوجهة ، من منشئها.
تحدد العقود كيفية إجراء التواصل بين الطبقات. تتكون وحدة من 5 عقود:
تتوافق فئة العرض مع هذا البروتوكول. يحدد التواصل من مقدم العرض لعرضه
// MARK: - View Contract
protocol MyModuleViewProtocol : class {
} تتوافق فئة مقدم العرض مع هذا البروتوكول. يحدد الاتصال من العرض إلى مقدم العرض
// MARK: - View Output Contract
protocol MyModuleViewOutputProtocol : class {
} تتوافق فئة التفاعل مع هذا البروتوكول. يحدد التواصل من مقدم العرض إلى التفاعل
// 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 {
}تحتاج فقط إلى تنفيذ الأساليب المحددة في عقد التفاعل .
final class MyModuleRouter : VIPERRouter {
weak var viewController : UIViewController !
}
// MARK: - MyModuleRouterProtocol
extension MyModuleRouter : MyModuleRouterProtocol {
}تحتاج فقط إلى تنفيذ الأساليب المحددة في عقد جهاز التوجيه .
في جهاز التوجيه ، يمكن إجراء التنقل بطريقتين: تقديم الوحدة النمطية أو دفع الوحدة النمطية إلى مكدس التنقل . لأداء التنقل ، استخدم الأساليب أدناه:
- PresentModule (Winview: inmbedin: الرسوم المتحركة: الانتهاء :)
تعرض هذه الطريقة الوحدة النمطية التالية. تحقق من تفاصيل المعلمات أدناه:
withView : عرض الوحدة النمطية للتنقل إلى.embedIn : .navigationController أو .none . القيمة الافتراضية هي .noneanimated : ما إذا كان لأداء الرسوم المتحركة للانتقال أم لا. القيمة الافتراضية truecompletion : المعالج يسمى عندما ينتهي الانتقال. القيمة الافتراضية nil- PushModule (Winview: inmbedin: الرسوم المتحركة :)
هذه الطريقة تدفع الوحدة التالية إلى مكدس التنقل . إنه يعمل فقط إذا تم تضمين الوحدة النمطية الحالية في وحدة تحكم التنقل أو هي جزء من مكدس التنقل.
withView : عرض الوحدة النمطية للتنقل إلى.embedIn : .navigationController أو .none . القيمة الافتراضية هي .noneanimated : ما إذا كان لأداء الرسوم المتحركة للانتقال أم لا. القيمة الافتراضية true في فئة البناء ، يمكنك تحديد الطبقات المعنية 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 {
} يمكنك أيضًا تحديد طريقة تحميل واجهة المستخدم ، من خلال خاصية defaultViewUIType . هناك 3 قيم محتملة:
. storyboard ( name : " MyModuleView " , bundle : nil ) . nib ( name : " MyModuleView " , bundle : nil ) . noneيمكن استخدام الطرق الأربعة أدناه لبناء وحدة نمطية. بالإضافة إلى ذلك ، يمكنك إنشاء طرق بناء مخصصة ، وفقًا لاحتياجات الوحدة النمطية.
- يبني() :
ينشئ الوحدة ويعيد بنية VIPERModule التي تحتوي على مراجع view presenter . يمكنك استخدام مرجع مقدم العرض لتمرير البيانات بين الوحدات النمطية .
// MARK: - MyModuleRouterProtocol
extension MyModuleRouter : MyModuleRouterProtocol {
func goToNextModule ( ) {
let module = NextModuleBuilder . build ( )
pushModule ( withView : module . view )
}
}- بناء (عرض :) :
تعمل هذه الطريقة مثل الطريقة أعلاه ولكنها تتيح لك تحديد نوع واجهة المستخدم أثناء استدعاء الطريقة. هذه الطريقة مريحة عند استخدام 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)
-
تنشئ هذه الطريقة الوحدة ، وتوصيلها بوحدة تحكم التنقل وإرجاع مرجع وحدة تحكم التنقل . إذا كنت تعتزم استخدام الوحدة النمطية داخل وحدة التحكم في شريط علامات التبويب ، فيمكنك استخدام معلمة 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 تحت رخصة معهد ماساتشوستس للتكنولوجيا.