1

I was thinking about using DataFormatter.localizedString... and setting the value to 2 different variables, one for when the app leaves the foreground and when the app comes back to the foreground, and then subtracting them to get the elapsed time to set it to the breakSession.text...but I'm a little new to swift and am unclear how to use the Date() functions. My main goal is to have it so that the breakTimer is running when the app is in the background and pauses when it is in the foreground, but the user should still be able to see the elapsed time. But apparently, timers can't run in the background...Can anyone help?

import UIKit

class HomeViewController: UIViewController {

@IBOutlet weak var focusSession: UILabel!
@IBOutlet weak var breakSession: UILabel!

/** Focus Time **/

var prodMinutes = String() // Productive time data from ViewController.swift arrives and is stored
lazy var intProdSeconds = (60*Int(prodMinutes)!) + 1
var focusTimer = Timer()
var isFocusTimerRunning = false // Make sure only one timer is running at a time

/** Break Time **/

var breakMinutes = String() // Break time data from BreaTimeViewController.swift arrives and is stored
lazy var intBrSeconds = (60*Int(breakMinutes)!) + 1
var breakTimer = Timer()
var isBreakTimerRunning = false

override func viewDidLoad() {
    super.viewDidLoad()

    let state: UIApplicationState = UIApplication.shared.applicationState

    /** Focus Time **/

    if isFocusTimerRunning == false && state == .active {
        runProdTimer()

    }
    else {
        focusTimer.invalidate()
    }

    /** Break Time (This part doesn't work) 

    if isBreakTimerRunning == false && state == .background {
        runBrTimer()
    }
    else {
        breakTimer.invalidate()
    } **/
}

func runProdTimer() {
    focusTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(HomeViewController.updateProdTimer)), userInfo: nil, repeats: true)
    isFocusTimerRunning = true
}

func runBrTimer() {
    breakTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(HomeViewController.updateBrTimer)), userInfo: nil, repeats: true)
    isBreakTimerRunning = true
}

@objc func updateProdTimer() {

    if intProdSeconds < 1 {
        focusTimer.invalidate()
        focusSession.text = "00:00"
    }
    else {
        intProdSeconds -= 1
        focusSession.text = prodTimeString(fTime: TimeInterval(intProdSeconds))
    }

}



@objc func updateBrTimer() {

    if intBrSeconds < 1 {
        breakTimer.invalidate()
        breakSession.text = "00:00"
    }

    else {
        intBrSeconds -= 1
        breakSession.text = brTimeString(bTime: TimeInterval(intBrSeconds))
    }

}

func prodTimeString(fTime: TimeInterval) -> String {

    let prodMinutes = Int(fTime) / 60 % 60
    let prodSeconds = Int(fTime) % 60

    return String(format: "%02d:%02d", prodMinutes, prodSeconds)

}

func brTimeString(bTime: TimeInterval) -> String {

    let brMinutes = Int(bTime) / 60 % 60
    let brSeconds = Int(bTime) % 60

    return String(format: "%02d:%02d", brMinutes, brSeconds)
}

Update: Ok so Sunil Chauhan's answer below sort of worked. I am able to get the time stamp and stuff. However, I ran into another problem and I do not understand why..

@objc func appWillEnterForeground() {
    endDate = Date()
    let secondsPassedInBackground = endDate.timeIntervalSince(beginDate)
    print(secondsPassedInBackground)
    let updateBrTime = intBrSeconds - Int(secondsPassedInBackground) // Updated Break Time after user returns to the app aka foreground
    breakSession.text = brTimeString(bTime: TimeInterval(updateBrTime))
}

In this function, I basically want to take the elapsed time and subtract it from intBrSeconds which is like the "Break" time the user sets for themself. So essentially, this works as a timer as the "Break Timer" runs when the user leaves the app. The label updates properly at first and correctly subtracts the right amount of seconds based on how much time has elapsed. However, after the elapsed time exceeds about 30 seconds or so, the timer starts to increase. So like if it was set to 10:00..it will go down to say, 9:34 but then the next time you leave the app in the background and come back, it will say 9:55 or something greater than 9:34 which is not supposed to happen..how can I fix this?

YungGoat
  • 123
  • 1
  • 9
  • based off your updated ^ info, it doesn't sound like you need to know how much time passed in background, you just need to know how much time has passed from now till when we started tracking break Time, regardless of if app was in background or foreground, is that correct? – Augie Jul 09 '18 at 22:08
  • @Augie No sorry, I need the elapsed time between when the app leaves the foreground and when it comes back to the foreground. Then, I subtract the elapsed time from intBrSeconds and update the label. I need this to happen everytime the app leaves the foreground and comes back to it so the label updates everytime and it appears as a "timer"..I can't do this using Timer() because apparently timers can't run in the background of an app. – YungGoat Jul 10 '18 at 21:45

1 Answers1

2

Try this:

var beginDate = Date()
var endDate = Date()

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(appWentInBackground), name: .UIApplicationDidEnterBackground, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground), name: .UIApplicationWillEnterForeground, object: nil)
}

@objc
func appWentInBackground() {
    beginDate = Date()
}

@objc
func appWillEnterForeground() {
    endDate = Date()
    let secondsPassedInBackground = endDate.timeIntervalSince(beginDate)
    //  secondsPassedInBackground passed while app was in background.
}
Sunil Chauhan
  • 2,074
  • 1
  • 15
  • 33
  • So then secondsPassedInBackground would be my elapsed time value as an integer? – YungGoat Jul 09 '18 at 20:38
  • Thats the seconds elapsed time value as Double. That should work according to me. – Sunil Chauhan Jul 09 '18 at 20:40
  • @YungGoat This answer shows the observers I was talking about, which are the way to detect the app entering th background or foreground. – Chris Jul 09 '18 at 21:43
  • Ok Sunil, I posted an update above ^ can you please check it out?! Your method worked to some extent but I am still facing some other issues – YungGoat Jul 09 '18 at 22:01
  • Hey @Chris can you check out the update I have above ^ ... I am still having some issues. – YungGoat Jul 11 '18 at 17:27
  • @YungGoat I’m travelling without WiFi or data connection but I should be able to work on this in the next couple of days. Sorry for delays! :) – Chris Jul 12 '18 at 11:29
  • @YungGoat Really don't understand what you want to achieve. – Sunil Chauhan Jul 12 '18 at 15:24
  • @SunilChauhan I just want it to work like a timer/ appear as a timer. I'm basically taking the elapsed time (intBrSeconds - secondsPassedInBackground) and setting that number in "minute:second" format as the text of the label....Only problem is that for some reason, the the label's time increases, even though I am subtracting. – YungGoat Jul 16 '18 at 21:01
  • Are you sure you are resetting the `beginDate` whenever the app goes in the background? – Sunil Chauhan Jul 17 '18 at 07:16