Перейти к основному содержимому

Жизненный цикл приложения на Swift

Разработчику Архитектору

Создание проекта и структура приложения

Инициализация проекта в Xcode

Создание нового проекта на Swift начинается с запуска Xcode и выбора опции "Create a new Xcode project". Xcode предоставляет различные шаблоны приложений — App, Game, Augmented Reality App, Document App, Framework, Package. Для стандартного приложения используется шаблон "App".

При создании проекта указываются следующие параметры:

  • Название продукта
  • Команда разработки
  • Идентификатор организации
  • Интерфейс (Storyboard или SwiftUI)
  • Язык программирования (Swift или Objective-C)
  • Поддержка Core Data
  • Включение тестирования

Пример конфигурации нового проекта:

Код ITЗагрузка примера кода…

Разбор:

  • Это манифест Swift Package Manager, который описывает пакет и правила сборки.
  • platforms фиксирует минимальную версию iOS, с которой совместим код.
  • products определяют, что именно экспортируется наружу (библиотека/исполняемый модуль).
  • targets задают единицы компиляции и их зависимости.

Структура каталогов проекта

Стандартная структура проекта Xcode включает следующие основные каталоги:

MyApplication/
├── MyApplication/
│ ├── Assets.xcassets/
│ ├── Base.lproj/
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── AppDelegate.swift
│ ├── SceneDelegate.swift
│ └── ViewController.swift
├── MyApplication.xcodeproj/
├── MyApplicationTests/
└── MyApplicationUITests/

Каталог Assets.xcassets содержит все изображения, цвета, шрифты и другие ресурсы приложения. Base.lproj содержит файлы интерфейса. AppDelegate.swift и SceneDelegate.swift управляют жизненным циклом приложения.


Конфигурационные файлы проекта

Файл Info.plist содержит метаданные приложения — идентификатор пакета, версия, необходимые разрешения, поддерживаемые ориентации устройства. Современные версии Xcode позволяют использовать Swift-код для конфигурации вместо XML-формата.

// Пример конфигурации через Swift
@main
struct MyApplicationApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}

Разбор:

  • @main помечает главный тип приложения и точку входа.
  • Протокол App используется в SwiftUI-жизненном цикле вместо классического AppDelegate-старта.
  • WindowGroup создаёт сцену окна, где корневым представлением становится ContentView.

Точка входа приложения

Функция main и @main атрибут

В традиционных языках программирования точкой входа служит функция main. В Swift для приложений iOS используется атрибут @main, который автоматически генерирует точку входа. Этот подход появился в Swift 5.3 и значительно упростил структуру приложения.

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
}

Разбор:

  • UIApplicationDelegate получает ключевые события старта и состояния приложения.
  • Метод didFinishLaunchingWithOptions вызывается после запуска инициализации приложения.
  • Возврат true подтверждает успешный запуск для системы.

Атрибут @main указывает компилятору, что данный класс или структура является основной точкой входа приложения. До появления @main требовалась явная функция main в отдельном файле.


Роль класса AppDelegate

Класс AppDelegate управляет глобальными событиями приложения — запуск, переход в фоновый режим, возврат в активное состояние, завершение работы. Все методы делегата вызываются системой в определенном порядке.

Код ITЗагрузка примера кода…

Разбор:

  • Пример показывает полный набор методов жизненного цикла приложения UIKit.
  • Методы applicationDidBecomeActive / applicationDidEnterBackground отмечают переходы между foreground/background.
  • В каждую точку обычно выносят соответствующие операции — запуск задач, пауза, сохранение данных.

Жизненный цикл приложения

Состояния приложения

Приложение iOS проходит через пять основных состояний в течение своего жизненного цикла:

Not Running — приложение не запущено или было завершено системой. Это начальное состояние до первого запуска или после полного завершения работы.

Inactive — приложение работает в фоновом режиме, но не получает события. Это состояние возникает при переходе между активным и фоновым режимами, при получении уведомлений, входящих вызовов.

Active — приложение работает на переднем плане и получает все события. Это основное рабочее состояние, когда пользователь взаимодействует с интерфейсом.

Background — приложение выполняется в фоновом режиме и может выполнять ограниченные задачи. Система предоставляет несколько минут для завершения операций.

Suspended — приложение находится в фоновом режиме, но не выполняет код. Система приостанавливает приложение для экономии ресурсов.


Последовательность методов жизненного цикла

При первом запуске приложения система вызывает методы в следующем порядке:

Код ITЗагрузка примера кода…

Разбор:

  • Блок показывает последовательность запуска: ранняя инициализация -> финальная конфигурация -> переход в active.
  • setupCoreServices/configureAppearance/setupNavigation демонстрируют распределение ответственности по этапам.
  • applicationDidBecomeActive — точка, где допустимо возобновлять интерактивные задачи.

При переходе в фоновый режим вызываются методы:

Код ITЗагрузка примера кода…

Разбор:

  • Эти методы обрабатывают уход приложения в фон.
  • В willResignActive выполняется пауза текущих задач и подготовка состояния.
  • beginBackgroundTask в didEnterBackground запрашивает ограниченное время на завершение критичных операций.

При возврате в активное состояние:

func applicationWillEnterForeground(_ application: UIApplication) {
// Подготовка к активному состоянию
restoreState()
checkForUpdates()
}

func applicationDidBecomeActive(_ application: UIApplication) {
// Полное восстановление активности
resumeTasks()
refreshInterface()
}

Разбор:

  • При возврате из фона сначала идёт подготовка (restoreState, checkForUpdates), затем полное восстановление активности.
  • Такой порядок помогает избежать гонок и неполного восстановления UI.
  • Разделение по методам делает поведение приложения предсказуемым при смене состояний.

SceneDelegate и многозадачность

Начиная с iOS 13, Apple ввела концепцию сцен (scenes) для поддержки многозадачности на iPad и других устройствах. SceneDelegate управляет жизненным циклом отдельных окон приложения.

Код ITЗагрузка примера кода…

Разбор:

  • SceneDelegate управляет отдельным окном/сценой, что важно для многосценарности iPadOS.
  • В willConnectTo создаётся UIWindow, назначается root-контроллер и окно делается видимым.
  • Остальные методы отражают lifecycle конкретной сцены, а не всего приложения.

Жизненный цикл представлений

Состояния жизненного цикла ViewController

Каждый ViewController проходит через серию состояний при загрузке, отображении и удалении. Понимание этих состояний критически важно для правильного управления ресурсами и данными.

Loaded — представление загружено в память, но еще не отображено на экране. Метод viewDidLoad вызывается один раз при создании представления.

Appeared — представление полностью отображено и видимо пользователю. Метод viewDidAppear вызывается каждый раз при появлении представления.

Disappeared — представление скрыто, но остается в памяти. Метод viewDidDisappear вызывается при скрытии представления.

Unloaded — представление удалено из памяти. Система освобождает ресурсы при нехватке памяти или при явном удалении.


Методы жизненного цикла ViewController

Код ITЗагрузка примера кода…

Разбор:

  • Показаны ключевые методы lifecycle экрана — загрузка, показ, скрытие, перерасчёт layout, реакция на memory warning.
  • viewDidLoad подходит для одноразовой настройки, viewWillAppear/viewDidAppear — для обновления перед показом и после него.
  • viewWillDisappear/viewDidDisappear — точки остановки задач и освобождения ресурсов.

Порядок вызова методов при навигации

При переходе между представлениями система вызывает методы в определенном порядке. Понимание этой последовательности помогает правильно управлять состоянием и ресурсами.

При показе нового представления:

Старый контроллер:
- viewWillDisappear
- viewDidDisappear

Новый контроллер:
- viewDidLoad
- viewWillAppear
- viewDidAppear

При возврате к предыдущему представлению:

Текущий контроллер:
- viewWillDisappear
- viewDidDisappear

Предыдущий контроллер:
- viewWillAppear
- viewDidAppear

Управление памятью в жизненном цикле

Swift использует автоматическое подсчет ссылок (ARC) для управления памятью. Понимание жизненного цикла помогает избежать утечек памяти и сильных ссылочных циклов.

Код ITЗагрузка примера кода…

Разбор:

  • Пример связывает lifecycle экрана с управлением ресурсами и наблюдателями.
  • Подписки добавляются в setupObservers() с [weak self], чтобы не создавать retain cycle.
  • В viewWillDisappear и viewDidDisappear останавливаются фоновые операции и удаляются observers.
  • deinit подтверждает освобождение контроллера и выполняет финальную очистку.

Управление фоновыми задачами

Типы фоновых операций

iOS поддерживает несколько типов фоновых операций, каждая со своими ограничениями и временем выполнения.

Background Fetch — периодическая загрузка данных в фоновом режиме. Система определяет оптимальное время для выполнения операций.

Код ITЗагрузка примера кода…

Разбор:

  • Включается Background Fetch через setMinimumBackgroundFetchInterval.
  • Метод performFetchWithCompletionHandler обязан вызвать completion с результатом (newData, noData, failed).
  • Корректный completion помогает системе планировать будущие фоновые запуски.

