'SwiftUI 5.5 Timeout After X of Inactivity

Unlike tracking a user's session via them logging in, I need to find a way to timeout the user going through a multi-page registration process after x amount of time of inactivity.

The registration process uses an observable class to store the various entered values in memory as the user is going through the process. Each page of the registration is within a NavigationView, with navigation links on each page that takes the user to the next screen.

I can't just timeout the entire registration if they are actively going through it because for some it may take a few minutes, whereas for someone who has a disability, it might take a lot longer. As long as there is some sort of activity I want to ensure no timeout.

However, if the user is on page 2 for example, and then gets a phone call (sending the app into the background), forgets about it until the next day (again, just an example), and then comes back to the app to keep going, I want to show an alert that upon the user tapping OK would clear out the values within the observable class and take them back to a certain page of the app.

While there are a lot of suggestions on something like this for UIKit (not really exactly for this, but....) I haven't seen anything to do this using the latest SwiftUI for iOS 15+.

I need the timer to start when the user begins the registration process, and not be interrupted if the app goes into the background. If the user quits the app, there's nothing I can do about that, but if the app remains open, in either the foreground OR the background, I need to time them out of the registration process, after x amount of time of inactivity.



Solution 1:[1]

@jnpdx Approach definitely works.
I can add an approach without Timer, using DispatchQueue, which also works when the app goes into background:

// global timer var for every view
var timerWorkItem: DispatchWorkItem?

struct ContentView: View {
    
    @State private var message = "not started"
    
    var body: some View {
        
        VStack {
            Text(message)
                .font(.title)
            
            Button("Start") {
                message = "in process"
                
                // set timer
                timerWorkItem = DispatchWorkItem {
                    message = "timed out!"
                }
                DispatchQueue.main.asyncAfter(deadline: .now() + 10, execute: timerWorkItem!)
            }
            .padding()
            
            Button("Do something") {
                // cancel old item
                timerWorkItem?.cancel()
                // set new item
                timerWorkItem = DispatchWorkItem {
                    message = "timed out!"
                }
                DispatchQueue.main.asyncAfter(deadline: .now() + 10, execute: timerWorkItem!)
            }
            .padding()
                        
        }
        .buttonStyle(.bordered)
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ChrisR