0

I am using Xcode 9.2/iOS 10 and Swift to detect user inactivity in iOS applications using information based on an excellent tutorial example from https://blog.gaelfoppolo.com/detecting-user-inactivity-in-ios-application-684b0eeeef5b The enhancements I am making are to start the timer when viewDidLoad is called and adding a snooze function in applicationDidTimeout. At compile time, the following error message is displayed: "Instance member 'snoozeTimer' cannot be used on type 'TimerApplication'; did you mean to use a value of this type instead?"

applicationDidTimeout will use snoozeTimer to control whether it needs to have another timer alarm depending on additional logic to be added to the method.

What is the correct way to call startTimer and snoozeTimer?

A code listing is shown below:

main.swift

UIApplicationMain(
   CommandLine.argc,
   UnsafeMutableRawPointer(CommandLine.unsafeArgv)
      .bindMemory(
         to: UnsafeMutablePointer<Int8>.self,
         capacity: Int(CommandLine.argc)),
   NSStringFromClass(TimerApplication.self),
   NSStringFromClass(AppDelegate.self)
)

AppDelegate.swift

import UIKit

import Foundation

extension Notification.Name {

    static let appTimeout = Notification.Name("appTimeout")

}

//@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

   var window: UIWindow?
   var backgroundTaskRunCount : Int = 0


   func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
      // Override point for customization after application launch.

      NotificationCenter.default.addObserver(self,
                                             selector: #selector(AppDelegate.applicationDidTimeout(notification:)),
                                             name: .appTimeout,
                                             object: nil
      )


      return true
   }

.   //other func
 .
  .

@objc func applicationDidTimeout(notification: NSNotification) {
      backgroundTaskRunCount = backgroundTaskRunCount + 1

      print("application did timeout, perform actions. Count=", backgroundTaskRunCount)

      TimerApplication.snoozeTimer()
   }
}

TimerApplication.swift

import UIKit

class TimerApplication: UIApplication {

    private var wakeupInSeconds: TimeInterval {
        // 2 minutes for for timer to fire first time
        return 2 * 60
    }

    private let snoozeInSeconds: TimeInterval = 5

    // the timeout in seconds, after which should perform custom actions
    // such as disconnecting the user
    private var timeoutInSeconds: TimeInterval = 120

   private var idleTimer: Timer?

   // resent the timer because there was user interaction
   private func resetIdleTimer() {
      if timeoutInSeconds > 0 {
         if let idleTimer = idleTimer {
            idleTimer.invalidate()
         }

         idleTimer = Timer.scheduledTimer(timeInterval: timeoutInSeconds,
                                       target: self,
                                       selector: #selector(TimerApplication.timeHasExceeded),
                                       userInfo: nil,
                                       repeats: false
         )
      } else {
        stopTimer()
      }
   }

   // if the timer reaches the limit as defined in timeoutInSeconds, post this notification
   @objc private func timeHasExceeded() {
      NotificationCenter.default.post(name: .appTimeout,
                                      object: nil
      )
   }

   override func sendEvent(_ event: UIEvent) {

      super.sendEvent(event)

      if idleTimer != nil {
         self.resetIdleTimer()
      }

      if let touches = event.allTouches {
         for touch in touches where touch.phase == UITouchPhase.began {
            self.resetIdleTimer()
         }
      }
   }

    func startTimer() {
       self.timeoutInSeconds = wakeupInSeconds
       self.resetIdleTimer()
    }

    func stopTimer() {
        if let idleTimer = idleTimer {
            idleTimer.invalidate()
        }
    }

    func snoozeTimer() {
        self.timeoutInSeconds = snoozeInSeconds
    }
}

ViewController.swift

import UIKit

class ViewController: UIViewController {

   override func viewDidLoad() {
      super.viewDidLoad()
      // Do any additional setup after loading the view, typically from a nib.

    TimerApplication.startTimer()

   }

   override func didReceiveMemoryWarning() {
      super.didReceiveMemoryWarning()
      // Dispose of any resources that can be recreated.
   }


}
TurboPascal
  • 61
  • 2
  • 6

1 Answers1

1

You want to use TimerApplication.shared.snoozeTimer() instead of TimerApplication.snoozeTimer() because snoozeTimer() is an instance method, not class or static method.

You should verify that TimerApplication.shared returns the instance of TimerApplication you're expecting because I've never subclassed a system singleton like this. If that doesn't work, you'll need a way to get a reference to the TimerApplication instance you want.

AdamPro13
  • 7,232
  • 2
  • 30
  • 28