I want to show a hint after the user hasn't touched a screen for 30 seconds. For this restartInactivityTimer() function needs to be called whenever the screen is touched.
Is there a way to discover any screen touch to restart the timer, but without "consuming" it's tap gesture? Do I really need to call restartInactivityTimer() from every onTapGesture (sub)view closure, like in the code snippet below?
struct MyView: View {
@State private var inactivityTimer: Timer?
var body: some View {
VStack {
subView1
subView2
subView3
subView4
}
.onAppear {
restartInactivityTimer()
}
.onTapGesture {
// not reaching here when any subView is touched
restartInactivityTimer()
}
}
func restartInactivityTimer() {
inactivityTimer?.invalidate()
inactivityTimer = Timer.scheduledTimer(withTimeInterval: 30.0, repeats: false) { _ in
showHint()
}
}
var subView1: some View {
// ...
.onTapGesture {
// ... other actions for tapping that subview
restartInactivityTimer()
}
}
// ... other subviews
}
Most solutions I see detect user activity by overriding
UIApplication.sendEvent(Example). We can't do that easily in SwiftUI, but it is possible if you do method swizzling. That is quite hacky though.You can swizzle the methods just before your app launches by writing your own entry point.
The swizzled method is:
Now in your view, you can listen for the notification:
Using
Timer.publishhere is kind of wasteful, since you only need one output from the publisher. Consider using aTaskfor example: