Coordinator (koordinator) — navigatsiyaning arxitektura namunasi bo'lib, ekranlar o'rtasidagi o'tish mantiqini ViewController-dan alohida sinflarga chiqaradi. Namuna 2015-yilda Soroush Khanlou tomonidan taklif qilingan va iOS hamjamiyatida keng tarqalgan. Coordinator ilovaning flow-ni boshqaradi: ViewController yaratadi va ko'rsatadi, ekranlar o'rtasida ma'lumot uzatadi va flow tugashini qayta ishlaydi. Namuna Massive View Controller muammosini hal qiladi, navigatsiyani kontrollerdan chiqaradi. Batafsil — Coordinator asl maqolasida.
Asosiy fikrlar
Coordinator — iOS ilovasida navigatsiya uchun mas'uliyatni o'z zimmasiga oladigan namuna. Standart UIKit-da ViewController o'tishlarni o'zi boshqaradi: present, push, show segue — barcha navigatsiya metodlari UIViewController-dan chaqiriladi. Coordinator bu mantiqni chiqaradi: ViewController hodisa haqida xabar beradi (masalan, „foydalanuvchi kirish tugmasini bosdi“), Coordinator keyingi qaysi ekranni ko'rsatishni hal qiladi. ViewController faqat UI mantiqi bilan qoladi va navigatsiyani koordinatorga topshiradi.
Namuna tuzilishi — start() va finish() metodlari bilan CoordinatorProtocol. start() — flow boshlanishi: birinchi ViewController yaratish va ko'rsatish. finish() — ota-ona koordinatoriga xabar berish bilan flow tugashi. Router — UINavigationController (yoki UISplitViewController) ustidagi qobiq, show, push, pop, dismiss metodlarini taqdim etadi. Koordinator to'g'ridan-to'g'ri UINavigationController bilan ishlamaydi — faqat Router orqali. Bu navigatsiyani test qilish va UI freymvorkini almashtirish imkonini beradi.
| Komponent | Rol | Misol |
|---|---|---|
| Coordinator | Navigatsiya flow-ni boshqarish | AuthCoordinator, ProfileCoordinator |
| Router | UINavigationController ustidagi abstraksiya | push, present, pop, dismiss |
| ViewController | UI + hodisalarni Coordinator-ga delegatsiya qilish | LoginViewController.delegate |
Coordinator hal qiladigan muammolar — Massive View Controller (navigatsiya — kontrollerning kengayishining keng tarqalgan sababi). Standart UIKit-da ViewController prepareForSegue, navigatsiya delegate-lari, unwind segues qayta ishlashni o'z ichiga oladi. Coordinator buni bartaraf qiladi. Storyboard-dagi Segue — ekranlar o'rtasida statik bog'lanish, Coordinator shartlar bilan dinamik navigatsiyani ta'minlaydi. Navigatsiyani test qilish mumkin bo'ladi: Coordinator-ni UI-siz, Router chaqiruvlari ketma-ketligini tekshirib test qilish mumkin.
Swift-da asosiy Coordinator — Router uchun bog'langan tur va start/finish metodlari bilan protokol. Router — UINavigationController-ni abstraksiya qiluvchi protokol. Router-ning aniq amalga oshirilishi UINavigationController-ni o'rab oladi va metodlarni unga topshiradi. Coordinator Router-ni init-da qabul qiladi va navigatsiya uchun foydalanadi. Bola koordinatorlari hayot aylanishini boshqarish uchun childCoordinators massivida saqlanadi.
// Router — navigatsiya abstraksiyasi
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 boshqaruvi
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-da Coordinator yaratish — AppDelegate yoki SceneDelegate UINavigationController yaratadi, uni NavigationRouter-ga o'raydi, ildiz Coordinator (AppCoordinator) yaratadi va start() chaqiradi. AppCoordinator ilova holatiga qarab onboarding, kirish yoki asosiy ekranni ko'rsatishni hal qiladi. Coordinator — navigatsiya uchun yagona kirish nuqtasi, ViewController boshqa ekranlar haqida bilmaydi.
Koordinatorlar iyerarxiyasi — AppCoordinator → AuthCoordinator/MainCoordinator → ProfileCoordinator/SettingsCoordinator. Bola koordinatori ota-ona tomonidan yaratiladi va childCoordinators massivida saqlanadi. Bola koordinatori ishini tugatganda, ota-onada finish() chaqiradi va ota-ona uni childCoordinators-dan olib tashlaydi. Bu xotira oqishining oldini oladi: Coordinator ViewController-ga (Router orqali) kuchli havolaga ega va childCoordinators-dan olib tashlanmasa, obyekt bo'shatilmaydi.
// Coordinator -> Parent aloqasi uchun Delegate
protocol AuthCoordinatorDelegate: AnyObject {
func authCoordinatorFinished(_ coordinator: AuthCoordinator)
}
class AuthCoordinator: CoordinatorProtocol {
weak var delegate: AuthCoordinatorDelegate?
func finish() {
delegate?.authCoordinatorFinished(self)
}
}
// AppCoordinator — ota-ona
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-ni boshqarish — Coordinator-ni massivdan olib tashlash uni bo'shatishning yagona usuli. Tugallangan Coordinator-ni olib tashlashni unutib qo'ysangiz, u ViewController-lar bilan birga xotirada qoladi. Tavsiya etiladi: ota-onaning didMove(toParent:), tugatish callback-i yoki avtomatik olib tashlash uchun Combine publisher. Coordinator namunasi xabardor qilish mexanizmini spetsifikatsiya qilmaydi — delegate, closure yoki Combine — tanlov dasturchiga qoldiriladi.
Delegate orqali ma'lumot uzatish — bola Coordinator natijalar uzatiladigan metodlar bilan delegate protokolini belgilaydi: func authCoordinator(_:didLoginWith user: User). Ota-ona protokolni amalga oshiradi va bola flow tugashida ma'lumot oladi. Bu tur xavfsiz va ochiq-oydin. Kamchilik: har bir bola Coordinator uchun alohida protokol yozish kerak. 10+ Coordinator bo'lgan loyihalar uchun bu fayllar sonining ko'payishiga olib keladi.
Result turi orqali ma'lumot uzatish — finish metodi Result
| Usul | Afzalliklar | Kamchiliklar |
|---|---|---|
| Delegate | Tur xavfsiz, ochiq-oydin, alohida protokollar | Ko'p protokollar, ko'p boilerplate |
| Closure | Kompakt, kam fayl | Retain cycle-ni tuzatish qiyin |
| Combine/Rx | Reaktiv, oson birlashtirish | Kutubxonaga bog'liqlik, Debug qiyinroq |
Umumiy ma'lumot qatlami — Coordinatorlar ma'lumotni to'g'ridan-to'g'ri uzatmaydi, balki umumiy xizmat/repozitoriydan foydalanadi. AuthCoordinator token-ni Keychain/UserDefaults-da saqlaydi, ProfileCoordinator u yerdan o'qiydi. Coordinatorlar umumiy holat (Dependency Injection konteyneri) orqali muloqot qiladi, to'g'ridan-to'g'ri chaqiruvlar bilan emas. Bu Coordinatorlar bog'liqligini kamaytiradi, lekin umumiy holatdan yashirin bog'liqliklarni yaratadi.
Coordinator vs Router — Router UINavigationController-ni abstraksiya qiluvchi Coordinator komponenti. Coordinator flow uchun javobgar (qaysi ekranni ko'rsatish), Router — mexanika uchun (qanday ko'rsatish: push/present). Router „qanday“, Coordinator „nima“. Router-ni Coordinator-siz ishlatish mumkin (masalan, Navigator-singleton), lekin Coordinator Router-siz — shunchaki boshqa abstraksiya bilan ViewController. Odatda ikkala namuna birgalikda ishlatiladi.
Coordinator vs VIPER — VIPER navigatsiya uchun mas'ul bo'lgan Wireframe komponentiga ega — Coordinator analogi. VIPER-da Wireframe modulning bir qismi, Coordinator — modullar ustidagi alohida qatlam. VIPER moduli (View-Interactor-Presenter-Entity-Router) navigatsiyani modulning bir qismi sifatida o'z ichiga oladi. Coordinator modullarga nisbatan tashqi: modullarni yaratadi va bog'laydi, lekin ularning tarkibiga kirmaydi. Coordinator ekranlarni turli flow-larda qayta ishlatish uchun moslashuvchanroq.
MVVM-C — MVVM-ni Coordinator bilan kengaytirish. ViewModel to'g'ridan-to'g'ri Coordinator-ni bilmaydi — ViewController navigatsiyani ViewModel orqali topshiradi, ViewModel protokol orqali koordinatorni chaqiradi. MVVM-C — SwiftUI bilan iOS loyihalari uchun standart yondashuv: Coordinator NavigationStack yoki fullScreenCover-ni boshqaradi, ViewModel holatni nashr etish orqali koordinatorni chaqiradi. Apple SwiftUI uchun Coordinator-ni tavsiya etmaydi — NavigationStack va NavigationPath o'rnatilgan navigatsiya mexanizmlari.
// MVVM-C: ViewModel protokol orqali Coordinator-ni chaqiradi
protocol AuthNavigationProtocol: AnyObject {
func showMainScreen()
func showForgotPassword()
}
class AuthViewModel: ObservableObject {
weak var navigation: AuthNavigationProtocol?
func loginTapped() {
// mantiq...
navigation?.showMainScreen()
}
}
Ko'p beriladigan savollar
SwiftUI uchun o'rnatilgan navigatsiya (NavigationStack, NavigationPath) ko'pincha Coordinator-ni almashtiradi. Apple path-based navigatsiyani tavsiya qiladi. Coordinator chuqur shartlarga ega murakkab flow-lar uchun ma'noga ega (roldan kelib chiqib onboarding-kirish-asosiy ekran). Oddiy SwiftUI ilovalari uchun Coordinator ortiqcha — NavigationPath dan foydalaning.
Yo'q, bu turli namunalar. Coordinator navigatsiya flow-ni boshqaradi: qaysi ekranni ko'rsatishni hal qiladi, ViewController yaratadi va ularni bog'laydi. Router — UINavigationController ustidagi abstraksiya: push, present, pop, dismiss. Coordinator navigatsiyani bajarish uchun Router-dan foydalanadi. Ba'zi amalga oshirishlarda Router Coordinator mantiqini o'z ichiga oladi (Router-per-screen), lekin bu asl namunadan chetlanishdir.
Ikkita asosiy oqish joyi: childCoordinators (ota-ona bolani ushlab, olib tashlashni unutadi) va Router (UINavigationController ViewController-ni ushlab turadi). Yechim: har doim bola Coordinator-ni finish() vaqtida massivdan olib tashlang. Delegate uchun weak havoladan foydalaning. Router uchun — UINavigationController-ga kuchli havola saqlamang, agar u allaqachon window iyerarxiyasida bo'lsa. Coordinator deinit-ni test qiling.
3-5 ekranli ilovalar uchun Coordinator ortiqcha — segue yoki oddiy navigationController.pushViewController soddaroq. NavigationStack bilan SwiftUI ilovalari uchun ham ortiqcha. Coordinator 15+ ekranli, murakkab flow-li (tarmoqlangan onboarding, parolni tiklash bilan avtorizatsiya) va aralash UIKit/SwiftUI loyihalari uchun asoslangan.
Mock Router — qaysi metodlar qanday parametrlar bilan chaqirilganini tekshirish. childCoordinators tekshiruvi: start() dan keyin massiv bo'sh emas, finish() dan keyin — bo'sh. Coordinator UI-siz test qilinadi: Router protokol, uning mock-i UIKit talab qilmaydi. Async flow uchun XCTestExpectation dan foydalaning. Android-da analog — NavigationController va NavHost-ni mock-navigatsiya bilan test qilish.
Xulosa
Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz
IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.