Background Processing — длительные задачи, требующие дополнительного времени. Система предоставляет до 3 минут для завершения операций.

func performBackgroundTask() {
var task: UIBackgroundTaskIdentifier = .invalid
task = UIApplication.shared.beginBackgroundTask {
UIApplication.shared.endBackgroundTask(task)
task = .invalid
}

processLargeFile { _ in
UIApplication.shared.endBackgroundTask(task)
task = .invalid
}
}

Разбор:

  • beginBackgroundTask запрашивает дополнительное время для длинной операции.
  • В expiration-handler обязательно нужно закрывать задачу через endBackgroundTask.
  • После завершения processLargeFile задача также закрывается, чтобы не расходовать системный лимит.

Location Updates — отслеживание местоположения в фоновом режиме. Требует явного разрешения пользователя и постоянного отображения индикатора.

func startLocationTracking() {
locationManager.requestAlwaysAuthorization()
locationManager.allowsBackgroundLocationUpdates = true
locationManager.startUpdatingLocation()
}

Разбор:

  • Запрашивается разрешение requestAlwaysAuthorization для фоновой геолокации.
  • allowsBackgroundLocationUpdates = true включает обновления в фоне.
  • startUpdatingLocation() запускает фактическое получение координат.

Audio Playback — воспроизведение аудио в фоновом режиме. Приложение продолжает работать, пока воспроизводится звук.

func configureAudioSession() {
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
try AVAudioSession.sharedInstance().setActive(true)
} catch {
print("Ошибка настройки аудио сессии")
}
}

Разбор:

  • AVAudioSession.setCategory(.playback) переводит аудиосессию в режим фонового воспроизведения.
  • setActive(true) активирует сессию перед проигрыванием.
  • do/catch нужен для обработки ошибок конфигурации аудио-стека.

Ограничения фонового режима

Система iOS накладывает строгие ограничения на фоновые операции для экономии ресурсов и продления времени работы от батареи. Приложение получает ограниченное время для завершения операций после перехода в фоновый режим.

func applicationDidEnterBackground(_ application: UIApplication) {
print("Оставшееся время: \(application.backgroundTimeRemaining) секунд")

let task = application.beginBackgroundTask {
print("Фоновое время истекло")
self.endBackgroundTask()
}

performCriticalTasks {
application.endBackgroundTask(task)
}
}

Разбор:

  • backgroundTimeRemaining показывает доступный лимит времени для фоновой работы.
  • Через beginBackgroundTask создаётся защищённое окно для критичных операций.
  • В completion критичной задачи вызывается endBackgroundTask, чтобы корректно завершить фоновой режим.

Сборка и архивация приложения

Конфигурации сборки

Xcode поддерживает различные конфигурации сборки — Debug, Release, Custom. Каждая конфигурация имеет свои настройки оптимизации, отладки и подписи кода.

// Проверка конфигурации сборки
#if DEBUG
print("Отладочная сборка")
enableDebugFeatures()
#else
print("Релизная сборка")
enableProductionFeatures()
#endif

// Проверка режима оптимизации
#if swift(>=5.5)
print("Swift 5.5 или новее")
#endif

Разбор:

  • Условная компиляция #if DEBUG разделяет поведение для debug и release сборок.
  • Можно включать отладочные функции только в development-конфигурации.
  • Проверка #if swift(>=5.5) помогает ветвить код по версии компилятора Swift.

Схемы сборки (Schemes)

Схема определяет, как проект собирается, запускается, тестируется и профилируется. Можно создавать пользовательские схемы для разных целей — разработка, тестирование, производство.

Код ITЗагрузка примера кода…

Разбор:

  • XML-фрагмент описывает настройки запуска схемы Xcode.
  • buildConfiguration="Debug" выбирает профиль сборки при запуске.
  • EnvironmentVariables позволяет подставлять переменные окружения для разных стендов (API_BASE_URL).

Архивация приложения

Архивация создает дистрибутивную версию приложения для распространения через App Store или тестирования. Процесс включает компиляцию, связывание, кодирование и создание пакета.

# Команда архивации через xcodebuild
xcodebuild archive \
-scheme "MyApplication" \
-configuration "Release" \
-archivePath "build/MyApplication.xcarchive" \
-destination "generic/platform=iOS"

# Экспорт архива
xcodebuild -exportArchive \
-archivePath "build/MyApplication.xcarchive" \
-exportPath "build/export" \
-exportOptionsPlist "ExportOptions.plist"

Разбор:

  • Первая команда создаёт архив .xcarchive для выбранной схемы в Release-конфигурации.
  • Вторая команда экспортирует архив в готовый артефакт (например, .ipa) по правилам ExportOptions.plist.
  • Такой CLI-пайплайн используется в CI/CD для воспроизводимой сборки.

Распространение приложения

Подготовка к публикации

Перед публикацией в App Store необходимо подготовить метаданные — иконки, скриншоты, описание, ключевые слова, возрастной рейтинг. Все изображения должны соответствовать требованиям размера и формата.

Код ITЗагрузка примера кода…

Разбор:

  • JSON описывает набор файлов иконок по устройствам (idiom), размерам (size) и плотности (scale).
  • Каждая запись связывает конкретный PNG с требуемым слотом App Icon.
  • Блок info хранит служебные метаданные каталога ассетов.

Тестирование перед релизом

Перед публикацией необходимо провести полное тестирование — функциональное, производительное, совместимости, безопасности. TestFlight позволяет распространять бета-версии среди тестировщиков.

Код ITЗагрузка примера кода…

Разбор:

  • Bundle.main читает метаданные текущей сборки из Info.plist.
  • Ключи CFBundleShortVersionString и CFBundleVersion дают версию и номер билда.
  • if #available(iOS 15.0, *) позволяет безопасно включать функции по версии ОС.

Отправка в App Store Connect

Процесс отправки включает загрузку архива, заполнение метаданных, выбор категории, установку цены, указание территорий распространения. После отправки приложение проходит модерацию Apple.

# Загрузка в App Store Connect через xcodebuild
xcodebuild -exportArchive \
-archivePath "build/MyApplication.xcarchive" \
-exportPath "build/export" \
-exportOptionsPlist "AppStoreExportOptions.plist" \
-allowProvisioningUpdates

# Использование Transporter для загрузки
xcrun altool --upload-app \
-f "build/export/MyApplication.ipa" \
-u "developer@apple.com" \
-p "@keychain:Application Loader"

Разбор:

  • Блок показывает CLI-этап экспорта и загрузки сборки в App Store Connect.
  • -allowProvisioningUpdates разрешает Xcode автоматически подтягивать профили подписи при необходимости.
  • xcrun altool --upload-app отправляет .ipa; учётные данные берутся из keychain-профиля.

Практическая карта жизненного цикла

Жизненный цикл проще держать в голове как рабочую карту "что делать в какой точке", а не как список методов.


Где выполняется основная ответственность

  • didFinishLaunching — инициализация глобальных сервисов.
  • sceneDidBecomeActive — запуск интерактивных задач и подписок.
  • sceneDidEnterBackground — сохранение изменений и остановка тяжелых операций.
  • viewDidLoad — одноразовая сборка UI и зависимостей экрана.
  • viewWillAppear — актуализация данных перед показом.

Частые ошибки

  1. Тяжелые запросы или синхронные операции в делегатах приложения.
  2. Дублирование инициализации одновременно в viewDidLoad и viewWillAppear.
  3. Отсутствие отмены задач при закрытии экрана или уходе сцены в фон.

Связанные материалы


Практический сценарий одного экрана

Чтобы связать теорию с ежедневной разработкой, удобно держать простой шаблон ответственности по методам.


Где что размещать

  • viewDidLoad — создание UI, настройка подписок, инъекция сервисов.
  • viewWillAppear — обновление видимых данных и запуск легких операций.
  • viewDidAppear — запуск анимаций и не критичных метрик экрана.
  • viewWillDisappear — остановка таймеров, отмена задач и сохранение черновиков.

Мини-шаблон контроллера

Код ITЗагрузка примера кода…

Разбор:

  • refreshTask хранит активную асинхронную загрузку заказов для конкретного экрана.
  • В viewWillAppear создаётся новая Task, в viewWillDisappear она отменяется.
  • Такой шаблон предотвращает "висячие" запросы после ухода пользователя со страницы.
  • @MainActor у reloadOrders() фиксирует обновление UI на главном потоке.

Такой каркас помогает держать жизненный цикл управляемым и защищает от утечек фоновых задач.


SwiftUI — scenePhase и отмена задач

Код ITЗагрузка примера кода…

Разбор:

  • @Environment(\.scenePhase) отражает состояние сцены приложения (active / inactive / background).
  • При .active запускается новая Task для загрузки ленты.
  • При уходе в фон задача отменяется, чтобы не тратить сеть и не обновлять UI "в пустоту".
  • onChange(of:scenePhase) — SwiftUI-аналог части логики AppDelegate/SceneDelegate для современных приложений.

Содержание