SwiftUI — декларативната рамка на Apple за изграждане на потребителски интерфейси на всички платформи от екосистемата, представена на WWDC 2019. За разлика от императивния UIKit с неговия viewDidLoad и ръчно обновяване на екрана, SwiftUI описва UI като колекция от прости структури, отговарящи на протокола View. Според Swift.org (2025), SwiftUI се използва в 65% от новите проекти, публикувани в App Store. Рамката автоматично управлява обновяването на интерфейса чрез механизма State и Data Flow — при промяна на данните View се прерисува без ръчно извикване на reloadData.
Основни точки
SwiftUI — декларативната UI рамка на Apple, коренно различна от UIKit. Вместо да създава контролери, изгледи и ръчно да управлява жизнения им цикъл, разработчикът описва интерфейса под формата на декларации: какво трябва да има на екрана, а не как да се изгради. SwiftUI се основава на принципа на реактивност: интерфейсът е функция на състоянието. При промяна на състоянието (State) SwiftUI автоматично преизчислява body на всички зависими View и актуализира само променените части на екрана. SwiftUI е достъпен на iOS 13+, iPadOS 13+, macOS 10.15+, watchOS 6+, tvOS 13+ и visionOS 1+. Кодът на SwiftUI е междуплатформен: един файл работи на iPhone, iPad, Mac и Apple Watch с минимални платформени адаптации. Според Apple WWDC Session 101 (2024), SwiftUI покрива над 90% от стандартните UI модели на App Store.
UIKit — императивната рамка (2008): разработчикът създава UIViewController, конфигурира subviews в viewDidLoad, имплементира delegate/datasource за UITableView и актуализира екрана чрез reloadData или setNeedsLayout. SwiftUI замества контролерите с прости структури View, делегатите — с binding и onChange, Auto Layout — с HStack/VStack/ZStack и модификатори (padding, frame, offset). UIKit изисква ръчно управление на паметта чрез ARC; SwiftUI — структури, които не изискват броене на референции. Производителността на SwiftUI е сравнима с UIKit: рамката използва diffing алгоритъм за минимален набор от промени. В IT Sectr SwiftUI се използва за нови проекти с целева iOS 17+; проекти с поддръжка на iOS 14–15 изискват UIKit поради ограничената съвместимост на SwiftUI.
В SwiftUI интерфейсът се описва чрез ViewBuilder — result builder, който трансформира набор от View в tuple или Group. Модификаторите (.padding(), .font(), .foregroundColor()) създават нови View с променени настройки, а не мутират оригиналния обект. Всеки модификатор връща ново View, което позволява верижно свързване. ViewBuilder поддържа if/else, switch, ForEach — условно и циклично изобразяване без отделни контролери. View в SwiftUI е value type (struct), което гарантира предвидимо поведение и елиминира race conditions.
View — протокол с едно единствено изискване: computed property body от тип some View. Всяка структура, отговаряща на View, описва своята част от екрана в body. Типът some View — opaque return type, който скрива конкретния тип на върнатото View (комбинация от VStack, HStack, ZStack, Text, Image и т.н.). Компилаторът Swift извежда конкретния тип по време на компилация, запазвайки производителността на директно извикване без изтриване на тип.
import SwiftUI
struct GreetingView: View {
var name: String
var body: some View {
VStack(spacing: 12) {
Text("Здравейте, \(name)!")
.font(.largeTitle)
.foregroundColor(.primary)
Text("Добре дошли в SwiftUI")
.font(.body)
.foregroundColor(.secondary)
}
.padding()
.background(
RoundedRectangle(cornerRadius: 12)
.fill(.ultraThinMaterial)
)
}
}Структурата GreetingView приема параметъра name и показва два текстови блока във вертикален стек. Модификаторите .font, .foregroundColor, .padding и .background конфигурират външния вид. SwiftUI извиква body всеки път, когато входните параметри (name) се променят — прерисуването се извършва само за променените части. В примера се използва RoundedRectangle с .ultraThinMaterial — вграден нативен blur фон в SwiftUI.
@State — property wrapper, деклариращ локално състояние, принадлежащо на едно View. SwiftUI автоматично управлява паметта на State: при промяна на стойността body се прерисува, но само за View, които използват това State. State е източник на истина (source of truth) за прости типове (String, Int, Bool, enum). Не използвайте @State за сложни модели от данни — за тях са предназначени @StateObject и @ObservedObject. State трябва да бъде private и съхраняван в самото View, а не предаван между компоненти.
import SwiftUI
struct CounterView: View {
@State private var count = 0
var body: some View {
VStack(spacing: 20) {
Text("Брой: \(count)")
.font(.system(size: 48, weight: .bold))
Button(action: { count += 1 }) {
Label("Увеличи", systemImage: "plus.circle")
}
.buttonStyle(.borderedProminent)
}
.padding()
}
}Начална стойност count = 0. Всяко натискане на бутона увеличава count; SwiftUI автоматично прерисува CounterView изцяло (всички View). В UIKit подобен сценарий би изисквал IBOutlet, IBAction и ръчно обновяване на label.text. @State гарантира, че View се прерисува само при промяна на конкретен State — diffing алгоритъмът на SwiftUI намира минимални промени в дървото.
@Binding — property wrapper, създаващ двупосочна връзка между View и данни, които View не притежава. Binding е референция към State (или друг източник на истина), позволяваща на дъщерното View да чете и променя стойността, съхранявана в родителя. Binding се обозначава с префикс $: $count предава Binding<Int> на дъщерното View. Без Binding дъщерното View не може да промени данните на родителя — може само да ги чете.
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 притежава State quantity и предава Binding чрез $quantity. StepperControl може да променя value, а quantity в родителя се синхронизира автоматично. Binding не е копие на данни, а мост към източника на истина. Използвайте @Binding за персонализирани контроли, редактори и многократно използвани компоненти, които трябва да променят данните на родителя.
@StateObject — property wrapper за създаване и притежаване на инстанция на клас, отговарящ на ObservableObject. View създава обекта веднъж за жизнения цикъл и се прерисува при промяна на неговите @Published свойства. @ObservedObject — подобен wrapper, но View не притежава обекта — обектът се създава и съхранява извън View (предава се чрез инициализатора). Apple препоръчва @StateObject за източник на истина в йерархията на View и @ObservedObject за инжектиране на зависимости.
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("Добре дошли, \(settings.username)!")
.font(.headline)
}
}
.padding()
}
}UserSettings — ObservableObject с две @Published свойства. ProfileView притежава обекта чрез @StateObject. Промяната на username или isLoggedIn автоматично прерисува ProfileView. @Published използва Combine Publisher за уведомяване на SwiftUI за промени. За предаване на settings на дъщерни View използвайте @ObservedObject:
Apple дефинира четири нива на Data Flow в SwiftUI: @State (локално, value type), @Binding (двупосочно), @StateObject/@ObservedObject (reference type с ObservableObject), @EnvironmentObject (глобално, инжектиране чрез среда). EnvironmentObject позволява предаване на данни през цялата йерархия на View без изрично предаване в инициализатора. Допълнително @AppStorage работи с UserDefaults, @SceneStorage — със състоянието на сцената, @FetchRequest — с Core Data. Изборът на ниво Data Flow определя архитектурата на приложението: простите екрани използват State/Binding, модулните — ObservedObject, мащабните — EnvironmentObject + решения, подобни на Redux (TCA, Composable Architecture).
| Property Wrapper | Притежание | Тип | Кога да се използва |
|---|---|---|---|
| @State | Локално | Value (struct, enum) | Просто състояние на едно View (брояч, toggle, текстово поле) |
| @Binding | Външно | Референция към State | Дъщерно View, променящо данните на родителя |
| @StateObject | Притежание на View | Reference (class) | Източник на истина за сложен модел данни |
| @ObservedObject | Инжектиране | Reference (class) | Модел, създаден извън View (предаден чрез init) |
| @EnvironmentObject | Глобално | Reference (class) | Данни, достъпни за цялата йерархия (автентикация, тема) |
Често задавани въпроси
@State — за value types (struct, enum, String, Int) и локално състояние на едно View. SwiftUI автоматично управлява паметта на State. @StateObject — за reference types (class), отговарящи на ObservableObject. @StateObject притежава обекта и прерисува View при промяна на @Published свойства. За прости броячи използвайте @State; за модели с логика — @StateObject.
Да, SwiftUI се интегрира с UIKit чрез UIHostingController (SwiftUI вътре в UIKit) и UIViewRepresentable (UIKit вътре в SwiftUI). UIHostingController обвива SwiftUI View в UIViewController. UIViewRepresentable позволява използването на UIKit компоненти (MKMapView, WKWebView) в SwiftUI. Това е стандартният подход за миграция на проекти от UIKit към SwiftUI.
ViewBuilder — result builder (Swift 5.1), който трансформира набор от View в единична стойност от тип TupleView, Group или ConditionalContent. ViewBuilder позволява писане на императивен if/else и switch вътре в декларативен body. Без ViewBuilder би трябвало да връщате AnyView или Group за всеки условен блок. ViewBuilder е причината, поради която в body не е необходима запетая между View.
Да, SwiftUI поддържа iOS 13+, iPadOS 13+, macOS 10.15+, watchOS 6+, tvOS 13+ и visionOS 1+. Някои API обаче са достъпни само на по-нови версии: например navigationStack (iOS 16+), Observable macro (iOS 17+). За обратна съвместимост използвайте #available и UIKit адаптации.
Xcode Debug View Hierarchy показва дървото SwiftUI View с модификатори и рамки. Инструментът SwiftUI Inspector (десен панел на Xcode) позволява промяна на модификатори в реално време. self._printChanges() в body логва причините за прерисуване. Instruments с шаблон SwiftUI проследява производителността на View и открива прекомерни прерисувания.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също