Active — the active state of the iOS application lifecycle, in which it is in the foreground, receives touch events and interacts with the user. Let’s figure out how the Active state works, which UIApplicationDelegate methods are responsible for it, and how to correctly handle transitions between Active and Inactive in Swift.
Key Takeaways
Active — a state of the mobile application lifecycle in which it is in the foreground, displayed on the device screen, and actively interacts with the user. In this state, the application receives all touch events, key presses, accelerometer and gyroscope data, and has full access to the graphics processor for rendering the interface.
On iOS, the Active state is part of the five-state lifecycle model: Not Running → Inactive → Active → Inactive → Background → Suspended → Not Running. On Android, the equivalent is the Activity state after onResume is called, when the Activity is at the top of the stack and accepts user input. Active is the only state in which the UI is fully interactive and responds to gestures, scrolling, taps, and animations.
The system gives an Active application the highest priority for CPU and RAM. This means the system will not terminate such an application when resources are low — background and suspended processes will be unloaded first. However, the application should use resources efficiently to avoid draining the battery and causing CPU throttling.
For the user, Active is the normal state of working with an application. The user sees the interface, can press buttons, fill out forms, and scroll through feeds. Any interruption of this state (a call, notification, swipe up for Control Center) moves the application to Inactive, after which it can return to Active or go to Background.
iOS uses UIApplicationMain to manage state. When transitioning to Active, the system calls applicationDidBecomeActive. For SwiftUI, the equivalent mechanism is observing scenePhase via Environment. Android uses onResume as an indicator of Activity being in the foreground. Both approaches guarantee that the application receives a state change notification and can adapt its behavior.
| Platform | Method/Event | Swift (UIKit) | SwiftUI | Android (Kotlin) |
|---|---|---|---|---|
| iOS | Transition to Active | applicationDidBecomeActive | scenePhase == .active | — |
| iOS | Leaving Active | applicationWillResignActive | scenePhase == .inactive | — |
| Android | Transition to Active | — | — | onResume() |
| Android | Leaving Active | — | — | onPause() |
In iOS, the Active state is handled via UIApplicationDelegate. The main method is applicationDidBecomeActive(_:). It is called on the first launch of the application and when returning from Inactive. This method is the ideal place to resume tasks that were paused when entering Inactive: starting animations, resuming timers, restarting sensors, checking for data updates on the server.
With iOS 13, Apple introduced UISceneDelegate to support multiple windows on iPad. In this case, applicationDidBecomeActive is replaced by sceneDidBecomeActive for each individual scene. Applications that support only a single screen can continue using UIApplicationDelegate. Both approaches are called when the application or scene becomes active.
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()
}
}
}The code shows correct Active handling in UIKit. applicationDidBecomeActive resumes animations, timers, and checks if data updates are required. applicationWillResignActive pauses everything that could consume resources and saves drafts. Such a pair of methods ensures that the application correctly responds to state changes.
SwiftUI has no AppDelegate — state management happens via Environment<ScenePhase>. The .active value is set when the scene is in the foreground and interactive. SwiftUI automatically restarts animations and updates when returning to Active. The developer only needs to subscribe to onChange to perform side effects.
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() {
// Поновлення мережевих запитів, анімацій
}
private func pauseWork() {
// Призупинення чутливих до часу завдань
}
private func saveState() {
// Збереження стану додатка
}
}In SwiftUI, scenePhase is the single source of truth about the application state. onChange allows performing actions on each transition. It is important to remember that scenePhase is only available on iOS 14+ and in the SwiftUI Lifecycle. For UIKit applications with SwiftUI screens, use the UIApplicationDelegate approach.
Active can be reached through several paths. The first and most obvious is a cold start: the user taps the icon, the application goes from Not Running through Inactive to Active. The second is returning from the background: the user switches back to the application via App Switcher, the application passes through Inactive and becomes Active. The third is returning from a temporary interruption: the user ends a call, closes Control Center, or responds to a notification — the application returns from Inactive to Active.
Not Running → Inactive → Active — cold start. Background → Inactive → Active — return from background. Inactive → Active — return from a temporary interruption. In each case, applicationDidBecomeActive is called, but the context may differ. On a cold start, didFinishLaunchingWithOptions is called before Active; when returning from the background, willEnterForeground is called. The developer can use these differences to choose a state restoration strategy.
| Scenario | Transition path | iOS callbacks | Android callbacks |
|---|---|---|---|
| Cold start | Not Running → Active | didFinishLaunching → didBecomeActive | onCreate → onStart → onResume |
| Return from background | Background → Active | willEnterForeground → didBecomeActive | onRestart → onStart → onResume |
| Return from Suspended | Suspended → Active | willEnterForeground → didBecomeActive | onRestart → onStart → onResume |
| After interruption | Inactive → Active | didBecomeActive | onResume |
Important note: when returning from Suspended, iOS does not call didFinishLaunchingWithOptions because the application was already loaded into memory. This means that initialization code placed in this method is not re-executed. Developers often forget this and move critical logic to applicationWillEnterForeground or applicationDidBecomeActive for both scenarios.
In Android, the equivalent of Active is the Activity state after onResume() is called. An Activity is considered active when it is in the foreground and accepts user input. This state corresponds to the top of the Activity stack. If another Activity appears on top (even partially), the current Activity transitions to the onPause state — the equivalent of iOS Inactive.
A key difference in Android is that multiple Activities can be active simultaneously in multi-window mode (split screen, freeform). In this case, the Activity the user is interacting with is considered active, while the neighboring one is paused (onPause). iOS does not support multi-window on iPhone, only on iPad via 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()
)
}
}The code shows Active handling in Android via onResume/onPause. onResume resumes camera, geolocation, and sensor work — resources that should only be active when the application is visible to the user. onPause releases these resources to avoid draining the battery. The CameraX lifecycle-aware API automatically pauses the preview on onPause.
First rule — do not perform heavy operations in applicationDidBecomeActive or onResume. Data loading, JSON parsing, database work — all of this should be asynchronous and not block the main thread. Use GCD (DispatchQueue) on iOS and Coroutines in Kotlin for background tasks. The main thread should only update the UI and launch asynchronous operations.
Second rule — synchronize state on every return to Active. The user might have changed settings in a system application, received a push notification, or updated data in another app. Check cache relevance when transitioning to Active — data might have become stale while the user was away.
Third rule — do not rely on Active as the only state. The application can skip Active and go directly from Not Running to Background (if launched in background mode). On iOS, this happens when launched via a push notification with the content-available option. On Android, when launched via BroadcastReceiver. Always check the current state before performing UI operations.
Fourth rule — use the Activity Result API on Android instead of onActivityResult. This allows handling the result of camera, gallery, or permission calls directly in the Active state without data loss on Activity recreation. For iOS, use async/await with UIApplication.shared.open for system dialogs.
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")
}The code shows an Active state manager that allows other application components to check the current active state. performWhenActive either executes the block immediately if the application is active, or defers execution until returning to Active. This is useful for services that need to perform an action after the user returns to the application.
Frequently Asked Questions
The method is called every time the application transitions to the active state: on first launch, when returning from the background, after closing Control Center or Notification Center, after ending a call. In a normal session it can be called 5–10 times depending on user actions. Do not place one-time initialization in this method.
Visible is an unofficial term meaning the application is visible on the screen but may not receive events (for example, partially covered by another window on iPad). Active is the official state in which the application is both visible and interactive. On iPhone, a Visible application is always Active; on iPad, a Visible + Inactive situation is possible.
willEnterForeground is called when returning from the background, but the application is not yet active — it is in Inactive. didBecomeActive is called after the application becomes fully interactive. If you need to perform an action before the user sees the interface — use willEnterForeground. If after displaying — use didBecomeActive.
No. Active implies that the application is in the foreground and displayed on the screen. Without a visible UI, the application can be in Background or Suspended. The exception is iPad multi-window, where one window can be active and another not, but both are visible. VoiceOver and voice recorder do not change this rule.
On the iOS simulator, press Cmd+Shift+H to go to the Home Screen (the application goes to Background), then tap the application icon again. Use Cmd+L to lock the screen (willResignActive) and unlock (didBecomeActive). To test Inactive, open Control Center (Cmd+Shift+; for macOS keyboard) or Notification Center.
Summary
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також