Active — активное состояние жизненного цикла iOS-приложения, в котором оно находится на переднем плане, получает события касания и взаимодействует с пользователем. Разбираемся, как работает состояние Active, какие методы делегата UIApplicationDelegate за него отвечают и как корректно обрабатывать переходы между Active и Inactive в Swift.
Главное
Active — состояние жизненного цикла мобильного приложения, в котором оно находится на переднем плане, отображается на экране устройства и активно взаимодействует с пользователем. В этом состоянии приложение получает все события касания, нажатия клавиш, данные с акселерометра и гироскопа, а также имеет полный доступ к графическому процессору для рендеринга интерфейса.
На iOS состояние Active — это часть пятисостоянийной модели жизненного цикла: Not Running → Inactive → Active → Inactive → Background → Suspended → Not Running. На Android аналогом является состояние Activity после вызова onResume, когда Activity находится на вершине стека и принимает пользовательский ввод. Active — единственное состояние, в котором UI полностью интерактивен и реагирует на жесты, скролл, нажатия и анимации.
Система предоставляет приложению в Active максимальный приоритет по процессору и оперативной памяти. Это означает, что система не будет завершать такое приложение при нехватке ресурсов — сначала будут выгружены фоновые и приостановленные процессы. Однако приложение должно эффективно использовать ресурсы, чтобы не разряжать батарею и не вызывать троттлинг CPU.
Для пользователя Active — это нормальное состояние работы с приложением. Пользователь видит интерфейс, может нажимать кнопки, заполнять формы, скроллить ленту. Любое прерывание этого состояния (звонок, уведомление, свайп вверх для Control Center) переводит приложение в Inactive, после чего оно может вернуться в Active или уйти в Background.
iOS использует UIApplicationMain для управления состоянием. При переходе в Active система вызывает applicationDidBecomeActive. Для SwiftUI аналогичный механизм — наблюдение за scenePhase через Environment. Android использует onResume как индикатор активности Activity на переднем плане. Оба подхода гарантируют, что приложение получает уведомление о смене состояния и может адаптировать своё поведение.
| Платформа | Метод/событие | Swift (UIKit) | SwiftUI | Android (Kotlin) |
|---|---|---|---|---|
| iOS | Переход в Active | applicationDidBecomeActive | scenePhase == .active | — |
| iOS | Уход из Active | applicationWillResignActive | scenePhase == .inactive | — |
| Android | Переход в Active | — | — | onResume() |
| Android | Уход из Active | — | — | onPause() |
В iOS состояние Active обрабатывается через UIApplicationDelegate. Основной метод — applicationDidBecomeActive(_:). Он вызывается при первом запуске приложения и при возврате из Inactive. Этот метод — идеальное место для возобновления задач, которые были приостановлены при уходе в Inactive: запуск анимаций, возобновление таймеров, перезапуск датчиков, проверка обновлений данных на сервере.
С iOS 13 Apple представила UISceneDelegate для поддержки нескольких окон на iPad. В этом случае applicationDidBecomeActive заменяется на sceneDidBecomeActive для каждого отдельного сцены. Приложения, поддерживающие только один экран, могут продолжать использовать UIApplicationDelegate. Оба подхода вызываются в момент, когда приложение или сцена становится активной.
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
// Приложение стало активным — возобновляем задачи
func applicationDidBecomeActive(_ application: UIApplication) {
resumeAnimations()
restartTimers()
refreshDataIfNeeded()
startObservingSensors()
}
// Приложение теряет активность — приостанавливаем
func applicationWillResignActive(_ application: UIApplication) {
pauseAnimations()
stopTimers()
saveDraftData()
}
private func resumeAnimations() {
UIView.animate(withDuration: 0.3) {
// Возобновление UI-анимаций
}
}
private func refreshDataIfNeeded() {
let lastRefresh = UserDefaults.standard.object(forKey: "lastRefresh") as? Date ?? .distantPast
if Date().timeIntervalSince(lastRefresh) > 300 {
fetchDataFromServer()
}
}
}В коде показана корректная обработка Active в UIKit. applicationDidBecomeActive возобновляет анимации, таймеры и проверяет, требуется ли обновление данных. applicationWillResignActive приостанавливает всё, что может потреблять ресурсы, и сохраняет черновики. Такая пара методов гарантирует, что приложение правильно реагирует на смену состояния.
В SwiftUI нет AppDelegate — управление состоянием происходит через Environment<ScenePhase>. Значение .active устанавливается, когда сцена находится на переднем плане и интерактивна. SwiftUI автоматически перезапускает анимации и обновления при возврате в Active. Разработчику нужно только подписаться на onChange для выполнения побочных эффектов.
import SwiftUI
@main
struct ActiveDemoApp: App {
@Environment(\.scenePhase) private var scenePhase
var body: some Scene {
WindowGroup {
ContentView()
}
.onChange(of: scenePhase) { oldPhase, newPhase in
switch newPhase {
case .active:
print("Сцена стала активной")
resumeWork()
case .inactive:
print("Сцена стала неактивной")
pauseWork()
case .background:
print("Сцена ушла в фон")
saveState()
@unknown default:
break
}
}
}
private func resumeWork() {
// Возобновление Network-запросов, анимаций
}
private func pauseWork() {
// Приостановка чувствительных ко времени задач
}
private func saveState() {
// Сохранение состояния приложения
}
}В SwiftUI scenePhase — это единственный источник истины о состоянии приложения. onChange позволяет выполнять действия при каждом переходе. Важно помнить, что scenePhase доступна только на iOS 14+ и в SwiftUI Lifecycle. Для приложений на UIKit со SwiftUI-экранми используйте подход с UIApplicationDelegate.
Active достигается несколькими путями. Первый и очевидный — холодный старт: пользователь нажимает на иконку, приложение переходит из Not Running через Inactive в Active. Второй — возврат из фона: пользователь переключается обратно в приложение через App Switcher, приложение проходит через Inactive и становится Active. Третий — возврат из временного прерывания: пользователь завершает звонок, закрывает Control Center или отвечает на уведомление — приложение возвращается из Inactive в Active.
Not Running → Inactive → Active — холодный старт. Background → Inactive → Active — возврат из фона. Inactive → Active — возврат из временного прерывания. В каждом случае applicationDidBecomeActive вызывается, но контекст может отличаться. При холодном старте перед Active вызывается didFinishLaunchingWithOptions, при возврате из фона — willEnterForeground. Разработчик может использовать эти различия для выбора стратегии восстановления состояния.
| Сценарий | Путь перехода | Коллбэки iOS | Коллбэки Android |
|---|---|---|---|
| Холодный старт | Not Running → Active | didFinishLaunching → didBecomeActive | onCreate → onStart → onResume |
| Возврат из фона | Background → Active | willEnterForeground → didBecomeActive | onRestart → onStart → onResume |
| Возврат из Suspended | Suspended → Active | willEnterForeground → didBecomeActive | onRestart → onStart → onResume |
| После прерывания | Inactive → Active | didBecomeActive | onResume |
Важное замечание: при возврате из Suspended iOS не вызывает didFinishLaunchingWithOptions, так как приложение уже было загружено в память. Это означает, что код инициализации, размещённый в этом методе, не выполняется повторно. Разработчики часто забывают об этом и переносят критическую логику в applicationWillEnterForeground или applicationDidBecomeActive для обоих сценариев.
В Android аналогом Active является состояние Activity после вызова onResume(). Activity считается активной, когда она находится на переднем плане и принимает пользовательский ввод. Это состояние соответствует вершине Activity-стека. Если другое Activity появляется поверх (даже частично), текущее Activity переходит в состояние onPause — аналог iOS Inactive.
Ключевое отличие Android — несколько Activity могут быть активными одновременно в multi-window режиме (split screen, freeform). В этом случае Activity, с которым взаимодействует пользователь, считается активным, а соседнее — приостановленным (onPause). iOS не поддерживает multi-window на iPhone, только на iPad через UIScene.
class MainActivity : AppCompatActivity() {
override fun onResume() {
super.onResume()
// Приложение стало активным — возобновляем задачи
resumeCameraPreview()
startLocationUpdates()
activateSensors()
}
override fun onPause() {
super.onPause()
// Приложение теряет активность — освобождаем ресурсы
releaseCamera()
stopLocationUpdates()
deactivateSensors()
}
private fun resumeCameraPreview() {
// Запуск предпросмотра камеры (требует разрешения)
cameraProvider?.unbindAll()
cameraProvider?.bindToLifecycle(
this,
cameraSelector,
preview,
imageAnalyzer
)
}
private fun startLocationUpdates() {
val locationRequest = LocationRequest.Builder(
Priority.PRIORITY_HIGH_ACCURACY, 5000
).build()
locationClient.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper()
)
}
}В коде показана обработка Active в Android через onResume/onPause. onResume возобновляет работу с камерой, геолокацией и датчиками — ресурсы, которые должны быть активны только когда приложение видимо пользователю. onPause освобождает эти ресурсы, чтобы не расходовать батарею. CameraX lifecycle-aware API автоматически приостанавливает предпросмотр при onPause.
Первое правило — не выполняйте тяжёлые операции в applicationDidBecomeActive или onResume. Загрузка данных, парсинг JSON, работа с базой данных — всё это должно быть асинхронным и не блокировать главный поток. Используйте GCD (DispatchQueue) в iOS и Coroutines в Kotlin для фоновых задач. Главный поток должен только обновлять UI и запускать асинхронные операции.
Второе правило — синхронизируйте состояние при каждом возврате в Active. Пользователь мог изменить настройки в системном приложении, получить push-уведомление или обновить данные в другом приложении. Проверяйте актуальность кеша при переходе в Active — возможно, данные устарели за время отсутствия пользователя.
Третье правило — не полагайтесь на Active как на единственное состояние. Приложение может пропустить Active и перейти напрямую из Not Running в Background (если запущено в фоновом режиме). На iOS это происходит при запуске через push-уведомление с опцией content-available. На Android — при запуске через BroadcastReceiver. Всегда проверяйте текущее состояние перед выполнением UI-операций.
Четвёртое правило — используйте Activity Result API на Android вместо onActivityResult. Это позволяет обрабатывать результат вызова камеры, галереи или разрешений прямо в Active-состоянии без потери данных при пересоздании Activity. Для iOS используйте async/await с UIApplication.shared.open для системных диалогов.
import UIKit
final class ActiveStateManager {
static let shared = ActiveStateManager()
private var isActive = false
func setActive(_ active: Bool) {
isActive = active
if active {
NotificationCenter.default.post(name: .appDidBecomeActive, object: nil)
}
}
func performWhenActive(_ block: @escaping () -> Void) {
if isActive {
block()
} else {
// Отложить выполнение до возврата в Active
NotificationCenter.default.addObserver(
forName: .appDidBecomeActive,
object: nil,
queue: .main
) { _ in
block()
}
}
}
}
extension Notification.Name {
static let appDidBecomeActive = Notification.Name("appDidBecomeActive")
}В коде показан менеджер состояния Active, который позволяет другим компонентам приложения проверять текущее активное состояние. performWhenActive либо выполняет блок немедленно, если приложение активно, либо откладывает выполнение до возврата в Active. Это полезно для сервисов, которые должны выполнить действие после того, как пользователь вернётся в приложение.
Часто задаваемые вопросы
Метод вызывается каждый раз, когда приложение переходит в активное состояние: при первом запуске, при возврате из фона, после закрытия Control Center или Notification Center, после завершения звонка. В нормальной сессии может быть вызван 5–10 раз в зависимости от действий пользователя. Не размещайте в этом методе одноразовую инициализацию.
Visible — неофициальный термин, означающий, что приложение видимо на экране, но может не принимать события (например, частично перекрыто другим окном на iPad). Active — официальное состояние, в котором приложение и видимо, и интерактивно. На iPhone Visible приложение всегда Active, на iPad возможна ситуация Visible + Inactive.
willEnterForeground вызывается при возврате из фона, но приложение ещё не активно — оно находится в Inactive. didBecomeActive вызывается после того, как приложение стало полностью интерактивным. Если нужно выполнить действие до того, как пользователь увидит интерфейс — используйте willEnterForeground. Если после отображения — didBecomeActive.
Нет. Active подразумевает, что приложение находится на переднем плане и отображается на экране. Без видимого UI приложение может быть в Background или Suspended. Исключение — iPad multi-window, где одно окно может быть активным, а другое — нет, но оба видимы. VoiceOver и диктофон не меняют это правило.
На iOS симуляторе нажмите Cmd+Shift+H для перехода на Home Screen (приложение уходит в Background), затем снова нажмите на иконку приложения. Используйте Cmd+L для блокировки экрана (willResignActive) и разблокировки (didBecomeActive). Для тестирования Inactive вызовите Control Center (Cmd+Shift+; для macOS-клавиатуры) или Notification Center.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также