Active — stato attivo del ciclo di vita di un'applicazione iOS, in cui si trova in primo piano, riceve eventi tattili e interagisce con l'utente. Scopriamo come funziona lo stato Active, quali metodi del delegato UIApplicationDelegate lo gestiscono e come gestire correttamente le transizioni tra Active e Inactive in Swift.
Punti chiave
Active — stato del ciclo di vita di un'applicazione mobile in cui si trova in primo piano, viene visualizzata sullo schermo del dispositivo e interagisce attivamente con l'utente. In questo stato l'applicazione riceve tutti gli eventi tattili, le pressioni dei tasti, i dati dall'accelerometro e dal giroscopio, e ha pieno accesso al processore grafico per il rendering dell'interfaccia.
Su iOS lo stato Active fa parte del modello di ciclo di vita a cinque stati: Not Running → Inactive → Active → Inactive → Background → Suspended → Not Running. Su Android l'analogo è lo stato di Activity dopo la chiamata a onResume, quando Activity si trova in cima allo stack e riceve input dall'utente. Active è l'unico stato in cui l'UI è completamente interattiva e risponde a gesti, scroll, pressioni e animazioni.
Il sistema fornisce all'applicazione in Active la massima priorità di CPU e RAM. Ciò significa che il sistema non terminerà tale applicazione in caso di carenza di risorse — verranno prima scaricati i processi in background e sospesi. Tuttavia l'applicazione deve utilizzare le risorse in modo efficiente per non scaricare la batteria e causare throttling della CPU.
Per l'utente Active è lo stato normale di lavoro con l'applicazione. L'utente vede l'interfaccia, può premere pulsanti, compilare moduli, scorrere il feed. Qualsiasi interruzione di questo stato (chiamata, notifica, swipe verso l'alto per Control Center) porta l'applicazione in Inactive, dopodiché può tornare in Active o andare in Background.
iOS utilizza UIApplicationMain per gestire lo stato. Alla transizione in Active il sistema chiama applicationDidBecomeActive. Per SwiftUI il meccanismo analogo è l'osservazione di scenePhase tramite Environment. Android utilizza onResume come indicatore di attività di Activity in primo piano. Entrambi gli approcci garantiscono che l'applicazione riceva la notifica del cambio di stato e possa adattare il proprio comportamento.
| Piattaforma | Metodo/Evento | Swift (UIKit) | SwiftUI | Android (Kotlin) |
|---|---|---|---|---|
| iOS | Transizione in Active | applicationDidBecomeActive | scenePhase == .active | — |
| iOS | Uscita da Active | applicationWillResignActive | scenePhase == .inactive | — |
| Android | Transizione in Active | — | — | onResume() |
| Android | Uscita da Active | — | — | onPause() |
In iOS lo stato Active viene gestito tramite UIApplicationDelegate. Il metodo principale è applicationDidBecomeActive(_:). Viene chiamato al primo avvio dell'applicazione e al ritorno da Inactive. Questo metodo è il luogo ideale per riprendere le attività sospese all'ingresso in Inactive: avviare animazioni, riprendere timer, riavviare sensori, verificare aggiornamenti dati sul server.
Da iOS 13 Apple ha introdotto UISceneDelegate per supportare più finestre su iPad. In questo caso applicationDidBecomeActive viene sostituito da sceneDidBecomeActive per ogni scena individuale. Le applicazioni che supportano un solo schermo possono continuare a utilizzare UIApplicationDelegate. Entrambi gli approcci vengono chiamati nel momento in cui l'applicazione o la scena diventa attiva.
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
// Applicazione diventata attiva — riprendiamo le attività
func applicationDidBecomeActive(_ application: UIApplication) {
resumeAnimations()
restartTimers()
refreshDataIfNeeded()
startObservingSensors()
}
// L'applicazione perde attività — sospendiamo
func applicationWillResignActive(_ application: UIApplication) {
pauseAnimations()
stopTimers()
saveDraftData()
}
private func resumeAnimations() {
UIView.animate(withDuration: 0.3) {
// Ripresa delle animazioni UI
}
}
private func refreshDataIfNeeded() {
let lastRefresh = UserDefaults.standard.object(forKey: "lastRefresh") as? Date ?? .distantPast
if Date().timeIntervalSince(lastRefresh) > 300 {
fetchDataFromServer()
}
}
}Il codice mostra la corretta gestione di Active in UIKit. applicationDidBecomeActive riprende animazioni, timer e verifica se è necessario un aggiornamento dati. applicationWillResignActive sospende tutto ciò che potrebbe consumare risorse e salva le bozze. Tale coppia di metodi garantisce che l'applicazione reagisca correttamente al cambio di stato.
In SwiftUI non c'è AppDelegate — la gestione dello stato avviene tramite Environment<ScenePhase>. Il valore .active viene impostato quando la scena è in primo piano e interattiva. SwiftUI riavvia automaticamente animazioni e aggiornamenti al ritorno in Active. Lo sviluppatore deve solo sottoscriversi a onChange per eseguire effetti collaterali.
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("La scena è diventata attiva")
resumeWork()
case .inactive:
print("La scena è diventata inattiva")
pauseWork()
case .background:
print("La scena è andata in background")
saveState()
@unknown default:
break
}
}
}
private func resumeWork() {
// Ripresa delle richieste di rete, animazioni
}
private func pauseWork() {
// Sospensione delle attività sensibili al tempo
}
private func saveState() {
// Salvataggio dello stato dell'applicazione
}
}In SwiftUI scenePhase è l'unica fonte di verità sullo stato dell'applicazione. onChange permette di eseguire azioni a ogni transizione. È importante ricordare che scenePhase è disponibile solo su iOS 14+ e in SwiftUI Lifecycle. Per applicazioni UIKit con schermate SwiftUI utilizzate l'approccio con UIApplicationDelegate.
Active si raggiunge attraverso diversi percorsi. Il primo e ovvio — avvio a freddo: l'utente preme l'icona, l'applicazione passa da Not Running attraverso Inactive ad Active. Il secondo — ritorno dallo sfondo: l'utente torna all'applicazione tramite App Switcher, l'applicazione passa attraverso Inactive e diventa Active. Il terzo — ritorno da un'interruzione temporanea: l'utente termina una chiamata, chiude Control Center o risponde a una notifica — l'applicazione torna da Inactive ad Active.
Not Running → Inactive → Active — avvio a freddo. Background → Inactive → Active — ritorno dallo sfondo. Inactive → Active — ritorno da interruzione temporanea. In ogni caso applicationDidBecomeActive viene chiamato, ma il contesto può differire. All'avvio a freddo prima di Active viene chiamato didFinishLaunchingWithOptions, al ritorno dallo sfondo — willEnterForeground. Lo sviluppatore può utilizzare queste differenze per scegliere la strategia di ripristino dello stato.
| Scenario | Percorso di transizione | Callback iOS | Callback Android |
|---|---|---|---|
| Avvio a freddo | Not Running → Active | didFinishLaunching → didBecomeActive | onCreate → onStart → onResume |
| Ritorno dallo sfondo | Background → Active | willEnterForeground → didBecomeActive | onRestart → onStart → onResume |
| Ritorno da Suspended | Suspended → Active | willEnterForeground → didBecomeActive | onRestart → onStart → onResume |
| Dopo interruzione | Inactive → Active | didBecomeActive | onResume |
Nota importante: al ritorno da Suspended iOS non chiama didFinishLaunchingWithOptions, poiché l'applicazione era già stata caricata in memoria. Ciò significa che il codice di inizializzazione inserito in questo metodo non viene eseguito nuovamente. Gli sviluppatori spesso lo dimenticano e spostano la logica critica in applicationWillEnterForeground o applicationDidBecomeActive per entrambi gli scenari.
In Android l'analogo di Active è lo stato di Activity dopo la chiamata a onResume(). Activity è considerata attiva quando si trova in primo piano e riceve input dall'utente. Questo stato corrisponde alla cima dello stack di Activity. Se un'altra Activity appare sopra (anche parzialmente), l'Activity corrente passa allo stato onPause — analogo di iOS Inactive.
La differenza chiave di Android — più Activity possono essere attive contemporaneamente in modalità multi-window (split screen, freeform). In questo caso l'Activity con cui l'utente interagisce è considerata attiva, mentre quella adiacente è in pausa (onPause). iOS non supporta multi-window su iPhone, solo su iPad tramite UIScene.
class MainActivity : AppCompatActivity() {
override fun onResume() {
super.onResume()
// Applicazione diventata attiva — riprendiamo le attività
resumeCameraPreview()
startLocationUpdates()
activateSensors()
}
override fun onPause() {
super.onPause()
// L'applicazione perde attività — liberiamo risorse
releaseCamera()
stopLocationUpdates()
deactivateSensors()
}
private fun resumeCameraPreview() {
// Avvio dell'anteprima della fotocamera (richiede autorizzazione)
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()
)
}
}Il codice mostra la gestione di Active in Android tramite onResume/onPause. onResume riprende il lavoro con la fotocamera, la geolocalizzazione e i sensori — risorse che dovrebbero essere attive solo quando l'applicazione è visibile all'utente. onPause rilascia queste risorse per non consumare la batteria. CameraX lifecycle-aware API sospende automaticamente l'anteprima in onPause.
Prima regola — non eseguire operazioni pesanti in applicationDidBecomeActive o onResume. Caricamento dati, parsing JSON, lavoro con il database — tutto deve essere asincrono e non bloccare il thread principale. Utilizzate GCD (DispatchQueue) su iOS e Coroutines in Kotlin per attività in background. Il thread principale dovrebbe solo aggiornare l'UI e avviare operazioni asincrone.
Seconda regola — sincronizzate lo stato a ogni ritorno in Active. L'utente potrebbe aver modificato le impostazioni nell'applicazione di sistema, ricevuto una notifica push o aggiornato dati in un'altra applicazione. Verificate la validità della cache al passaggio in Active — i dati potrebbero essere obsoleti durante l'assenza dell'utente.
Terza regola — non affidatevi ad Active come unico stato. L'applicazione potrebbe saltare Active e passare direttamente da Not Running a Background (se avviata in modalità background). Su iOS ciò accade all'avvio tramite notifica push con opzione content-available. Su Android — all'avvio tramite BroadcastReceiver. Verificate sempre lo stato corrente prima di eseguire operazioni UI.
Quarta regola — utilizzate Activity Result API su Android invece di onActivityResult. Ciò consente di gestire il risultato della chiamata alla fotocamera, galleria o permessi direttamente nello stato Active senza perdita di dati alla ricreazione di Activity. Per iOS utilizzate async/await con UIApplication.shared.open per i dialoghi di sistema.
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 {
// Rimandare l'esecuzione fino al ritorno in Active
NotificationCenter.default.addObserver(
forName: .appDidBecomeActive,
object: nil,
queue: .main
) { _ in
block()
}
}
}
}
extension Notification.Name {
static let appDidBecomeActive = Notification.Name("appDidBecomeActive")
}Il codice mostra un gestore dello stato Active che permette ad altri componenti dell'applicazione di verificare lo stato attivo corrente. performWhenActive esegue il blocco immediatamente se l'applicazione è attiva, oppure rimanda l'esecuzione fino al ritorno in Active. Utile per servizi che devono eseguire un'azione dopo che l'utente torna all'applicazione.
Domande frequenti
Il metodo viene chiamato ogni volta che l'applicazione passa allo stato attivo: al primo avvio, al ritorno dallo sfondo, dopo aver chiuso Control Center o Notification Center, dopo la fine di una chiamata. In una sessione normale può essere chiamato 5–10 volte a seconda delle azioni dell'utente. Non inserite inizializzazioni una tantum in questo metodo.
Visible — termine non ufficiale che significa che l'applicazione è visibile sullo schermo ma potrebbe non ricevere eventi (ad esempio, parzialmente coperta da un'altra finestra su iPad). Active — stato ufficiale in cui l'applicazione è sia visibile che interattiva. Su iPhone un'applicazione Visible è sempre Active, su iPad è possibile la situazione Visible + Inactive.
willEnterForeground viene chiamato al ritorno dallo sfondo, ma l'applicazione non è ancora attiva — si trova in Inactive. didBecomeActive viene chiamato dopo che l'applicazione è diventata completamente interattiva. Se è necessario eseguire un'azione prima che l'utente veda l'interfaccia — utilizzate willEnterForeground. Se dopo la visualizzazione — didBecomeActive.
No. Active implica che l'applicazione sia in primo piano e venga visualizzata sullo schermo. Senza UI visibile l'applicazione può essere in Background o Suspended. Eccezione — iPad multi-window, dove una finestra può essere attiva e un'altra no, ma entrambe sono visibili. VoiceOver e dittafono non cambiano questa regola.
Sul simulatore iOS premete Cmd+Shift+H per andare alla Home Screen (l'applicazione va in Background), poi premete di nuovo sull'icona dell'applicazione. Utilizzate Cmd+L per bloccare lo schermo (willResignActive) e sbloccarlo (didBecomeActive). Per testare Inactive chiamate Control Center (Cmd+Shift+; per tastiera macOS) o Notification Center.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche