1

I have a timer running in my UIApplication subclass, that is should send the user to a certain ViewController when it runs out.

I am able to instantiate the ViewController I want to go to...

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "StartVC")

...but I do not know how to actually present it. Inside AppDelegate I would be able to do window.rootViewController etc. But this is not available in my UIApplication subclass.

I have also tried to self.windows[0].rootViewController but that is always just the first ViewController, that was present when the app was started. Same with self.keyWindow.rootViewController. And I honestly do not know what both of there properties are.

Full code for context:

import Foundation
import UIKit

class MyApplication: UIApplication {

    var inactivityTimer: Timer!

    override init() {
        super.init()
        restartInactivityTimer()
    }

    @objc func timerExceeded() {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let vc = storyboard.instantiateViewController(withIdentifier: "StartVC")
        //...here I would need to present "vc"
    }

    override func sendEvent(_ event: UIEvent) {
        super.sendEvent(event)
        restartInactivityTimer()
    }

    func restartInactivityTimer() {
        if inactivityTimer != nil { inactivityTimer.invalidate() }
        inactivityTimer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(timerExceeded), userInfo: nil, repeats: false)
    }
}
Marmelador
  • 947
  • 7
  • 27

2 Answers2

0

Implementing an inactivity timer does not require subclassing UIApplication. According to the subclassing notes from the UIApplication documentation, subclassing should only be needed in very rare cases:

Most apps do not need to subclass UIApplication. Instead, use an app delegate to manage interactions between the system and the app.

If your app must handle incoming events before the system does—a very rare situation—you can implement a custom event or action dispatching mechanism. To do this, subclass UIApplication and override the sendEvent(:) and/or the sendAction(:to:from:for:) methods. For every event you intercept, dispatch it back to the system by calling [super sendEvent:event] after you handle the event. Intercepting events is only rarely required and you should avoid it if possible.

As you already mentioned in your question, you can access everything you need from / via the AppDelegate. So, why not handle the inactivity timeout in / via the AppDelegate?

Community
  • 1
  • 1
Rein Spijkerman
  • 604
  • 5
  • 15
  • Because I *do* need to override `sendEvent(:)`. I need to restart the timer every time the user touches the screen. – Marmelador Apr 30 '18 at 05:51
0

I ended up solving it myself like this:

//inside my UIApplication subclass
@objc func timerExceeded() {
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let vc = storyboard.instantiateViewController(withIdentifier: "StartVC") as! StartVC
    self.windows.first?.rootViewController = vc
    self.windows.first?.makeKeyAndVisible()
}
Marmelador
  • 947
  • 7
  • 27