Suspended — what it is, app freeze in iOS background

Author: IT Sectr Published: 2026-03-03 Reading time: 11 min

Suspended — a paused state of the iOS application lifecycle in which the app is frozen in memory but does not execute code. We show how Suspended works, what risks background app freezing carries, how iOS manages the eviction of Suspended apps, and how to implement state restoration for seamless recovery after returning from Suspended.

Key Takeaways

  • Suspended — the app is frozen in memory, code is not executed, the UI stack is preserved
  • iOS Suspended — a unique state not present in Android; the process exists but is not active
  • Eviction — when memory is low, Suspended apps are removed first, in-memory data is lost
  • State Restoration — an iOS mechanism for saving and restoring the UI stack after eviction
  • didEnterBackground — the last method that is guaranteed to be called before Suspended

Suspended — what is this state

Suspended is a state of the iOS application lifecycle in which the app resides in the device’s RAM but does not execute any code. This is the final state before full termination: the app transitions to Suspended from Background after completing all background tasks or after a timeout expires. In Suspended, the application is completely frozen — all threads are paused, timers do not work, and there is no network activity.

Suspended is a unique feature of iOS, absent from the standard Android lifecycle. The reason lies in the different process management architectures. iOS preserves the app’s image in memory (similar to desktop hibernation) so that when the user returns, the interface can be instantly restored without a cold start. Android does not have Suspended — a process either exists and can execute code (Background) or is terminated (Not Running), although Android can pause thread execution via LMK.

To the user, Suspended looks like instant restoration: they switch between apps using the App Switcher, and each app opens exactly where they left off. This creates the illusion that all apps are running simultaneously. In reality, most of them are frozen in Suspended. A hot start from Suspended is many times faster than a cold start from Not Running, because the code is already loaded into memory.

How the system manages Suspended

iOS monitors the state of all applications and decides to evict Suspended apps based on available memory. When memory is low, the system begins evicting Suspended apps, starting with those that have been in this state the longest. If memory is still insufficient, the system transitions apps from Background and Inactive into Suspended with subsequent eviction. This process is completely transparent to the user — they simply see the app icon in the App Switcher, which triggers a cold start when tapped.

CharacteristicSuspended (iOS)Background (iOS)Background (Android)
Code executesNoYes (limited)Yes (limited)
In memoryYesYesYes
CPU consumption0%LowLow
Hot startYes — instant restorationYes — via InactiveNo — process may have been killed
TimeoutNo — can be in memory for hours~30 seconds (after beginBackgroundTask)Depends on API version
System evictionWhen memory is lowWhen critically low on memoryLMK (Low Memory Killer)
Return to workFrom App Switcher — instantlyFrom App Switcher — via InactiveCold start
State RestorationRecommendedNot requiredSavedStateHandle

Suspended in iOS: the freezing mechanism

In iOS, Suspended is reached automatically after all background tasks are completed. The system calls applicationDidEnterBackground, gives time to execute beginBackgroundTask (about 30 seconds), then forcibly pauses all threads and transitions the app to Suspended. Objects in memory are preserved, but no code is executed — the application is frozen in its current state.

A critically important point: applicationDidEnterBackground is the last method guaranteed to be called before Suspended. After this, the app receives no notifications about memory eviction. If the user or system kills the app while it is in Suspended, neither applicationWillTerminate nor applicationDidEnterBackground is called again. Therefore, all data saving must happen in applicationDidEnterBackground, not in applicationWillTerminate.

swift
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    // Last guaranteed call before Suspended
    func applicationDidEnterBackground(_ application: UIApplication) {
        // Save everything that needs to survive memory eviction
        savePersistentState()
        saveNavigationStack()

        // Request extra time if needed
        let task = application.beginBackgroundTask {
            application.endBackgroundTask(task)
        }
    }

    // Return from Suspended — hot start
    func applicationWillEnterForeground(_ application: UIApplication) {
        // App was in Suspended, resuming work
        print("Return from Suspended or Background")
    }

    // Full restoration after memory eviction
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        // If this is a cold start after eviction from Suspended —
        // restore state restoration
        return true
    }

    private func savePersistentState() {
        UserDefaults.standard.set(Date(), forKey: "lastActiveDate")
    }

    private func saveNavigationStack() {
        guard let rootVC = window?.rootViewController else { return }
        // Save the current navigation stack
        if let navController = rootVC as? UINavigationController {
            let vcClasses = navController.viewControllers.map { type(of: $0) }
            UserDefaults.standard.set(vcClasses.map { NSStringFromClass($0) }, forKey: "navStack")
        }
    }
}

The code shows the critically important handling of Suspended on iOS. applicationDidEnterBackground is the last guaranteed call. All data saving should happen here: user state, navigation stack, drafts, timers. applicationWillEnterForeground is called when returning from Suspended or Background. didFinishLaunchingWithOptions — only on cold start, when the app was evicted from memory after Suspended.

