Coordinator (کوآرڈینیٹر) — ایک آرکیٹیکچرل نیویگیشن پیٹرن ہے جو اسکرینوں کے درمیان منتقلی کی منطق کو ViewController سے علیحدہ کلاسز میں منتقل کرتا ہے۔ یہ پیٹرن 2015 میں Soroush Khanlou نے تجویز کیا تھا اور iOS کمیونٹی میں وسیع پیمانے پر اپنایا گیا۔ Coordinator ایپلیکیشن کے بہاؤ کو منظم کرتا ہے: ViewController بناتا اور دکھاتا ہے، اسکرینوں کے درمیان ڈیٹا منتقل کرتا ہے، اور بہاؤ کی تکمیل کو ہینڈل کرتا ہے۔ یہ پیٹرن کنٹرولر سے نیویگیشن نکال کر Massive View Controller کے مسئلے کو حل کرتا ہے۔ مزید معلومات کے لیے Coordinator پر اصل مضمون دیکھیں۔
اہم نکات
Coordinator — ایک پیٹرن ہے جو iOS ایپلیکیشن میں نیویگیشن کی ذمہ داری سنبھالتا ہے۔ معیاری UIKit میں، ViewController خود منتقلی کا انتظام کرتا ہے: present، push، show segue — تمام نیویگیشن طریقے UIViewController سے بلائے جاتے ہیں۔ Coordinator اس منطق کو نکالتا ہے: ViewController ایک واقعہ رپورٹ کرتا ہے (مثال، «صارف نے لاگ ان بٹن پر کلک کیا»)، Coordinator فیصلہ کرتا ہے کہ اگلی کون سی اسکرین دکھانی ہے۔ ViewController صرف UI منطق کے ساتھ رہتا ہے اور نیویگیشن کو کوآرڈینیٹر کو سونپتا ہے۔
پیٹرن کا ڈھانچہ — start() اور finish() طریقوں کے ساتھ CoordinatorProtocol۔ start() — بہاؤ کا آغاز: پہلا ViewController بنانا اور دکھانا۔ finish() — والدین کوآرڈینیٹر کو اطلاع کے ساتھ بہاؤ کی تکمیل۔ Router — UINavigationController (یا UISplitViewController) کے گرد ایک لفافہ، جو show، push، pop، dismiss طریقے فراہم کرتا ہے۔ کوآرڈینیٹر براہ راست UINavigationController کے ساتھ کام نہیں کرتا — صرف Router کے ذریعے۔ یہ نیویگیشن کی جانچ اور UI فریم ورک تبدیل کرنے کی اجازت دیتا ہے۔
| جزو | کردار | مثال |
|---|---|---|
| Coordinator | نیویگیشن بہاؤ کا انتظام | AuthCoordinator, ProfileCoordinator |
| Router | UINavigationController پر تجرید | push, present, pop, dismiss |
| ViewController | UI + Coordinator کو واقعات کی تفویض | LoginViewController.delegate |
مسائل جو Coordinator حل کرتا ہے — Massive View Controller (نیویگیشن کنٹرولر کے بڑھنے کی ایک عام وجہ ہے)۔ معیاری UIKit میں، ViewController میں prepareForSegue، نیویگیشن ڈیلیگیٹس، unwind segues کی ہینڈلنگ شامل ہے۔ Coordinator اسے ختم کرتا ہے۔ Storyboard میں Segues اسکرینوں کے درمیان جامد کنکشن ہیں، Coordinator شرائط کے ساتھ متحرک نیویگیشن فراہم کرتا ہے۔ نیویگیشن کی جانچ ممکن ہو جاتی ہے: Router کالوں کی ترتیب کی تصدیق کرکے Coordinator کو UI کے بغیر جانچا جا سکتا ہے۔
Swift میں بنیادی Coordinator — Router کے لیے ایک منسلک قسم اور start/finish طریقوں کے ساتھ ایک پروٹوکول۔ Router — UINavigationController کو تجریدی بنانے والا پروٹوکول۔ Router کا ٹھوس نفاذ UINavigationController کو لپیٹتا ہے اور اسے طریقے سونپتا ہے۔ Coordinator init میں Router قبول کرتا ہے اور نیویگیشن کے لیے استعمال کرتا ہے۔ ذیلی کوآرڈینیٹر زندگی کے دورانیے کے انتظام کے لیے childCoordinators صف میں محفوظ کیے جاتے ہیں۔
// Router — نیویگیشن تجرید
protocol RouterProtocol: AnyObject {
func push(_ viewController: UIViewController, animated: Bool)
func pop(animated: Bool)
func present(_ viewController: UIViewController, animated: Bool)
func dismiss(animated: Bool)
}
final class NavigationRouter: RouterProtocol {
private let navigationController: UINavigationController
init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
func push(_ vc: UIViewController, animated: Bool) {
navigationController.pushViewController(vc, animated: animated)
}
func pop(animated: Bool) {
navigationController.popViewController(animated: animated)
}
func present(_ vc: UIViewController, animated: Bool) {
navigationController.present(vc, animated: animated)
}
func dismiss(animated: Bool) {
navigationController.dismiss(animated: animated)
}
}
// Coordinator — بہاؤ کا انتظام
protocol CoordinatorProtocol: AnyObject {
var childCoordinators: [CoordinatorProtocol] { get set }
var router: RouterProtocol { get }
func start()
func finish()
}
class AuthCoordinator: CoordinatorProtocol {
var childCoordinators: [CoordinatorProtocol] = []
let router: RouterProtocol
init(router: RouterProtocol) {
self.router = router
}
func start() {
let loginVC = LoginViewController()
loginVC.onLogin = { [weak self] in
self?.showHome()
}
router.push(loginVC, animated: true)
}
private func showHome() {
let homeCoordinator = HomeCoordinator(router: router)
childCoordinators.append(homeCoordinator)
homeCoordinator.start()
}
func finish() {
childCoordinators.removeAll()
router.pop(animated: true)
}
}
AppDelegate/SceneDelegate میں Coordinator بنانا — AppDelegate یا SceneDelegate UINavigationController بناتا ہے، اسے NavigationRouter میں لپیٹتا ہے، ایک جڑ Coordinator (AppCoordinator) بناتا ہے اور start() کال کرتا ہے۔ AppCoordinator ایپلیکیشن کی حالت کے لحاظ سے onboarding، لاگ ان یا مرکزی اسکرین دکھانے کا فیصلہ کرتا ہے۔ Coordinator نیویگیشن کے لیے واحد داخلہ نقطہ ہے، ViewController دوسری اسکرینوں کے بارے میں نہیں جانتا۔
کوآرڈینیٹر کا درجہ بندی — AppCoordinator → AuthCoordinator/MainCoordinator → ProfileCoordinator/SettingsCoordinator۔ ذیلی کوآرڈینیٹر والدین کے ذریعے بنایا جاتا ہے اور childCoordinators صف میں محفوظ کیا جاتا ہے۔ جب ذیلی کوآرڈینیٹر اپنا کام مکمل کرتا ہے، تو یہ والدین پر finish() کال کرتا ہے، اور والدین اسے childCoordinators سے ہٹا دیتا ہے۔ یہ میموری لیک کو روکتا ہے: Coordinator کے پاس ViewController پر ایک مضبوط حوالہ ہوتا ہے (Router کے ذریعے)، اور childCoordinators سے ہٹائے بغیر آبجیکٹ آزاد نہیں ہوگا۔
// Coordinator -> Parent مواصلت کے لیے Delegate
protocol AuthCoordinatorDelegate: AnyObject {
func authCoordinatorFinished(_ coordinator: AuthCoordinator)
}
class AuthCoordinator: CoordinatorProtocol {
weak var delegate: AuthCoordinatorDelegate?
func finish() {
delegate?.authCoordinatorFinished(self)
}
}
// AppCoordinator — والدین
class AppCoordinator: AuthCoordinatorDelegate {
func startAuthFlow() {
let authCoordinator = AuthCoordinator(router: router)
authCoordinator.delegate = self
childCoordinators.append(authCoordinator)
authCoordinator.start()
}
func authCoordinatorFinished(_ coordinator: AuthCoordinator) {
childCoordinators.removeAll { $0 is AuthCoordinator }
startMainFlow()
}
}
childCoordinators کا انتظام — Coordinator کو صف سے ہٹانا اسے آزاد کرنے کا واحد طریقہ ہے۔ اگر آپ مکمل شدہ Coordinator کو ہٹانا بھول جاتے ہیں، تو یہ اپنے ViewControllers سمیت میموری میں رہتا ہے۔ تجویز کردہ طریقے: والدین کا didMove(toParent:)، تکمیل پر callback، یا خودکار ہٹانے کے لیے Combine publisher۔ Coordinator پیٹرن اطلاع کے طریقہ کار کی وضاحت نہیں کرتا — delegate، closure یا Combine — انتخاب ڈویلپر پر منحصر ہے۔
delegate کے ذریعے ڈیٹا کی منتقلی — ذیلی Coordinator ایک delegate پروٹوکول کی وضاحت کرتا ہے جس کے طریقوں کے ذریعے نتائج منتقل کیے جاتے ہیں: func authCoordinator(_:didLoginWith user: User)۔ والدین پروٹوکول کو نافذ کرتا ہے اور ذیلی بہاؤ کی تکمیل پر ڈیٹا وصول کرتا ہے۔ یہ قسم سے محفوظ اور واضح ہے۔ نقصان: ہر ذیلی Coordinator کے لیے علیحدہ پروٹوکول درکار ہے۔ 10+ Coordinator والے منصوبوں کے لیے یہ فائلوں میں اضافے کا باعث بنتا ہے۔
Result قسم کے ذریعے ڈیٹا کی منتقلی — finish طریقہ Result
| طریقہ | فوائد | نقصانات |
|---|---|---|
| Delegate | قسم سے محفوظ، واضح، علیحدہ پروٹوکول | بہت سے پروٹوکول، بہت زیادہ بائلرپلیٹ |
| Closure | مختصر، کم فائلیں | retain cycle کو ڈیبگ کرنا مشکل |
| Combine/Rx | رد عمل، آسانی سے ملایا جا سکتا ہے | لائبریری پر انحصار، ڈیبگ کرنا مشکل |
مشترکہ ڈیٹا پرت — Coordinator براہ راست ڈیٹا منتقل نہیں کرتے بلکہ مشترکہ سروس/ریپوزٹری استعمال کرتے ہیں۔ AuthCoordinator ٹوکن کو Keychain/UserDefaults میں محفوظ کرتا ہے، ProfileCoordinator وہاں سے پڑھتا ہے۔ Coordinator براہ راست کالوں کے بجائے مشترکہ حالت (Dependency Injection کنٹینر) کے ذریعے بات چیت کرتے ہیں۔ یہ Coordinator کے درمیان وابستگی کو کم کرتا ہے لیکن مشترکہ حالت پر پوشیدہ انحصار پیدا کرتا ہے۔
Coordinator بمقابلہ Router — Router Coordinator کا ایک جزو ہے جو UINavigationController کو تجریدی بناتا ہے۔ Coordinator بہاؤ (کون سی اسکرین دکھانی ہے) کے لیے ذمہ دار ہے، Router — میکانکس (کیسے دکھانا ہے: push/present) کے لیے۔ Router «کیسے» ہے، Coordinator «کیا» ہے۔ آپ Router کو Coordinator کے بغیر استعمال کر سکتے ہیں (مثال، Navigator سنگلٹن)، لیکن Coordinator بغیر Router کے صرف ایک مختلف تجرید کے ساتھ ViewController ہے۔ عام طور پر دونوں پیٹرن ایک ساتھ استعمال ہوتے ہیں۔
Coordinator بمقابلہ VIPER — VIPER میں نیویگیشن کے لیے ذمہ دار Wireframe جزو ہے — Coordinator کے مشابہ۔ VIPER میں، Wireframe ماڈیول کا حصہ ہے، Coordinator ماڈیولز کے اوپر ایک علیحدہ پرت ہے۔ VIPER ماڈیول (View-Interactor-Presenter-Entity-Router) نیویگیشن کو ماڈیول کے حصے کے طور پر شامل کرتا ہے۔ Coordinator ماڈیولز کے لیے خارجی ہے: یہ ماڈیولز بناتا اور جوڑتا ہے، لیکن ان کا حصہ نہیں ہے۔ Coordinator مختلف بہاؤ میں اسکرینوں کے دوبارہ استعمال کے لیے زیادہ لچکدار ہے۔
MVVM-C — Coordinator کے ساتھ MVVM کی توسیع۔ ViewModel براہ راست Coordinator کے بارے میں نہیں جانتا — ViewController ViewModel کے ذریعے نیویگیشن سونپتا ہے، ViewModel ایک پروٹوکول کے ذریعے coordinator کو کال کرتا ہے۔ MVVM-C SwiftUI کے ساتھ iOS منصوبوں کے لیے معیاری نقطہ نظر ہے: Coordinator NavigationStack یا fullScreenCover کا انتظام کرتا ہے، ViewModel حالت شائع کرکے coordinator کو کال کرتا ہے۔ Apple SwiftUI کے لیے Coordinator کی سفارش نہیں کرتا — NavigationStack اور NavigationPath بلٹ ان نیویگیشن میکانزم ہیں۔
// MVVM-C: ViewModel پروٹوکول کے ذریعے Coordinator کو کال کرتا ہے
protocol AuthNavigationProtocol: AnyObject {
func showMainScreen()
func showForgotPassword()
}
class AuthViewModel: ObservableObject {
weak var navigation: AuthNavigationProtocol?
func loginTapped() {
// منطق...
navigation?.showMainScreen()
}
}
اکثر پوچھے جانے والے سوالات
SwiftUI کے لیے، بلٹ ان نیویگیشن (NavigationStack, NavigationPath) اکثر Coordinator کی جگہ لے لیتی ہے۔ Apple path-based نیویگیشن کی سفارش کرتا ہے۔ Coordinator پیچیدہ بہاؤ کے لیے معنی رکھتا ہے جس میں گہری شرائط ہوں (کردار کے لحاظ سے onboarding-لاگ ان-مرکزی اسکرین)۔ سادہ SwiftUI ایپلیکیشنز کے لیے Coordinator ضرورت سے زیادہ ہے — NavigationPath استعمال کریں۔
نہیں، یہ مختلف پیٹرن ہیں۔ Coordinator نیویگیشن بہاؤ کا انتظام کرتا ہے: فیصلہ کرتا ہے کہ کون سی اسکرین دکھانی ہے، ViewControllers بناتا ہے اور انہیں جوڑتا ہے۔ Router UINavigationController پر ایک تجرید ہے: push, present, pop, dismiss۔ Coordinator نیویگیشن انجام دینے کے لیے Router استعمال کرتا ہے۔ کچھ نفاذات میں Router Coordinator منطق شامل کرتا ہے (Router-per-screen)، لیکن یہ اصل پیٹرن سے انحراف ہے۔
دو اہم لیک ذرائع: childCoordinators (والدین ذیلی کو رکھتا ہے، ہٹانا بھول جاتا ہے) اور Router (UINavigationController ViewController کو رکھتا ہے)۔ حل: finish() پر ذیلی Coordinator کو ہمیشہ صف سے ہٹائیں۔ delegate کے لیے weak حوالہ استعمال کریں۔ Router کے لیے — UINavigationController پر مضبوط حوالہ نہ رکھیں اگر یہ پہلے سے window کے درجہ بندی میں ہے۔ Coordinator کے deinit کو جانچیں۔
3-5 اسکرینوں والی ایپلیکیشنز کے لیے Coordinator ضرورت سے زیادہ ہے — segue یا سادہ navigationController.pushViewController آسان ہے۔ NavigationStack کے ساتھ SwiftUI ایپلیکیشنز کے لیے بھی ضرورت سے زیادہ ہے۔ Coordinator 15+ اسکرینوں، پیچیدہ بہاؤ (شاخوں کے ساتھ onboarding، پاس ورڈ کی بازیابی کے ساتھ تصدیق) اور مخلوط UIKit/SwiftUI منصوبوں کے لیے جائز ہے۔
Mock Router — چیک کریں کہ کون سے طریقے اور کن پیرامیٹرز کے ساتھ بلائے گئے۔ childCoordinators چیک کریں: start() کے بعد صف خالی نہیں ہے، finish() کے بعد — خالی۔ Coordinator UI کے بغیر جانچا جاتا ہے: Router ایک پروٹوکول ہے، اس کے mock کو UIKit کی ضرورت نہیں ہے۔ async بہاؤ کے لیے XCTestExpectation استعمال کریں۔ Android میں — mock نیویگیشن کے ساتھ NavigationController اور NavHost کی اسی طرح جانچ۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں