Arhitecturile tradiționale din lumea UIKit (cum ar fi MVC sau VIPER) nu se transpun direct în SwiftUI datorită naturii declarative a acestuia: Interfața utilizator este o funcție pură a stării sale (UI = f(State)). Încercarea de a manipula direct view-urile sau de a folosi controller-e imperative duce rapid la stări inconsistente și bug-uri vizuale greu de reprodus.
Arhitectura modernă recomandată de Apple pentru aplicații mari la scară enterprise este bazată pe Flux Unidirecțional de Date (Unidirectional Data Flow – UDF) combinat cu noul framework Observation și navigare decuplată prin NavigationStack.
import SwiftUI
import Observation
// 1. Rutele aplicației definite ca o enumerare tipizată și Hashable:
enum AppRoute: Hashable {
case detaliiArticol(id: Int, titlu: String)
case profilUtilizator(username: String)
case setariCont
}
// 2. Coordonatorul de Navigare (Router centralizat):
@Observable
@MainActor
final class AppCoordinator {
var navigationPath = NavigationPath()
func navigheazaLa(_ ruta: AppRoute) {
navigationPath.append(ruta)
}
func revinoLaRadacina() {
navigationPath.removeLast(navigationPath.count)
}
}
// 3. View-ul Rădăcină cu NavigationStack decuplat:
struct MainRootView: View {
@State private var coordinator = AppCoordinator()
var body: some View {
NavigationStack(path: $coordinator.navigationPath) {
HomeFeedView()
.navigationTitle("DexBlog Invata")
// Înregistrarea destinațiilor tipizate:
.navigationDestination(for: AppRoute.self) { ruta in
switch ruta {
case .detaliiArticol(let id, let titlu):
ArticleDetailView(articleId: id, titlu: titlu)
case .profilUtilizator(let user):
UserProfileView(username: user)
case .setariCont:
SettingsView()
}
}
}
// Injectăm coordonatorul în mediu pentru acces facil de la orice nivel:
.environment(coordinator)
}
}
Ierarhia Corectă a Wrapper-elor de Stare în SwiftUI
| Instrument | Cine deține starea? | Scenariu optim de utilizare |
|---|---|---|
@State |
View-ul curent (Value Type local). | Stare efemeră internă de UI: dacă un popover este deschis, conținutul unui formular în curs de completare, tab-ul activ. |
@Binding |
Un View părinte. | Permite unui sub-view copil să citească și să modifice starea deținută de părinte prin legătură bidirecțională (Two-Way Binding). |
@Bindable |
O clasă @Observable. |
Generează bindings ($model.camp) către proprietățile unui model de date într-un formular sau controale vizuale. |
@Environment |
Arborele de vizualizare (SwiftUI Environment). | Injectarea de dependențe globale partajate: stiluri de temă (Light/Dark), instanțe de router, baze de date (SwiftData ModelContext). |
Separarea pe Straturi (Clean Architecture)
- Presentation Layer: Conține structuri
Viewdeclarate cât mai compacte posibil, fără logici grele de transformare de date. - State / ViewModel Layer: Clase adnotate cu
@Observableși@MainActor, gestionând doar pregătirea datelor pentru afișare și orchestrarea acțiunilor utilizatorului. - Domain / Service Layer: Structuri sau actori puri (ex:
AuthenticationService,NetworkClient) independenți de orice framework grafic de UI.