Skip to content

Foreground Lock Timeout

The foreground lock timeout feature automatically locks the application after a period of user inactivity while the app is in the foreground. This protects sensitive data when a user leaves the app unattended without explicitly locking or moving it to the background.

When the inactivity threshold defined in the passcode policy is reached, the SDK locks the session and presents the passcode screen, requiring the user to re-enter passcode before continuing.

Prerequisites

To enable this feature, the administrator must configure a Foreground Lock Timeout value under Client Settings in the mobile services cockpit.

App Level Changes

1. Add MyApplication.swift

Create a new file MyApplication.swift in your app target and subclass UIApplication to intercept touch events and report activity to the SDK.

import UIKit
import SAPFioriFlows

class MyApplication: UIApplication {
    override func sendEvent(_ event: UIEvent) {
        super.sendEvent(event)

        // Only update last-activity time when the session is open.
        // Guarding on .opened prevents the monitor running during onboarding, restore,
        // or while the passcode screen is already visible (locked state).
        if case .opened = OnboardingSessionManager.shared?.state {
            OnboardingSessionManager.shared?.passcodePolicyManager?.manageForegroundPasscodeTimeout()
        }
    }
}

2. Add main.swift

Create a new file main.swift in your app target. This replaces the @UIApplicationMain / @main annotation.

import UIKit
import SAPFioriFlows

UIApplicationMain(
    CommandLine.argc,
    CommandLine.unsafeArgv,
    NSStringFromClass(MyApplication.self),
    NSStringFromClass(AppDelegate.self)
)

Note: Remove @UIApplicationMain from AppDelegate.swift because main.swift will serve as the entry point.

3. Add CustomPasscodePolicyManagerDelegate and Wire It in AppDelegate

Create a delegate class that responds when the foreground inactivity timeout elapses. It locks the session and calls unlock to present the passcode screen.

import SAPFioriFlows
import SAPCommon

class CustomPasscodePolicyManagerDelegate: PasscodePolicyManagerDelegate {

    private let logger = Logger.shared(named: "SAP.Flows.CustomPasscodePolicyManagerDelegate")

    func didEnforceForegroundPasscodeTimeout() {
        logger.debug("Foreground timeout elapsed — locking session.")

        OnboardingSessionManager.shared.lock { [weak self] lockError in
            if let lockError = lockError {
                self?.logger.error("lock() failed on foreground timeout.", error: lockError)
                return
            }
            OnboardingSessionManager.shared.unlock { unlockError in
                guard let unlockError = unlockError else {
                    return
                }
                AppDelegate.shared.onboardingErrorHandler?.handleUnlockingError(unlockError)
            }
        }
    }
}

Then in AppDelegate.initializeOnboarding(), after sessionManager is created, set the delegate:

func initializeOnboarding() {
    let presentationDelegate = ApplicationUIManager(window: window!)
    onboardingErrorHandler = OnboardingErrorHandler()
    sessionManager = OnboardingSessionManager(presentationDelegate: presentationDelegate, flowProvider: flowProvider, onboardingIDManager: MultiUserOnboardingIDManager(), delegate: onboardingErrorHandler)
    presentationDelegate.showSplashScreenForOnboarding { _ in }

    // Set the foreground delegate so PasscodePolicyManager can notify the app
    // when the inactivity timer elapses.
    let foregroundDelegate = CustomPasscodePolicyManagerDelegate()
    sessionManager.passcodePolicyManager?.delegate = foregroundDelegate

    onboardUser()
}

Note: The delegate must be set after sessionManager is created. The foreground monitor itself only arms after onboarding completes, when the passcode policy is downloaded from SAP Mobile Services and initialize(passcodePolicy:) is called internally.

visionOS App Level Changes

visionOS apps use SwiftUI with @main — no MyApplication.swift or main.swift is needed. Instead, use simultaneousGesture on ContentView to capture user activity.

In your App definition, update the .app case inside WindowGroup to add the gesture:

case .app:
    ContentView()
        .simultaneousGesture(
            DragGesture(minimumDistance: 0)
                .onChanged { _ in
                    if case .opened = OnboardingSessionManager.shared?.state {
                        OnboardingSessionManager.shared?.passcodePolicyManager?.manageForegroundPasscodeTimeout()
                    }
                }
        )

Note: The .opened state guard ensures the monitor is only updated when the session is active — not during onboarding, restore, or while the passcode screen is visible.

The CustomPasscodePolicyManagerDelegate setup in AppDelegate.initializeOnboarding() is the same as for apps using UIApplication (Step 3 above).


Last update: July 13, 2026