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 заснований на принципі реактивності: інтерфейс — це функція від стану. При зміні стану 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 використовується для нових проєктів з target iOS 17+; проєкти з підтримкою iOS 14–15 потребують UIKit через обмежену сумісність SwiftUI.
У SwiftUI інтерфейс описується через ViewBuilder — result builder, що трансформує набір View у кортеж або Group. Модифікатори (.padding(), .font(), .foregroundColor()) створюють нові View зі зміненими налаштуваннями, а не мутувати вихідний об'єкт. Кожен модифікатор повертає нове View, що дозволяє chaining. 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 знаходить мінімальні зміни в tree.
@Binding — property wrapper, що створює двосторонній зв'язок між View та даними, якими View не володіє. Binding — це посилання на State (або інший source of truth), що дозволяє дочірньому 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 для source of truth в ієрархії 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, text field) |
| @Binding | Зовнішнє | Посилання на State | Дочірнє View, що змінює дані батька |
| @StateObject | Володіння View | Reference (class) | Source of truth для складної моделі даних |
| @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 трасує performance View та виявляє надлишкові перемальовки.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.