VIPER (View-Interactor-Presenter-Entity-Router) — Mutual Mobile कंपनी द्वारा iOS ऐप्स के लिए विकसित एक मॉड्यूलर आर्किटेक्चर। VIPER एप्लिकेशन को पाँच परतों में विभाजित करता है: View प्रदर्शन के लिए, Interactor व्यावसायिक तर्क के लिए, Presenter डेटा तैयार करने के लिए, Entity डेटा मॉडल के लिए, Router मॉड्यूल के बीच नेविगेशन के लिए जिम्मेदार है। VIPER मोबाइल आर्किटेक्चर में एकल जिम्मेदारी सिद्धांत का सबसे विस्तृत कार्यान्वयन है। objc.io पर लेख में और पढ़ें।
मुख्य बातें
VIPER (View-Interactor-Presenter-Entity-Router) — 2013–2014 में Mutual Mobile में बड़े iOS प्रोजेक्ट्स के लिए विकसित एक आर्किटेक्चरल पैटर्न। एप्लिकेशन की प्रत्येक स्क्रीन पाँच घटकों का एक अलग मॉड्यूल है जिसमें कड़ाई से परिभाषित जिम्मेदारियाँ हैं। VIPER मोबाइल डेवलपमेंट में एकल जिम्मेदारी सिद्धांत (Single Responsibility Principle) का सबसे सख्त कार्यान्वयन है: कोई भी घटक वह नहीं करता जो दूसरा कर सकता है।
View एक निष्क्रिय घटक है जो केवल Presenter द्वारा भेजे गए डेटा को प्रदर्शित करने के लिए जिम्मेदार है। View में कोई व्यावसायिक तर्क नहीं है, नेविगेशन नहीं संभालता, नेटवर्क अनुरोध नहीं करता। iOS में — UIViewController एक ViewProtocol के साथ। Interactor व्यावसायिक तर्क परत है जो Entity और सेवाओं (नेटवर्क, DB, GPS) के साथ काम करती है। Interactor UIKit को इम्पोर्ट नहीं करता। Presenter View और Interactor के बीच मध्यस्थ है: Interactor से डेटा प्राप्त करता है, प्रदर्शन के लिए फ़ॉर्मेट करता है, View को भेजता है। Presenter भी UIKit को इम्पोर्ट नहीं करता। Entity — डेटा मॉडल (struct, class)। Router — नेविगेशन प्रबंधित करता है: मॉड्यूल बनाता है, स्क्रीन खोलता है, मॉड्यूल के बीच डेटा भेजता है।
| घटक | जिम्मेदारी | निर्भरताएँ |
|---|---|---|
| View | प्रदर्शन, एनिमेशन, जेस्चर | UIKit (केवल View) |
| Interactor | व्यावसायिक तर्क, नेटवर्क, DB | Entity, सेवाएँ |
| Presenter | डेटा फ़ॉर्मेटिंग, View कमांड | ViewProtocol, Interactor |
| Entity | डेटा मॉडल | कोई नहीं |
| Router | नेविगेशन, मॉड्यूल निर्माण | UIViewController (संक्रमण के लिए) |
घटकों के बीच संबंध प्रोटोकॉल द्वारा वर्णित हैं। ViewProtocol प्रदर्शन विधियों को परिभाषित करता है, InteractorProtocol व्यावसायिक तर्क विधियों को, PresenterProtocol घटना प्रबंधन विधियों को, RouterProtocol नेविगेशन विधियों को परिभाषित करता है। प्रत्येक घटक केवल प्रोटोकॉल के माध्यम से दूसरे से संचार करता है, जो कार्यान्वयन को आसानी से बदलने और पृथक परीक्षण की अनुमति देता है। औसतन, एक स्क्रीन के लिए VIPER मॉड्यूल में 5 प्रोटोकॉल + 5 क्लास + 1 Builder/Assembler = 11 फ़ाइलें प्रति स्क्रीन होती हैं।
VIPER मॉड्यूल का निर्माण Builder (या Assembler) में किया जाता है, जो सभी पाँच घटकों को बनाता है और उन्हें प्रोटोकॉल के माध्यम से जोड़ता है। Builder एकमात्र स्थान है जहाँ घटक एक-दूसरे के ठोस प्रकारों को जानते हैं। असेंबली के बाद, View प्रदर्शन के लिए बाहर लौटाया जाता है, शेष श्रृंखला साफ है और पृथक रूप से परीक्षण योग्य है।
// प्रोटोकॉल — View
protocol UserViewProtocol: AnyObject {
func display(name: String)
func display(email: String)
func showLoading()
func hideLoading()
}
// प्रोटोकॉल — Interactor
protocol UserInteractorProtocol {
func fetchUser(id: Int, completion: @escaping (Result<User, Error>) -> Void)
}
// प्रोटोकॉल — Router
protocol UserRouterProtocol {
func navigateToProfile(userId: Int)
}
// Interactor — व्यावसायिक तर्क
final class UserInteractor: UserInteractorProtocol {
private let service: UserService
init(service: UserService) { self.service = service }
func fetchUser(id: Int, completion: @escaping (Result<User, Error>) -> Void) {
service.fetchUser(id: id, completion: completion)
}
}
// Presenter — डेटा तैयारी
final class UserPresenter {
private weak var view: UserViewProtocol?
private let interactor: UserInteractorProtocol
private let router: UserRouterProtocol
init(interactor: UserInteractorProtocol, router: UserRouterProtocol) {
self.interactor = interactor
self.router = router
}
func setView(_ view: UserViewProtocol) {
self.view = view
}
func viewDidLoad() {
view?.showLoading()
interactor.fetchUser(id: 42) { [weak self] result in
guard let self else { return }
self.view?.hideLoading()
switch result {
case .success(let user):
self.view?.display(name: user.name)
self.view?.display(email: user.email)
case .failure(let error):
// त्रुटि प्रबंधन
}
}
}
}
// Router — नेविगेशन
final class UserRouter: UserRouterProtocol {
private weak var viewController: UIViewController?
func setViewController(_ vc: UIViewController) {
viewController = vc
}
func navigateToProfile(userId: Int) {
let profileModule = ProfileModuleBuilder.build(userId: userId)
viewController?.navigationController?.pushViewController(profileModule, animated: true)
}
}
// Builder — मॉड्यूल असेंबली
enum UserModuleBuilder {
static func build(userId: Int) -> UIViewController {
let service = UserService()
let interactor = UserInteractor(service: service)
let router = UserRouter()
let presenter = UserPresenter(interactor: interactor, router: router)
let viewController = UserViewController(presenter: presenter)
presenter.setView(viewController)
router.setViewController(viewController)
return viewController
}
}
Builder/Assembler VIPER का एक प्रमुख तत्व है, जो मैन्युअल रूप से डिपेंडेंसी इंजेक्शन लागू करता है। कंस्ट्रक्टर के माध्यम से डिपेंडेंसी इंजेक्शन (constructor injection) गारंटी देता है कि कोई घटक अपनी निर्भरताओं के बिना नहीं बनाया जा सकता। आधुनिक VIPER में, Builder Swinject (DI कंटेनर) का उपयोग कर सकता है, लेकिन मैन्युअल असेंबली परीक्षण के लिए अधिक पारदर्शी रहती है। IT Sectr में, हम जटिल तर्क वाले मॉड्यूल के लिए मैन्युअल असेंबली के साथ VIPER लागू करते हैं — यह नए डेवलपर्स के लिए कोड पढ़ना सरल बनाता है।
Mapper (Formatter) VIPER का एक वैकल्पिक छठा घटक है। Mapper Entity (DB/सर्वर मॉडल) को ViewModel (प्रदर्शन मॉडल) में बदलता है। Entity में फ़ील्ड id, first_name, last_name, email के साथ UserDTO होता है। ViewModel — name (first_name + last_name) और email के साथ UserDisplayItem। Mapper Presenter में निष्पादित होता है। यदि डेटा मैपिंग जटिल है (एकाधिक Entity → एक ViewModel), तो Mapper को परीक्षण के लिए एक अलग क्लास में निकाला जाता है।
VIPER मॉड्यूल पृथक हैं और एक-दूसरे के बारे में नहीं जानते। मॉड्यूल के बीच संचार Router के माध्यम से होता है। जब उपयोगकर्ता उपयोगकर्ता स्क्रीन पर "प्रोफ़ाइल" बटन पर क्लिक करता है, Presenter router.navigateToProfile(userId: 42) को कॉल करता है। Router ProfileModuleBuilder.build(userId: 42) के माध्यम से एक नया मॉड्यूल बनाता है और इसे navigationController.push के माध्यम से खोलता है। डेटा प्रवाह: मॉड्यूल A → Router A → मॉड्यूल B Builder → मॉड्यूल B बनाया और खोला जाता है।
डेटा वापस भेजना (उदाहरण के लिए, चयन स्क्रीन पर शहर चुनना → प्रोफ़ाइल संपादन स्क्रीन पर वापस लौटना) VIPER में डेलिगेट या क्लोज़र के माध्यम से कार्यान्वित किया जाता है। मॉड्यूल B didSelectCity(_ city: City) विधि के साथ ModuleBDelegate प्रोटोकॉल को परिभाषित करता है। मॉड्यूल A इस प्रोटोकॉल को लागू करता है। Router A डेलिगेट को मॉड्यूल B Builder को भेजता है। जब कोई शहर चुना जाता है, मॉड्यूल B delegate?.didSelectCity(city) को कॉल करता है। यह मानक iOS अभ्यास है, जो किसी भी UIKit डेवलपर से परिचित है।
| परिदृश्य | तंत्र | उदाहरण |
|---|---|---|
| आगे नेविगेशन | Router → Builder | navigateToProfile(userId:) |
| डेटा वापस भेजना | Delegate | didSelectCity(_:) |
| सिस्टम सूचना | NotificationCenter | UserDidLogout |
| Interactor से घटना | Presenter → View | WebSocket संदेश |
NotificationCenter का उपयोग सिस्टम ईवेंट (लॉगआउट, प्लान बदलना, पुश सूचनाएँ) के लिए किया जाता है जो एक साथ कई मॉड्यूल को प्रभावित करते हैं। Router या AppDelegate Notification की सदस्यता लेता है, आवश्यक मॉड्यूल बनाता है या स्थिति अपडेट करता है। VIPER NotificationCenter को प्रतिबंधित नहीं करता — महत्वपूर्ण यह है कि इसका उपयोग केवल 1-to-many ईवेंट के लिए किया जाए, जबकि 1-to-1 संचार के लिए डेलिगेट या क्लोज़र का उपयोग किया जाए।
VIPER बनाम MVVM — VIPER को प्रति स्क्रीन 2–3 गुना अधिक कोड की आवश्यकता होती है लेकिन पूर्ण घटक पृथक्करण प्रदान करता है। MVVM ViewModel + SwiftUI के साथ सरल और तेज़ है लेकिन 5+ डेवलपर्स की टीमों के लिए कम स्केलेबल है। VIPER सख्ती से परिभाषित करता है कि कौन किसके लिए जिम्मेदार है: Interactor — केवल व्यावसायिक तर्क, Presenter — फ़ॉर्मेटिंग, Router — नेविगेशन। MVVM में, ViewModel अक्सर बढ़ता है, नेविगेशन और व्यावसायिक तर्क को अपने हाथ में ले लेता है।
VIPER बनाम Clean Architecture — VIPER iOS UIKit के लिए अनुकूलित Clean Architecture का एक विशिष्ट मामला है। Interactor = Use Case, Entity = Domain Model, Presenter = Presentation, Router = Controller रॉबर्ट मार्टिन के शब्दों में। Clean Architecture Interactor और डेटा के बीच एक Gateway/Repository परत जोड़ता है, जो आमतौर पर VIPER में अलग नहीं की जाती। आधुनिक SwiftUI प्रोजेक्ट्स के लिए, अधिकांश टीमें Clean Architecture (The Composable Architecture) या MVVM चुनती हैं, VIPER को UIKit लिगेसी के लिए छोड़ देती हैं।
VIPER कब चुनें — 5+ डेवलपर्स की टीमें, 50+ स्क्रीन वाले UIKit प्रोजेक्ट्स, 80% से ऊपर परीक्षण आवश्यकताएँ, केवल iOS (VIPER बिना पुनर्लेखन के Android पर पोर्टेबल नहीं है)। VIPER एक पूर्वानुमानित संरचना प्रदान करता है: एक नया डेवलपर 15 मिनट में एक मॉड्यूल समझ लेता है। हालांकि, अधिक फ़ाइलों के कारण विकास की गति MVVM की तुलना में 20–30% कम है। IT Sectr में, हम 3+ लोगों की टीमों के साथ एंटरप्राइज़ UIKit प्रोजेक्ट्स के लिए VIPER का उपयोग करते हैं और नए SwiftUI प्रोजेक्ट्स के लिए Clean Architecture पसंद करते हैं।
VIPER परीक्षण के लिए डिज़ाइन किया गया है — प्रत्येक घटक प्रोटोकॉल के माध्यम से पृथक रूप से परीक्षण किया जाता है। Interactor का परीक्षण मॉक सेवाओं के साथ किया जाता है: यह जाँच की जाती है कि fetchUser सही ID के साथ कॉल किया गया और परिणाम Presenter को भेजा गया। Presenter का परीक्षण मॉक View और Interactor ऑब्जेक्ट के साथ किया जाता है। Router का परीक्षण मॉक नेविगेशन के साथ किया जाता है: यह जाँच की जाती है कि navigateToProfile सही userId के साथ कॉल किया गया और सही मॉड्यूल बनाया गया। View का परीक्षण UI टेस्ट (XCUITest) से किया जाता है।
import XCTest
final class UserPresenterTests: XCTestCase {
func testViewDidLoad_callsFetchUserAndUpdatesView() {
// Given
let view = MockUserView()
let interactor = MockUserInteractor()
let router = MockUserRouter()
let presenter = UserPresenter(interactor: interactor, router: router)
presenter.setView(view)
let expectedUser = User(id: 42, name: "John", email: "john@test.com")
interactor.result = .success(expectedUser)
// When
presenter.viewDidLoad()
// Then
XCTAssertEqual(interactor.capturedUserId, 42)
XCTAssertEqual(view.displayedName, "John")
XCTAssertTrue(view.didShowLoading)
}
}
मॉक ऑब्जेक्ट VIPER के लिए मैन्युअल रूप से बनाए जाते हैं (संग्रहीत कैप्चर गुणों वाली क्लास) या Cuckoo / Mockingbird जैसी लाइब्रेरी के माध्यम से। मैन्युअल मॉक क्लास सरल और स्पष्ट होते हैं, विशेष रूप से नए डेवलपर्स को प्रशिक्षित करने के लिए। प्रत्येक मॉक कैप्चर किए गए मान (capturedUserId, displayedName) और कॉल फ़्लैग (didShowLoading) संग्रहीत करता है। परीक्षण के अंत में, न केवल यह जाँच की जाती है कि विधि को कॉल किया गया था, बल्कि यह भी कि किन पैरामीटर के साथ — यह डेटा प्रवाह की शुद्धता में विश्वास दिलाता है।
कोड कवरेज IT Sectr VIPER प्रोजेक्ट्स में Interactor के लिए 85–95%, Presenter के लिए 90–95%, Router के लिए 70–80%, View के लिए 30–50% (UI टेस्ट के माध्यम से) तक पहुँचता है। View का परीक्षण स्नैपशॉट टेस्ट (SnapshotTesting, 1.5K स्टार) से किया जाता है — यह XCUITest से तेज़ है और अधिक मामलों को कवर करता है। VIPER प्रोजेक्ट का कुल कवरेज आमतौर पर 70–80% होता है, जो MVVM प्रोजेक्ट (50–65%) से अधिक है, लेकिन टेस्ट लिखने में अधिक समय लगता है (विकास समय का 30–40% बनाम MVVM में 20–25%)।
अक्सर पूछे जाने वाले प्रश्न
न्यूनतम 11 फ़ाइलें: 5 प्रोटोकॉल (ViewProtocol, InteractorProtocol, PresenterProtocol, RouterProtocol, Entity), 5 कार्यान्वयन (ViewController, Interactor, Presenter, Router, Entity) और Builder/Assembler। Mapper (Formatter) के साथ — 12–13। 50 स्क्रीन वाले प्रोजेक्ट के लिए, केवल VIPER मॉड्यूल की 550–650 फ़ाइलें होती हैं। MVVM को प्रति स्क्रीन 3 फ़ाइलों की आवश्यकता होती है (ViewModel, View, Model) — 50 स्क्रीन के लिए 150 फ़ाइलें।
हाँ, सैद्धांतिक रूप से VIPER Android पर पोर्टेबल है, लेकिन व्यवहार में इसका उपयोग नहीं किया जाता — Google Jetpack के साथ MVVM की सिफारिश करता है। VIPER iOS UIKit के लिए बनाया गया था, जहाँ ViewController अपने जीवनचक्र के कारण परीक्षण करना कठिन है। Android पर, Jetpack ViewModel VIPER पृथक्करण के बिना परीक्षण समस्या को हल करता है। Android पर VIPER का समकक्ष मॉड्यूल/फ़ीचर विभाजन के साथ Clean Architecture है।
VIPER Clean Architecture का iOS-विशिष्ट कार्यान्वयन है। Interactor Use Case से मेल खाता है, Entity — Domain Model, Presenter — Presentation परत। Clean Architecture Interactor और डेटा के बीच Repository/Gateway जोड़ता है, जो VIPER में आमतौर पर Interactor के अंदर कार्यान्वित किए जाते हैं। Clean Architecture Router को निर्धारित नहीं करता — नेविगेशन कार्यान्वयन पर छोड़ दिया जाता है।
नहीं — SwiftUI MVVM + Combine के लिए डिज़ाइन किया गया है। SwiftUI में VIPER अनावश्यक है: घोषणात्मक UI के साथ प्रति स्क्रीन पाँच घटक लाभ के बिना ओवरहेड है। SwiftUI के लिए, MVVM या TCA (The Composable Architecture) चुनें। VIPER UIKit लिगेसी और उन प्रोजेक्ट्स के लिए प्रासंगिक बना हुआ है जहाँ iOS 12 और उससे नीचे न्यूनतम संस्करण है।
Router के माध्यम से। मॉड्यूल A router.navigateToProfile(userId: id) कॉल करता है। Router A Builder के माध्यम से मॉड्यूल B बनाता है, userId भेजता है। वापसी संचार — डेलिगेट के माध्यम से: मॉड्यूल B ModuleBDelegate प्रोटोकॉल को परिभाषित करता है, मॉड्यूल A इसे लागू करता है और Router के माध्यम से भेजता है। सिस्टम ईवेंट (लॉगआउट) — NotificationCenter के माध्यम से।
सारांश
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।
यह भी पढ़ें