Active(アクティブ) — iOSアプリケーションのライフサイクルにおけるアクティブ状態であり、アプリがフォアグラウンドにあり、タッチイベントを受信し、ユーザーと対話します。Active状態がどのように機能するか、UIApplicationDelegateのどのメソッドがそれを担当するか、SwiftでActiveとInactive間の遷移を正しく処理する方法について解説します。
重要ポイント
Active — モバイルアプリケーションのライフサイクルにおける状態で、アプリがフォアグラウンドにあり、デバイスの画面に表示され、ユーザーと積極的に対話しています。この状態では、アプリはすべてのタッチイベント、キー入力、加速度計とジャイロスコープのデータを受信し、インターフェースのレンダリングのためにグラフィックプロセッサへの完全なアクセス権を持ちます。
iOSでは、Active状態は5状態のライフサイクルモデルの一部です:Not Running → Inactive → Active → Inactive → Background → Suspended → Not Running。Androidでは、Activityがスタックの最上位にありユーザー入力を受け付けるonResume呼び出し後のActivity状態に相当します。ActiveはUIが完全にインタラクティブである唯一の状態であり、ジェスチャー、スクロール、タップ、アニメーションに反応します。
システムはActive状態のアプリにCPUとRAMの最高優先度を提供します。つまり、リソースが不足してもシステムはそのようなアプリを終了しません — 最初にバックグラウンドプロセスと一時停止されたプロセスがアンロードされます。ただし、アプリはバッテリーを消耗させず、CPUのスロットリングを引き起こさないために、リソースを効率的に使用する必要があります。
ユーザーにとって、Activeはアプリを操作する通常の状態です。ユーザーはインターフェースを見て、ボタンを押し、フォームに入力し、フィードをスクロールできます。この状態の中断(電話、通知、Control Centerを開くための上スワイプ)はアプリをInactiveに移行させ、その後Activeに戻るかBackgroundに移行する可能性があります。
iOSはUIApplicationMainを使用して状態を管理します。Activeへの遷移時に、システムはapplicationDidBecomeActiveを呼び出します。SwiftUIの場合、同等のメカニズムはEnvironmentを通じたscenePhaseの監視です。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はiPadでの複数ウィンドウサポートのためにUISceneDelegateを導入しました。この場合、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()
}
}
}コードはUIKitでのActiveの正しい処理を示しています。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() {
// ネットワークリクエスト、アニメーションの再開
}
private func pauseWork() {
// 時間に敏感なタスクの一時停止
}
private func saveState() {
// アプリケーション状態の保存
}
}SwiftUIでは、scenePhaseがアプリケーション状態に関する唯一の情報源です。onChangeを使用すると、各遷移時にアクションを実行できます。scenePhaseはiOS 14+およびSwiftUI Lifecycleでのみ利用可能であることに注意してください。SwiftUI画面を持つUIKitアプリケーションの場合は、UIApplicationDelegateを使用したアプローチを使用してください。
Activeにはいくつかの方法で到達します。1つ目は明白なコールドスタートです:ユーザーがアイコンをタップし、アプリがNot RunningからInactiveを経てActiveに移行します。2つ目はバックグラウンドからの復帰です:ユーザーがApp Switcherを介してアプリに戻ると、アプリはInactiveを経由してActiveになります。3つ目は一時的な中断からの復帰です:ユーザーが電話を終了し、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に相当するのはonResume()呼び出し後のActivity状態です。Activityはフォアグラウンドにありユーザー入力を受け付けているときにアクティブと見なされます。この状態はActivityスタックの最上位に対応します。別のActivityが(部分的にでも)上に表示されると、現在のActivityはonPause状態に移行します — iOSのInactiveに相当します。
Androidの重要な違いは、マルチウィンドウモード(分割画面、フリーフォーム)で複数のActivityが同時にアクティブになる可能性があることです。この場合、ユーザーが対話しているActivityがアクティブと見なされ、隣のActivityは一時停止(onPause)されます。iOSは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()
)
}
}コードはonResume/onPauseを通じたAndroidでのActive処理を示しています。onResumeはカメラ、地理位置情報、センサーとの連携を再開します — これらはアプリがユーザーに表示されているときのみアクティブであるべきリソースです。onPauseはこれらのリソースを解放してバッテリーを節約します。CameraXのライフサイクル対応APIはonPause時に自動的にプレビューを一時停止します。
第一のルール — applicationDidBecomeActiveやonResumeで重い操作を実行しないでください。データの読み込み、JSONのパース、データベース操作 — これらはすべて非同期で行い、メインスレッドをブロックしてはいけません。バックグラウンドタスクにはiOSではGCD(DispatchQueue)、KotlinではCoroutinesを使用してください。メインスレッドはUIの更新と非同期操作の開始のみを行うべきです。
第二のルール — Activeに戻るたびに状態を同期してください。ユーザーがシステム設定を変更したり、プッシュ通知を受け取ったり、別のアプリでデータを更新したりした可能性があります。Activeへの移行時にキャッシュの有効性を確認してください — ユーザーが離れていた間にデータが古くなっている可能性があります。
第三のルール — Activeだけに依存しないでください。アプリはActiveをスキップして、Not Runningから直接Backgroundに移行する可能性があります(バックグラウンドモードで起動された場合)。iOSでは、content-availableオプション付きのプッシュ通知を介して起動された場合に発生します。Androidでは、BroadcastReceiverを介して起動された場合に発生します。UI操作を実行する前に現在の状態を常に確認してください。
第四のルール — AndroidではonActivityResultの代わりにActivity Result APIを使用してください。これにより、Activityの再作成時にデータを失うことなく、Active状態でカメラ、ギャラリー、または権限の呼び出し結果を処理できます。iOSでは、システムダイアログにUIApplication.shared.openとasync/awaitを使用してください。
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マルチウィンドウで、一方のウィンドウがアクティブで他方がアクティブでない場合がありますが、両方とも表示されています。VoiceOverとボイスレコーダーはこのルールを変更しません。
iOSシミュレーターでCmd+Shift+Hを押してホーム画面に移動し(アプリはBackgroundに移行します)、再度アプリアイコンをタップします。Cmd+Lを使用して画面をロック(willResignActive)およびロック解除(didBecomeActive)します。Inactiveをテストするには、Control Center(macOSキーボードの場合はCmd+Shift+;)またはNotification Centerを呼び出してください。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。