Suspended in Android — does an analogue exist

In Android, there is no direct analogue of iOS Suspended. Android does not freeze apps in memory while preserving execution context. Instead, Android either keeps the process in the background or terminates it. However, on Android 11+ (API 30), a mechanism called App Freezer was introduced, which suspends background process execution using the SIGSTOP signal. This is functionally similar to Suspended, but with important differences.

App Freezer is part of the Android memory management system. When an app has been in the background for a long time without active notifications, the system sends it SIGSTOP, pausing all threads. When the app returns to the foreground, SIGCONT is sent and execution resumes. The key difference from iOS: App Freezer does not guarantee state preservation — data in memory can be lost if the process is killed during freezing.

On Android, it is recommended to use SavedStateHandle in ViewModel for automatic state preservation during any process termination. SavedStateHandle saves data in a Bundle via onSaveInstanceState, which survives both App Freezer and Process Death. Unlike iOS, where eviction from Suspended is an exceptional situation, on Android, Process Death is normal behavior that should always be expected.

kotlin
// SavedStateHandle — salvation from Process Death on Android
class CheckoutViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    // State that survives the process even after App Freezer
    var currentStep: MutableLiveData<Int> =
        savedStateHandle.getLiveData("checkout_step", 1)

    var cartItems: MutableLiveData<List<CartItem>> =
        savedStateHandle.getLiveData("cart_items", emptyList())

    fun proceedToNextStep() {
        currentStep.value = (currentStep.value ?: 0) + 1
    }

    fun addToCart(item: CartItem) {
        val updatedList = (cartItems.value ?: emptyList()) + item
        cartItems.value = updatedList
        savedStateHandle["cart_items"] = updatedList
    }
}

// Save in onStop in case of App Freezer
class MainActivity : AppCompatActivity() {

    override fun onStop() {
        super.onStop()
        // Save data that must survive freezing
        saveDraftData()
        // Free resources not needed in a frozen state
        releaseHeavyResources()
        // Warn that the app will be frozen
        // (Logging for debugging)
        Log.d("Lifecycle", "Activity stopped — possible App Freeze")
    }
}

The code shows the approach to handling the Suspended analogue on Android. SavedStateHandle in ViewModel automatically saves and restores data during Process Death. onStop is the last guaranteed event before a possible App Freezer or process termination. The checkout form state, cart item list — all this data survives freezing thanks to SavedStateHandle. For heavy resources (bitmaps, DB cursors), onStop is the place to free memory.

State Restoration: recovery after Suspended

State Restoration is a built-in iOS mechanism for saving and restoring UI state after the app is evicted from memory. If the app was in Suspended and the system evicted it, on the next cold start state restoration restores the navigation stack, scroll position, form state, and other UI elements. The user returns to the same screen where they left off.

State Restoration works through the UIViewControllerRestoration and UIStateRestoring protocols. The developer assigns a restorationIdentifier to each ViewController and View they want to restore. When going to the background, iOS encodes the state of these objects. On return after eviction, iOS creates new objects and decodes the saved state. Without state restoration, the user will see a blank screen after a cold start instead of where they left off.

swift
import UIKit

class DetailViewController: UIViewController {

    var itemID: String = ""
    var scrollPosition: CGPoint = .zero

    override func viewDidLoad() {
        super.viewDidLoad()
        restorationIdentifier = "DetailViewController"
        restorationClass = type(of: self)
    }

    override func encodeRestorableState(with coder: NSCoder) {
        super.encodeRestorableState(with: coder)
        coder.encode(itemID, forKey: "itemID")
        coder.encode(scrollPosition, forKey: "scrollPosition")
    }

    override func decodeRestorableState(with coder: NSCoder) {
        super.decodeRestorableState(with: coder)
        if let savedID = coder.decodeObject(forKey: "itemID") as? String {
            itemID = savedID
            loadItem()
        }
        if let savedPosition = coder.decodeCGPoint(forKey: "scrollPosition") {
            scrollPosition = savedPosition
            // Restore position after data loading
        }
    }
}

// AppDelegate — activating State Restoration
func application(
    _ application: UIApplication,
    shouldSaveSecureApplicationState coder: NSCoder
) -> Bool {
    return true
}

func application(
    _ application: UIApplication,
    shouldRestoreSecureApplicationState coder: NSCoder
) -> Bool {
    return true
}

The code shows the implementation of State Restoration on iOS. restorationIdentifier and restorationClass are required for each restorable ViewController. encodeRestorableState/decodeRestorableState save and load data via NSCoder. In AppDelegate, shouldSaveSecureApplicationState and shouldRestoreSecureApplicationState enable encrypted state preservation. On iOS 12+, it is recommended to use secure encoding (NSSecureCoding) for data protection.

Best practices for working with Suspended

The first rule — never assume that the app will return from Suspended. The system can evict the app at any moment. All critically important data must be saved to persistent storage before transitioning to Suspended — that is, in applicationDidEnterBackground or onStop. UserDefaults, Core Data, File Manager — suitable storage options. Memory (variables, properties) is an unreliable storage for data that must survive Suspended.

The second rule — free resources before Suspended. Close file descriptors, release GPU memory (Metal, Core Graphics), close network connections. Although the app does not consume CPU in Suspended, held resources are blocked from other applications. On iOS, you cannot keep open sockets in Suspended — when returning from Suspended, they may be non-functional, causing errors.

The third rule — do not place time-dependent logic expecting to return from Suspended. Timers, callbacks, and network activity cease in Suspended. If the app was in Suspended for several hours, a timer may fire incorrectly upon return. Check data validity when returning — the cache may be stale, and the authorization token may have expired.

The fourth rule — use State Restoration for all screens, especially input forms, scrollable lists, and detail screens. Without state restoration, after returning from an evicted Suspended state, the user will see the app’s initial screen instead of where they left off. This degrades the user experience and forces the user to repeat actions.

swift
import UIKit

// Check: was the app evicted from memory?
func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
    // Check if saved state exists
    if UserDefaults.standard.object(forKey: "navStack") != nil {
        // The app was evicted from Suspended
        // Need to restore state
        restoreNavigationStack()
    } else {
        // Clean cold start from Not Running
        showOnboardingIfNeeded()
    }
    return true
}

private func restoreNavigationStack() {
    guard let savedStack = UserDefaults.standard.array(forKey: "navStack") as? [String],
          let navController = window?.rootViewController as? UINavigationController
    else { return }

    for vcClassName in savedStack {
        if let vcClass = NSClassFromString(vcClassName) as? UIViewController.Type {
            let vc = vcClass.init()
            navController.pushViewController(vc, animated: false)
        }
    }
}

The code shows the practice of determining whether the app was evicted from Suspended. Checking UserDefaults for the presence of a saved navigation stack allows distinguishing a cold start after eviction from a clean cold start. In the first case, the navigation stack is restored; in the second, onboarding or the main screen is shown. This approach complements the built-in State Restoration for cases where NSCoder is insufficient.

Frequently Asked Questions

How long can an app stay in Suspended?

Unlimited — from a few seconds to several days. iOS has no timeout for Suspended. The app will remain in memory until the system decides to evict it due to insufficient resources. In practice, apps stay in Suspended from 15 minutes to several hours, depending on the device’s RAM and the number of active applications.

Is applicationWillTerminate called when evicting from Suspended?

No. applicationWillTerminate is not called when the app is evicted from Suspended. The system simply frees memory without notifying the application. This is another reason why all data saving must occur in applicationDidEnterBackground. applicationWillTerminate is only called when the user manually terminates the app by swiping it out of the App Switcher.

Does Android have an analogue of Suspended?

There is no direct analogue. On Android 11+, App Freezer was introduced, which pauses background processes via SIGSTOP — this is functionally similar to Suspended. However, Android apps should be designed assuming Process Death can happen at any time. Use SavedStateHandle in ViewModel and onSaveInstanceState to save state that will survive both App Freezer and Process Death.

How to check if the app was in Suspended?

In iOS, there is no direct API for checking. An indirect method: check UserDefaults for the presence of saved state in didFinishLaunchingWithOptions. If state exists — the app was evicted from Suspended and is starting cold. If no state exists — it is a clean cold start. In SwiftUI, you can save a flag in scenePhase.background and check it on the next launch.

What is a snapshot in the context of Suspended?

When transitioning to Suspended, iOS takes a snapshot — a screenshot of the current app UI. This screenshot is shown in the App Switcher and when returning to the app (as a “thaw” animation). If the app contains confidential data, the snapshot may expose it. To protect against this, use UIApplication.shouldSnapshotSecureApp (iOS 16+) or apply a blur overlay in applicationDidEnterBackground.

Summary

  • Suspended — the app is frozen in iOS memory, code is not executed, but the UI stack is preserved for instant restoration
  • iOS uniqueness — Suspended is absent in Android; Android uses App Freezer (SIGSTOP) as a partial analogue
  • Eviction — the system evicts Suspended apps as a first priority when memory is low, without notification
  • Saving — applicationDidEnterBackground is the last guaranteed method; all data should be saved here
  • State Restoration — NSCoder-based mechanism for automatic UI restoration after eviction from Suspended
  • Android alternative — SavedStateHandle + onSaveInstanceState to survive Process Death
  • Snapshot — iOS takes a screenshot during Suspended; confidential data must be hidden via blur overlay or secure snapshot

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also