Coordinator (কোঅর্ডিনেটর) — একটি আর্কিটেকচারাল নেভিগেশন প্যাটার্ন যা স্ক্রিনের মধ্যে ট্রানজিশনের লজিককে ViewController থেকে আলাদা ক্লাসে স্থানান্তরিত করে। প্যাটার্নটি 2015 সালে Soroush Khanlou দ্বারা প্রস্তাবিত হয় এবং iOS কমিউনিটিতে ব্যাপকভাবে গৃহীত হয়। Coordinator অ্যাপ্লিকেশনের flow পরিচালনা করে: ViewCreator তৈরি এবং প্রদর্শন করে, স্ক্রিনের মধ্যে ডেটা স্থানান্তর করে এবং flow সমাপ্তি পরিচালনা করে। প্যাটার্নটি কন্ট্রোলার থেকে নেভিগেশন বের করে Massive View Controller সমস্যার সমাধান করে। আরও জানতে Coordinator সম্পর্কে মূল নিবন্ধ দেখুন।
মূল বিষয়
Coordinator — একটি প্যাটার্ন যা iOS অ্যাপ্লিকেশনে নেভিগেশনের দায়িত্ব গ্রহণ করে। স্ট্যান্ডার্ড UIKit-এ, ViewController নিজেই ট্রানজিশন পরিচালনা করে: present, push, show segue — সমস্ত নেভিগেশন পদ্ধতি UIViewController থেকে কল করা হয়। Coordinator এই লজিক বের করে: ViewController একটি ইভেন্ট রিপোর্ট করে (যেমন, «ব্যবহারকারী লগইন বাটনে ক্লিক করেছেন»), Coordinator সিদ্ধান্ত নেয় পরবর্তী কোন স্ক্রিন দেখাতে হবে। ViewController শুধুমাত্র UI লজিক নিয়ে থাকে এবং নেভিগেশন কোঅর্ডিনেটরের কাছে অর্পণ করে।
প্যাটার্ন গঠন — start() এবং finish() পদ্ধতি সহ CoordinatorProtocol। start() — flow-এর শুরু: প্রথম ViewController তৈরি এবং প্রদর্শন। finish() — প্যারেন্ট কোঅর্ডিনেটরকে বিজ্ঞপ্তি সহ flow সমাপ্তি। Router — UINavigationController (বা UISplitViewController)-এর উপরে একটি মোড়ক, যা show, push, pop, dismiss পদ্ধতি প্রদান করে। কোঅর্ডিনেটর সরাসরি UINavigationController-এর সাথে কাজ করে না — শুধুমাত্র Router-এর মাধ্যমে। এটি নেভিগেশন পরীক্ষা এবং UI ফ্রেমওয়ার্ক পরিবর্তনের অনুমতি দেয়।
| উপাদান | ভূমিকা | উদাহরণ |
|---|---|---|
| Coordinator | নেভিগেশন flow ব্যবস্থাপনা | AuthCoordinator, ProfileCoordinator |
| Router | UINavigationController-এর উপর বিমূর্ততা | push, present, pop, dismiss |
| ViewController | UI + Coordinator-এ ইভেন্ট প্রত্যায়োজন | LoginViewController.delegate |
Coordinator যে সমস্যাগুলি সমাধান করে — Massive View Controller (নেভিগেশন কন্ট্রোলার বৃদ্ধির একটি সাধারণ কারণ)। স্ট্যান্ডার্ড UIKit-এ, ViewController-এ prepareForSegue, নেভিগেশন delegates, unwind segues হ্যান্ডলিং থাকে। Coordinator এটি দূর করে। Storyboard-এ Segues স্ক্রিনের মধ্যে স্থির সংযোগ, Coordinator শর্তসহ গতিশীল নেভিগেশন প্রদান করে। নেভিগেশন পরীক্ষা সম্ভব হয়: Router কলের ক্রম যাচাই করে UI ছাড়াই Coordinator পরীক্ষা করা যায়।
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 — flow ব্যবস্থাপনা
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 অ্যাপ্লিকেশনের অবস্থার উপর নির্ভর করে অনবোর্ডিং, লগইন বা প্রধান স্ক্রিন দেখানোর সিদ্ধান্ত নেয়। 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)। প্যারেন্ট প্রোটোকল বাস্তবায়ন করে এবং চাইল্ড flow সম্পন্ন হলে ডেটা গ্রহণ করে। এটি টাইপ-নিরাপদ এবং সুস্পষ্ট। অসুবিধা: প্রতিটি চাইল্ড 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 flow-এর জন্য দায়ী (কোন স্ক্রিন দেখাতে হবে), 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 বিভিন্ন flow-এ স্ক্রিন পুনরায় ব্যবহারের জন্য আরও নমনীয়।
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 জটিল flow-এর জন্য উপযুক্ত যেখানে গভীর শর্ত থাকে (ভূমিকার উপর নির্ভর করে অনবোর্ডিং-লগইন-প্রধান স্ক্রিন)। সাধারণ SwiftUI অ্যাপ্লিকেশনের জন্য Coordinator অপ্রয়োজনীয় — NavigationPath ব্যবহার করুন।
না, এগুলি ভিন্ন প্যাটার্ন। Coordinator নেভিগেশন flow পরিচালনা করে: সিদ্ধান্ত নেয় কোন স্ক্রিন দেখাতে হবে, 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+ স্ক্রিন, জটিল flow (শাখাসহ অনবোর্ডিং, পাসওয়ার্ড পুনরুদ্ধারসহ প্রমাণীকরণ) এবং মিশ্র UIKit/SwiftUI প্রকল্পের জন্য ন্যায়সঙ্গত।
Mock Router — পরীক্ষা করুন কোন পদ্ধতি এবং কী প্যারামিটার দিয়ে কল করা হয়েছে। childCoordinators পরীক্ষা করুন: start()-এর পরে অ্যারে খালি নয়, finish()-এর পরে — খালি। Coordinator UI ছাড়াই পরীক্ষা করা হয়: Router একটি প্রোটোকল, এর mock-এর UIKit প্রয়োজন নেই। async flow-এর জন্য XCTestExpectation ব্যবহার করুন। Android-এ — mock নেভিগেশন সহ NavigationController এবং NavHost-এর অনুরূপ পরীক্ষা।
সারসংক্ষেপ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন