Let’s say you have a requirement to boot the user back to the login screen if they sit idle too long. Often this is the case with financial Apps, or anything displaying sensitive information.

class UserActivityTimeout {
    
    static var shared = UserActivityTimeout()
    
    private var timer: Timer!
    private var lastActivity = Date()
    private let heartbeatInterval: TimeInterval = Measurement(value: 1, unit: UnitDuration.seconds).value
    private let timeoutInterval: TimeInterval = Measurement(value: 20, unit: UnitDuration.minutes).converted(to: UnitDuration.seconds).value
    
    init() {
        timer = Timer.scheduledTimer(timeInterval: heartbeatInterval, target: self, selector: #selector(heartbeat), userInfo: nil, repeats: true)
    }
    
    public func registerActivity() {
        lastActivity = Date()
    }
    
    @objc func heartbeat() {
        if lastActivity.addingTimeInterval(timeoutInterval) < Date() {
            NotificationCenter.default.post(name: .onShouldLogOut, object: self, userInfo: ["UserInactivityTimeout": true])
        }
    }
}

This class does basically everything for you. On init() it creates a heartbeat(). Every second it checks if lastActivity + timeoutInterval < now. If true the App has been idle for longer than the timeoutInterval, so it posts a Notification for the App to Log Out.

 

RootNavigationController

In your NavigationController (or TabBarController) you’ll listen for this notification.

class RootNavigationController: UINavigationController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        UserActivityTimeout.shared.registerActivity()
        NotificationCenter.default.addObserver(self, selector: #selector(logOut), name: .onShouldLogOut, object: nil)
    }
    
    @objc func logOut() {
        popToRootViewController(animated: false)
        // any other logout cleanup
    }
}

 

All ViewControllers

And in every ViewController add UserActivityTimeout.shared.registerActivity() in viewWillAppear(). You could add it anywhere [scrollViewDidScroll, @IBActions, etc] but I prefer not to clutter up the project. As long as they navigate before 20 minutes they’ll stay logged in.

override func viewWillAppear(_ animated: Bool) {
    UserActivityTimeout.shared.registerActivity()
}

Download Sample Project on GitHub