Нативна розробка iOS на Swift
Програма падає при холодному старті — EXC_BAD_ACCESS при ініціалізації синглтона, який звертається до іншого синглтона, який ще не ініціалізований. Або: ViewController витікає в пам'яті, тому що closure захоплює self без [weak self], і цей ViewController висить в пам'яті через два переходи після того, як користувач його покинув. Це не гіпотетичні сценарії — це два найчастіших класи проблем на iOS-проектах, які приходять до нас після іншої команди.
Нативна розробка iOS на Swift — це прямий доступ до платформи. Без прошарку, без компромісів по продуктивності, з повним контролем над тим, що відбувається на кожному кадрі.
SwiftUI проти UIKit: не холіварне питання, а інженерне рішення
SwiftUI з'явився в 2019 році. До 2023–2024 років він закриває переважну більшість production-задач. Але UIKit не застарів та не зник — Apple його не deprecate-ить, а продовжує додавати API.
Реальна картина на крупних проектах: гібридний підхід. SwiftUI для більшості екранів, UIKit там, де SwiftUI упирається в обмеження.
Де SwiftUI перемагає беззаперечно
Декларативний синтаксис SwiftUI скорочує код UI в 3–5 разів порівняно з UIKit. Екран налаштувань з List, Toggle, Picker — це 40 рядків SwiftUI проти 200 рядків UIKit з делегатами UITableViewDataSource.
@State, @Binding, @ObservableObject (а з iOS 17 — макрос @Observable) створюють реактивний зв'язок між даними та UI без ручного reloadData(). Зміна @State-змінної автоматично перерисовує затронуту частину ієрархії. Це працює правильно, якщо розумієте, як SwiftUI вичислює diff — через Equatable та id у ForEach.
AsyncImage, NavigationStack з типобезпечним роутингом через NavigationPath, searchable, refreshable — це готові паттерни, які UIKit потребує реалізовувати вручну.
Де UIKit по-прежнему необхідний
UICollectionView з compositional layout та diffable data source — складні сітки з різними типами ячейок, горизонтальними секціями всередину вертикального скролу, динамічними розмірами ячейок. SwiftUI LazyVGrid / LazyHGrid не дають такого контролю.
Кастомні переходи між екранами. UIViewControllerAnimatedTransitioning та UIViewControllerInteractiveTransitioning — інтерактивний pop gesture з частковим прогресом, кастомний hero-перехід з точним управлінням frame. SwiftUI matchedGeometryEffect закриває частину випадків, але не всі.
UITextView з TextKit 2. Багатий редактор тексту, кастомні атрибути, кастомний рендеринг — TextKit 2 (iOS 16+) перейшов на async layout, що вирішило проблеми з продуктивністю на довгих документах. SwiftUI TextEditor — це обгортка навколо UITextView без прямого доступу до TextKit.
UIScrollView з кастомною поведінкою. scrollViewDidScroll, parallax-ефекти, sticky headers з кастомною логікою, pull-to-refresh з кастомним індикатором. SwiftUI ScrollView з scrollPosition та onScrollGeometryChange (iOS 17) закривають частину випадків, але не всі.
Інтеграція: UIHostingController та UIViewRepresentable
UIHostingController обгортає SwiftUI-вью та робить його UIViewController. Вбудовуємо SwiftUI-екран у UIKit navigation stack — без проблем.
UIViewRepresentable робить навпаки: обгортає UIView для використання всередину SwiftUI. MapKit's MKMapView, WKWebView, кастомні UIKit-компоненти — все це доступне в SwiftUI через цей протокол.
Один паттерн, який ми використовуємо на проектах: UIKit-координатор (Coordinator pattern) управляє навігацією на рівні флоу, а самі екрани реалізовані на SwiftUI. Координатор створює UIHostingController, передає йому ViewModel через ініціалізатор або @EnvironmentObject, управляє переходами. Це дає чисте розділення: SwiftUI займається UI, Coordinator — навігацією.
Combine та async/await: обидва живуть в одному проекті
До Swift 5.5 асинхронний код на iOS будувався на Combine або callback-ланцюгах. З появою async/await та Actor модель конкурентності стала частиною мови.
async/await + Actor: сучасний підхід
// Правильно — MainActor гарантує UI-оновлення на main thread
@MainActor
class UserViewModel: ObservableObject {
@Published var user: User?
@Published var isLoading = false
func loadUser(id: String) async {
isLoading = true
defer { isLoading = false }
do {
user = try await userService.fetch(id: id)
} catch {
// handle error
}
}
}
@MainActor — це actor-ізоляція, яка гарантує виконання на main thread. Без нього @Published-властивості можна оновити з фонового потоку, що приведе до попередження Xcode та потенціального краху.
Task { } запускає async-задачу з синхронного контексту. TaskGroup — паралельні задачі з агрегацією результатів. withCheckedThrowingContinuation — міст між callback API та async/await.
Де Combine залишається корисним
Combine не замінений async/await — вони доповнюють один одного. Combine ефективний для:
-
Дебаунсингу вводу.
searchTextField.textPublisher.debounce(for: .milliseconds(300), scheduler: RunLoop.main).sink { ... }— класичний паттерн для пошуку. -
Об'єднання кількох джерел даних.
Publishers.CombineLatestабоPublishers.Zipдля злиття двох Publishers в один. -
Операторів трансформації.
map,flatMap,filter,removeDuplicates— функціональна обробка потоку значень.
На нових проектах ми використовуємо async/await як основний інструмент для сетевих викликів та бізнес-логіки, Combine — для реактивного прив'язування UI-стану там, де @Published + sink зручніше явного управління задачами.
Архітектура iOS-програми
MVVM — базовий паттерн. ViewModel містить логіку та @Published-стан, SwiftUI View підписується через @ObservedObject або @StateObject. Одне правило: View не знає про URLSession, CoreData, UserDefaults.
Clean Architecture додає шари Repository та UseCase. UserRepository абстрагує джерело даних (мережа vs кеш). FetchUserUseCase містить бізнес-правило. UserViewModel викликає UseCase та управляє UI-станом. Кожен шар тестується незалежно через protocol-залежності та mock-підстановку.
TCA (The Composable Architecture) — більш строгий паттерн від Point-Free. State, Action, Reducer, Effect — все явне, все тестуємо, composable через Scope. Добре працює у великих командах, де важливі передбачуваність поведінки та можливість тестувати кожен reducer ізольовано.
Інструменти, без яких не обходиться жоден реліз
Xcode Instruments. Time Profiler показує, де CPU тратить час. Allocations — утечки пам'яті та надмірні виділення. Leaks — об'єкти, які не звільняються. Перед кожним релізом — обов'язковий прогон.
Firebase Crashlytics. Crash-free rate, групування за stack trace, breadcrumbs подій до краху. Налаштовується за 30 хвилин, дає картину по всьому парку пристроїв.
Fastlane match. Управління сертифікатами та provisioning profiles через зашифрований git-репозиторій. Усуває «у мене локально собирается, а на CI нема» раз і назавжди.
XCTest + XCUITest. Unit-тести для ViewModel та UseCase, UI-тесті для критичних флоу (онбординг, оплата, авторизація).
Процес та орієнтири
Розробка iOS-програми: аналіз вимог → архітектурне проектування → UI/UX (паралельно) → розробка → тестування (TestFlight) → подання в App Store → підтримка.
App Store Review займає 24–48 годин для стандартної програми, до 7 днів при першому поданні або після rejection.
| Складність | Орієнтовний період |
|---|---|
| MVP (5–8 екранів, базовий API) | 6–10 тижнів |
| Середня програма (15–25 екранів) | 3–5 місяців |
| Складна (платежі, AR, CoreML, кастомний UI) | 5–9 місяців |
Вартість — індивідуально після аналізу ТЗ та дизайну.







