I implemented a lesson coordinator Why You Should Use The SwiftUI Coordinator Pattern
I googled the question about loading a view several times. I still haven’t found how to fix it.
I realized that the problem is NavigationStack -> navigationDestination.
The main problem is that the view assembly runs several times. this is both the work of a worker and a request for data from the network...
struct SearchCoordinatorView: View {
@StateObject private var coordinator = SearchCoordinator()
@EnvironmentObject var globalFavoritesModel: GlobalFavoritesModel
var body: some View {
NavigationStack(path: self.$coordinator.path) {
self.coordinator.changeFlow(flow: coordinator.flow, globalFavoritesModel: globalFavoritesModel)
.navigationDestination(for: Flow.self) { page in
self.coordinator.changeFlow(flow: page, globalFavoritesModel: globalFavoritesModel)
.navigationBarTitle("", displayMode: .inline)
.navigationBarBackButtonHidden(true)
}
}
}
}
protocol IShowJobPageSceneDelegate: AnyObject {
func showJobPageScene(id: String)
}
protocol IShowPasswordSceneDelegate: AnyObject {
func showPasswordScene(eMail: String)
}
protocol IShowSearchSceneDelegate: AnyObject {
func showSearchScene()
}
protocol IBackSceneDelegate: AnyObject {
func backScene()
}
enum Flow: Hashable {
case loginScene
case passwordScene(String)
case vacancyScene
case jobScene(String)
}
final class SearchCoordinator: ObservableObject {
@Published var flow: Flow = .loginScene
@Published var path: NavigationPath = NavigationPath()
func push(_ page: Flow) {
path.append(page)
}
func pop() {
path.removeLast()
}
func popToRoot() {
path.removeLast(path.count)
}
@ViewBuilder
func changeFlow(flow: Flow, globalFavoritesModel: GlobalFavoritesModel) -> some View {
switch (flow, globalFavoritesModel.isAuthorized) {
case (.loginScene, false):
let assembler = AssemblerLoginView()
assembler.configurator(delegate: self)
case (.passwordScene(let email), _):
let assembler = AssemblerPasswordView()
assembler.configurator(delegate: self, modelDTO: email)
case (.vacancyScene, _):
let assembler = AssemblerMainSearch()
assembler.configurator(delegate: self, globalModel: globalFavoritesModel)
case (.jobScene(let vacancyID), _):
let assembler = AssemblerJobPage()
assembler.configurator(backSceneDelegate: self, modelDTO: vacancyID)
case (_, true):
let assembler = AssemblerMainSearch()
assembler.configurator(delegate: self, globalModel: globalFavoritesModel)
}
}
}
extension SearchCoordinator: IShowJobPageSceneDelegate {
func showJobPageScene(id: String) {
self.push(.jobScene(id))
}
}
extension SearchCoordinator: IBackSceneDelegate {
func backScene() {
self.pop()
}
}
extension SearchCoordinator: IShowPasswordSceneDelegate {
func showPasswordScene(eMail: String) {
self.push(.passwordScene(eMail))
}
}
extension SearchCoordinator: IShowSearchSceneDelegate {
func showSearchScene() {
push(.vacancyScene)
}
}
import SwiftUI
final class AssemblerMainSearch {
func configurator(
delegate: IShowJobPageSceneDelegate, globalModel: GlobalFavoritesModel
) -> some View {
let fileManagers = FileLoadManager()
// Сервисы.
let declinationNumberService = DeclinationNumberService()
let dateConvertService = DateConvertService()
let convertorCounterVacancies = ConvertCounterVacanciesService()
let assemblerURL = AssemblerURLService()
// Менеджер работы с сетью.
let networkManager = NetworkManager()
// Менеджер работы с JSON.
let jsonManager = DecodeJsonManager()
// Конвекторы.
let convertorVacancyDTO = ConvertorVacanciesDTO(
declinationNumberService: declinationNumberService,
dateConvertService: dateConvertService)
let convertorOfferDTO = ConvertorOffersDTO()
// Сборщик URL.
let worker = MainSearchWorker(
assemblerURL: assemblerURL,
decodeJSONManager: jsonManager,
networkManager: networkManager,
convertorVacancyDTO: convertorVacancyDTO,
convertorOffersDTO: convertorOfferDTO,
convertorCounterVacancies: convertorCounterVacancies,
fileManager: fileManagers
)
// Сборщик VIP.
let viewModel = MainSearchViewModel(showJobPageSceneDelegate: delegate, globalModel: globalModel)
let iterator = MainSearchIterator(viewModel: viewModel, worker: worker)
iterator.fitchVacancies()
var viewController = MainSearchView(mainSearchViewModel: viewModel)
viewController.iterator = iterator
return viewController
}
}