Reputation: 537
I want to save a timer using NSUserDefaults to accomplish my goal. Unfortunately when I try to save the NSDate() using NSUserDefaults, it becomes static and won't continue counting. Am I doing something wrong? Again my goal is have the timer still work regardless of whether it went to background or got terminated. It should save the current time and compare it to how much time is remaining. Here is my code so far. Other code addressing this issue is in OBJ C, and used didEnterBackground which is not necessary.
func startTimer() {
// then set time interval to expirationDate…
expirationDate = NSDate(timeIntervalSinceNow: 86400 )
dateTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(QOTDVC.updateUI(timer:)), userInfo: nil, repeats: true)
RunLoop.current.add(dateTimer, forMode: RunLoopMode.commonModes)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "TimerAdded"), object: nil)
}
func updateUI(timer: Timer)
{
// Call the currentTimeString method which can decrease the time..
let timeString = currentTimeString()
timerLabel.text = "\(timeString)"
}
func currentTimeString() -> DateComponents {
let unitFlags: Set<Calendar.Component> = [.hour, .minute, .second]
let countdown: DateComponents = Calendar.current.dateComponents(unitFlags, from: defaults.object(forKey: currentTime.description) as! Date, to: expirationDate as Date)
print("this is the \(countdown)")
if countdown.second! > 0 {
} else {
dateTimer.invalidate()
}
return countdown
}
Upvotes: 0
Views: 1132
Reputation: 25294
Here is my full code example. I hope this is what you wanted.
import UIKit
protocol UserDefaultsTimerDelegate {
func timerAction(timer: Timer, secondsToEnd:Int)
}
class UserDefaultsTimer {
static var delegate: UserDefaultsTimerDelegate?
class var timerEndDate: Date? {
get {
return UserDefaults.standard.value(forKey: "timerEndDate") as! Date?
}
set (newValue) {
UserDefaults.standard.setValue(newValue, forKey: "timerEndDate")
}
}
class var timerInited: Bool {
get {
if let _ = timerEndDate {
return true
} else {
return false
}
}
}
class func setTimer(date: Date, setDateOnlyIfCurrenTimerIsOver: Bool) {
if !setDateOnlyIfCurrenTimerIsOver {
timerEndDate = date
} else {
if !timerInited {
timerEndDate = date
} else {
let difference = timerEndDate!.seconds(from: Date())
if (difference <= 0) {
timerEndDate = date
}
}
}
}
class func resetTimer() {
timerEndDate = nil
}
class func resumeTimer() {
if timerInited {
NSLog("timer end date:\(timerEndDate)")
let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(action(timer:)), userInfo: nil, repeats: true)
RunLoop.current.add(timer, forMode: RunLoopMode.commonModes)
}
}
@objc class func action(timer: Timer) {
if let timerEndDate = timerEndDate {
let difference = timerEndDate.seconds(from: timer.fireDate)
if let delegate = delegate {
delegate.timerAction(timer: timer, secondsToEnd: difference)
}
NSLog("timer: \(difference)")
if (difference <= 0) {
timer.invalidate()
resetTimer()
}
} else {
timer.invalidate()
resetTimer()
}
}
}
extension Date {
/// Returns the amount of years from another date
func years(from date: Date) -> Int {
return Calendar.current.dateComponents([.year], from: date, to: self).year ?? 0
}
/// Returns the amount of months from another date
func months(from date: Date) -> Int {
return Calendar.current.dateComponents([.month], from: date, to: self).month ?? 0
}
/// Returns the amount of weeks from another date
func weeks(from date: Date) -> Int {
return Calendar.current.dateComponents([.weekOfYear], from: date, to: self).weekOfYear ?? 0
}
/// Returns the amount of days from another date
func days(from date: Date) -> Int {
return Calendar.current.dateComponents([.day], from: date, to: self).day ?? 0
}
/// Returns the amount of hours from another date
func hours(from date: Date) -> Int {
return Calendar.current.dateComponents([.hour], from: date, to: self).hour ?? 0
}
/// Returns the amount of minutes from another date
func minutes(from date: Date) -> Int {
return Calendar.current.dateComponents([.minute], from: date, to: self).minute ?? 0
}
/// Returns the amount of seconds from another date
func seconds(from date: Date) -> Int {
return Calendar.current.dateComponents([.second], from: date, to: self).second ?? 0
}
/// Returns the a custom time interval description from another date
func offset(from date: Date) -> String {
if years(from: date) > 0 { return "\(years(from: date))y" }
if months(from: date) > 0 { return "\(months(from: date))M" }
if weeks(from: date) > 0 { return "\(weeks(from: date))w" }
if days(from: date) > 0 { return "\(days(from: date))d" }
if hours(from: date) > 0 { return "\(hours(from: date))h" }
if minutes(from: date) > 0 { return "\(minutes(from: date))m" }
if seconds(from: date) > 0 { return "\(seconds(from: date))s" }
return ""
}
}
class ViewController: UIViewController, UserDefaultsTimerDelegate {
var label = UILabel(frame: CGRect(x: 40, y: 40, width: 60, height: 20))
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
label.text = ""
label.textColor = UIColor.black
view.addSubview(label)
UserDefaultsTimer.delegate = self
UserDefaultsTimer.setTimer(date: Date(timeIntervalSinceNow: 50), setDateOnlyIfCurrenTimerIsOver: true)
UserDefaultsTimer.resumeTimer()
}
func timerAction(timer: Timer, secondsToEnd: Int) {
label.text = "\(secondsToEnd)"
}
}
Upvotes: 4