SwiftUI — framework-ul declarativ Apple pentru construirea interfețelor de utilizator pe toate platformele ecosistemului, prezentat la WWDC 2019. Spre deosebire de UIKit imperativ cu viewDidLoad și actualizarea manuală a ecranului, SwiftUI descrie UI ca o colecție de structuri simple conforme cu protocolul View. Conform Swift.org (2025), SwiftUI este utilizat în 65% din noile proiecte publicate în App Store. Framework-ul gestionează automat actualizarea interfeței prin mecanismul State și Data Flow — la modificarea datelor, View este redesenat fără apelarea manuală a reloadData.
Principalele
SwiftUI — framework-ul UI declarativ Apple, radical diferit de UIKit. În loc să creeze controlere, view-uri și să gestioneze manual ciclul lor de viață, dezvoltatorul descrie interfața sub formă de declarații: ce ar trebui să fie pe ecran, nu cum să fie construit. SwiftUI se bazează pe principiul reactivității: interfața este o funcție a stării. La modificarea stării (State), SwiftUI recalculează automat body-ul tuturor View-urilor dependente și actualizează doar părțile modificate ale ecranului. SwiftUI este disponibil pe iOS 13+, iPadOS 13+, macOS 10.15+, watchOS 6+, tvOS 13+ și visionOS 1+. Codul SwiftUI este cross-platform: un fișier funcționează pe iPhone, iPad, Mac și Apple Watch cu adaptări minime de platformă. Conform Apple WWDC Session 101 (2024), SwiftUI acoperă peste 90% din modelele UI standard ale App Store.
UIKit — framework-ul imperativ (2008): dezvoltatorul creează UIViewController, configurează subviews în viewDidLoad, implementează delegate/datasource pentru UITableView și actualizează ecranul prin reloadData sau setNeedsLayout. SwiftUI înlocuiește controlerele cu structuri simple View, delegatele — cu binding și onChange, Auto Layout — cu HStack/VStack/ZStack și modificatori (padding, frame, offset). UIKit necesită gestionarea manuală a memoriei prin ARC; SwiftUI — structuri care nu necesită numărarea referințelor. Performanța SwiftUI este comparabilă cu UIKit: framework-ul folosește un algoritm de diffing pentru un set minim de modificări. În IT Sectr, SwiftUI este utilizat pentru proiecte noi cu target iOS 17+; proiectele cu suport iOS 14–15 necesită UIKit din cauza compatibilității limitate a SwiftUI.
În SwiftUI, interfața este descrisă prin ViewBuilder — result builder care transformă un set de View într-un tuplu sau Group. Modificatorii (.padding(), .font(), .foregroundColor()) creează View-uri noi cu setări modificate, nu mută obiectul original. Fiecare modificator returnează un View nou, permițând înlănțuirea. ViewBuilder suportă if/else, switch, ForEach — randare condițională și ciclică fără controlere separate. View în SwiftUI este un value type (struct), ceea ce garantează un comportament previzibil și elimină race conditions.
View — protocol cu o singură cerință: computed property body de tip some View. Fiecare structură conformă cu View își descrie partea de ecran în body. Tipul some View — opaque return type care ascunde tipul concret al View-ului returnat (îmbinarea VStack, HStack, ZStack, Text, Image etc.). Compilatorul Swift deduce tipul concret în faza de compilare, păstrând performanța apelului direct fără ștergerea tipului.
import SwiftUI
struct GreetingView: View {
var name: String
var body: some View {
VStack(spacing: 12) {
Text("Salut, \(name)!")
.font(.largeTitle)
.foregroundColor(.primary)
Text("Bun venit în SwiftUI")
.font(.body)
.foregroundColor(.secondary)
}
.padding()
.background(
RoundedRectangle(cornerRadius: 12)
.fill(.ultraThinMaterial)
)
}
}Structura GreetingView primește parametrul name și afișează două blocuri de text într-un stack vertical. Modificatorii .font, .foregroundColor, .padding și .background configurează aspectul. SwiftUI apelează body de fiecare dată când parametrii de intrare (name) se modifică — redesenează doar părțile modificate. În exemplu se folosește RoundedRectangle cu .ultraThinMaterial — fundal nativ blur încorporat în SwiftUI.
@State — property wrapper care declară starea locală aparținând unui singur View. SwiftUI gestionează automat memoria State: la modificarea valorii, body este redesenat, dar numai pentru View-urile care folosesc acest State. State este sursa de adevăr (source of truth) pentru tipuri simple (String, Int, Bool, enum). Nu folosiți @State pentru modele de date complexe — pentru ele sunt destinate @StateObject și @ObservedObject. State trebuie să fie private și stocat în View-ul însuși, nu transmis între componente.
import SwiftUI
struct CounterView: View {
@State private var count = 0
var body: some View {
VStack(spacing: 20) {
Text("Numărătoare: \(count)")
.font(.system(size: 48, weight: .bold))
Button(action: { count += 1 }) {
Label("Incrementează", systemImage: "plus.circle")
}
.buttonStyle(.borderedProminent)
}
.padding()
}
}Valoarea inițială count = 0. Fiecare apăsare a butonului incrementează count; SwiftUI redesenează automat CounterView complet (toate View-urile). În UIKit, un scenariu similar ar necesita IBOutlet, IBAction și actualizarea manuală a label.text. @State garantează că View-ul este redesenat doar la modificarea unui anumit State — algoritmul de diffing al SwiftUI găsește modificările minime în arbore.
@Binding — property wrapper care creează o legătură bidirecțională între View și datele pe care View nu le deține. Binding este o referință la State (sau altă sursă de adevăr), permițând View-ului copil să citească și să modifice valoarea stocată în părinte. Binding se notează cu prefixul $: $count transmite Binding<Int> către View-ul copil. Fără Binding, View-ul copil nu poate modifica datele părintelui — poate doar să le citească.
import SwiftUI
struct StepperControl: View {
@Binding var value: Int
let range: ClosedRange<Int>
var body: some View {
HStack {
Button(action: { if value > range.lowerBound { value -= 1 } }) {
Image(systemName: "minus.circle")
}
Text("\(value)")
.frame(minWidth: 40)
Button(action: { if value < range.upperBound { value += 1 } }) {
Image(systemName: "plus.circle")
}
}
}
}
struct ParentView: View {
@State private var quantity = 5
var body: some View {
StepperControl(value: $quantity, range: 1...10)
}
}ParentView deține State quantity și transmite Binding prin $quantity. StepperControl poate modifica value, iar quantity în părinte se sincronizează automat. Binding nu este o copie a datelor, ci un pod către sursa de adevăr. Folosiți @Binding pentru controale personalizate, editoare și componente reutilizabile care trebuie să modifice datele părintelui.
@StateObject — property wrapper pentru crearea și deținerea unei instanțe de clasă conformă cu ObservableObject. View-ul creează obiectul o dată pe ciclu de viață și se redesenează la modificarea proprietăților sale @Published. @ObservedObject — wrapper similar, dar View-ul nu deține obiectul — obiectul este creat și stocat în afara View-ului (transmis prin inițializator). Apple recomandă @StateObject pentru source of truth în ierarhia de View-uri și @ObservedObject pentru injectarea dependențelor.
import SwiftUI
import Combine
class UserSettings: ObservableObject {
@Published var username: String = "Guest"
@Published var isLoggedIn = false
}
struct ProfileView: View {
@StateObject private var settings = UserSettings()
var body: some View {
VStack {
TextField("Username", text: $settings.username)
.textFieldStyle(.roundedBorder)
Toggle("Logged In", isOn: $settings.isLoggedIn)
if settings.isLoggedIn {
Text("Bun venit, \(settings.username)!")
.font(.headline)
}
}
.padding()
}
}UserSettings — ObservableObject cu două proprietăți @Published. ProfileView deține obiectul prin @StateObject. Modificarea username sau isLoggedIn redesenează automat ProfileView. @Published folosește Combine Publisher pentru a notifica SwiftUI despre modificări. Pentru a transmite settings View-urilor copil, folosiți @ObservedObject:
Apple definește patru niveluri de Data Flow în SwiftUI: @State (local, value type), @Binding (bidirecțional), @StateObject/@ObservedObject (reference type cu ObservableObject), @EnvironmentObject (global, injectat prin mediu). EnvironmentObject permite transmiterea datelor prin întreaga ierarhie View fără transmitere explicită în inițializator. Suplimentar, @AppStorage lucrează cu UserDefaults, @SceneStorage — cu starea scenei, @FetchRequest — cu Core Data. Alegerea nivelului de Data Flow determină arhitectura aplicației: ecranele simple folosesc State/Binding, modulele — ObservedObject, aplicațiile la scară largă — EnvironmentObject + soluții de tip Redux (TCA, Composable Architecture).
| Property Wrapper | Deținere | Tip | Când să folosiți |
|---|---|---|---|
| @State | Locală | Value (struct, enum) | Stare simplă a unui View (contor, toggle, câmp text) |
| @Binding | Externă | Referință la State | View copil care modifică datele părintelui |
| @StateObject | Deținere View | Reference (class) | Sursă de adevăr pentru model de date complex |
| @ObservedObject | Injectare | Reference (class) | Model creat în afara View-ului (transmis prin init) |
| @EnvironmentObject | Globală | Reference (class) | Date accesibile întregii ierarhii (autentificare, temă) |
Întrebări frecvente
@State — pentru value types (struct, enum, String, Int) și starea locală a unui View. SwiftUI gestionează automat memoria State. @StateObject — pentru reference types (class) conforme cu ObservableObject. @StateObject deține obiectul și redesenează View-ul la modificarea proprietăților @Published. Pentru contoare simple folosiți @State; pentru modele cu logică — @StateObject.
Da, SwiftUI se integrează cu UIKit prin UIHostingController (SwiftUI în interiorul UIKit) și UIViewRepresentable (UIKit în interiorul SwiftUI). UIHostingController înfășoară SwiftUI View într-un UIViewController. UIViewRepresentable permite utilizarea componentelor UIKit (MKMapView, WKWebView) în SwiftUI. Aceasta este abordarea standard pentru migrarea proiectelor de la UIKit la SwiftUI.
ViewBuilder — result builder (Swift 5.1) care transformă un set de View într-o singură valoare de tip TupleView, Group sau ConditionalContent. ViewBuilder permite scrierea de if/else și switch imperativ în interiorul body-ului declarativ. Fără ViewBuilder ar trebui să returnați AnyView sau Group pentru fiecare bloc condițional. ViewBuilder este motivul pentru care în body nu este nevoie de virgulă între View-uri.
Da, SwiftUI suportă iOS 13+, iPadOS 13+, macOS 10.15+, watchOS 6+, tvOS 13+ și visionOS 1+. Totuși, unele API-uri sunt disponibile doar pe versiunile noi: de exemplu, navigationStack (iOS 16+), Observable macro (iOS 17+). Pentru compatibilitate inversă, folosiți #available și adaptări UIKit.
Xcode Debug View Hierarchy arată arborele SwiftUI View cu modificatori și cadre. Instrumentul SwiftUI Inspector (panoul drept Xcode) permite modificarea modificatorilor în timp real. self._printChanges() în body jurnalizează cauzele redesemnării. Instruments cu șablonul SwiftUI urmărește performanța View și detectează redesemnări excesive.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și