getTimeElapsed in Swift with NSTimer - ios

I'm a beginner in Swift and I'm facing a problem.
I want to create a class that contains a timer and a button
Everytime the button is tapped I have to restart the timer, if the time elapsed between the timer starts and the button is tapped is greater (or equal) to 400 milliseconds, I have to call the function in the selector.
But there isn't a "GetTimeElapsed()" method in swift and I don't know how to do it.
If you have some clue/tutorials it could be cool !
Thx guys

Few small steps:
Define the start time: (should happen at the same time you start the timer)
startTime = (NSDate.timeIntervalSinceReferenceDate())
Measure the time difference
let elapsed = NSDate.timeIntervalSinceReferenceDate() - startTime

By default, NSTimer does not have this feature.
Right way is do your job with dates and comparing it.
By the way, you can do a nice extension like this:
public extension NSTimer {
var elapsedTime: NSTimeInterval? {
if let startDate = self.userInfo as? NSDate {
return NSDate().timeIntervalSinceDate(startDate)
}
return nil
}
}
In this way, when you create your timer, you have to set userInfo:
let timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(foo), userInfo: NSDate(), repeats: true)
In your 'foo' method you can test elapsed time:
func foo() {
print(self.timer.elapsedTime)
}

Related

Swift: Timer run in foreground/background

I'm learning how to create a Pomodoro app, and am able to send notifications. However, I am totally clueless as to how to allow my timer label to update itself on reloading the app. Which means the timer works only when the app is open and not when it's in the foreground/background. Hoping to find a tutorial to learn from or just a quick answer code. Thanks!
Edit: Just to clear some misunderstandings, my app's Notification works fine with the timer, for example if 30mins is selected, the app would notify the user after 30mins. However, the problem is that when the app reopens, it resumes for example 29:57 seconds left on the timer label while the 30mins should have passed already.
*Added in AppDelegate*
var seconds = 0 //Timer countdown seconds
var currentDate = NSDate()
var setDate: Int = 0
func pauseApp(){
viewC.timer.invalidate() //invalidate timer
UserDefaults.standard.set(seconds, forKey: "current") //error occurs here where "Cannot assign value of type NSDate to type Timer"
setDate = UserDefaults.standard.integer(forKey: "current")
}
func startApp(){
let difference = currentDate.timeIntervalSince(NSDate() as Date) as Double
seconds = Int(Double(setDate) - difference)
viewC.updateTimer()
}
What someone suggests from a different thread is cancel the timer and store a NSDate when the app goes to the background. He stated we can use this notification to detect the app going to the background:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "pauseApp", name: UIApplicationDidEnterBackgroundNotification, object: nil)
Then cancel the timer and store the date:
func pauseApp(){
self.stop() //invalidate timer
self.currentBackgroundDate = NSDate()
}
Use this notification to detect the user coming back:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "startApp", name: UIApplicationDidBecomeActiveNotification, object: nil)
Then calculate the difference from the stored date to the current date, update your counter and start the timer again:
func startApp(){
let difference = self.currentBackgroundDate.timeIntervalSinceDate(NSDate())
self.handler(difference) //update difference
self.start() //start timer }
However, I do not fully understand this code (namely, the difference between the "handler" and my own "seconds") as am new to programming... Hoping for an answer or helpful insight.
Solved: I managed to solve it myself from this video https://www.youtube.com/watch?v=V6ta24iBNBQ
Using this concept of timeDifference as well as UserDefaults.standard.set....
I managed to adapt it to my personal app with the code
You can call Timer to run the timmer when the view loads.
var runTimer : Timer?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
runTimer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(myFun), userInfo: nil, repeats: true)
}
func myFun(){
//do your logic
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
runTimer?.invalidate()
}

NSTimer not precise enough

I'm setting up timers to execute code once each one finished. However, it seems that the timing of NSTimer is not completely precise, after a while, the timers seem to finish a little too early.
I've set up the following to test the deviation.
(The start button is done in the Storyboard and linked to.)
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var startButton: UIButton!
//var mainTimer: NSTimer!
var counter: NSTimeInterval = 0.0
#IBAction func startButtonPressed(sender: AnyObject) {
startMainTimer()
startTimers()
}
func startMainTimer() {
let mainTimer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: #selector(ViewController.count), userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(mainTimer, forMode: NSRunLoopCommonModes)
}
func count() {
counter += 0.01
}
func startTimers() {
var lastTimeInterval: NSTimeInterval = 0.0
for _ in 0..<50 {
let timeInterval = lastTimeInterval + 1.0
lastTimeInterval = timeInterval
// Not setting up a repeating timer as intervals would be different in my original project where the deviation will be even worse.
let timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval, target: self, selector: #selector(ViewController.printTime), userInfo: nil, repeats: false)
NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)
}
}
func printTime() {
print(counter)
}
}
After while, the timers will be early a tenth of a second or even more.
Should I be using NSDate for this to have better timing, or would that be overly complicated? How would the above look with NSDate?
Any help / pointers much appreciated!
It will never be perfect, but you can stop it from compounding by scheduling the next call at the end of the target selector. Calculate a new interval each time based on the current time and when you want it to trigger.
EDIT: I grabbed some code from a very old project to give the idea (that's why it's in Obj-C) -- You want something like this but not exactly:
- (void)constantIntervalTimer {
static const NSTimeInterval secondsBetweenMessageSend = 1.0;
if ( !self.timerShouldStop ) {
NSDate *startTime = [NSDate date];
// your code here, it should take less than a
// second to run or you need to increase the timer interval
// figure out the right time to preserve the interval
NSTimeInterval waitTime = secondsBetweenMessageSend -
[[NSDate date] timeIntervalSinceDate:startTime];
// in case your code runs in more than the interval,
// we just call back immediately
if (waitTime < 0.0)
waitTime = 0.0;
[NSTimer scheduledTimerWithTimeInterval: waitTime
target:self selector:#selector(constantIntervalTimer)
userInfo:nil repeats:NO];
}
}
Once you call this message, it will keep calling itself every second until you set a property called timerShouldStop to YES. You must declare and define this property and set it to NO in your init for this message to work.

Simple NSTimer implementation issue

I have just started to learn swift 2 and I am testing a few things in an Xcode 'playground'. When a create an instance of the 'pyx' (below) I am not seeing the console output I would expect. I am sure I have made a silly mistake but after staring at it for a while I cannot figure it out.
class zxy {
var gameTimer = NSTimer()
var counter = 0
init() {
gameTimer = NSTimer (timeInterval: 1, target: self, selector: "Run:", userInfo: nil, repeats: true)
}
func Run(timer : NSTimer) {
while(counter < 10){
print(counter)
counter++
}
timer.invalidate()
}
}
Thanks in advanced.
You have 2 problems with your code. As #glenstorey points out in his answer, you need to call the method scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:, not the init method you're calling.
EDIT:
As #DanBeauleu says in his comment to my answer, the call would look like this in Swift:
NSTimer.scheduledTimerWithTimeInterval(
1,
target: self,
selector: "Run:",
userInfo: nil,
repeats: true)
The second problem is your Run method.
You don't want a while loop. That will repeat 10 times in a tiny fraction of a second the first time the timer fires, then invalidate the timer.
Your timer method needs to be changed like this:
func Run(timer : NSTimer)
{
if counter < 10
{
print(counter)
counter++
}
else
{
timer.invalidate()
}
}
(BTW, by strong convention, method/function names should start with a lower-case letter, so your Run function should be named run instead.)
You've created a NSTimer object, but that doesn't start the timer - just gets it ready to go. Use scheduledTimerWithTimeInterval to create and start the timer.

WKInterfaceTimer used as a timer to countdown start and stop

I am trying to create a timer to countdown x minutes and y seconds.
I am computing the number of seconds and creating the InterfaceTimer like this:
timer.setDate(NSDate(timeIntervalSinceNow:Double(secondsValue+1)))
timer.stop()
after that I keep stoping it and starting it again and again, but the values are suddenly decreasing as "time(now) doesn't stop".
Eg: if the timer shows :55, I start it for 3sec and stop it, it shows :52, I wait 10seconds and then start it again, it starts from :42.
I can not save the value currently in the WKInterfaceTimer, so that I could start again from the same point. Everything I tried doesn't work. Did anyone work with the timer and it stayed at the same value after stopping it?
Yes the watchkit timer is a bit...awkward...and definitely not very intuitive. But that's just my opinion
You'll have to keep setting the date/timer each time the user chooses to resume the timer.
Remember, you'll also need an internal NSTimer to keep track of things since the current WatchKit timer is simply for display without having any real logic attached to it.
So maybe something like this...It's not elegant. But it works
#IBOutlet weak var WKTimer: WKInterfaceTimer! //watchkit timer that the user will see
var myTimer : NSTimer? //internal timer to keep track
var isPaused = false //flag to determine if it is paused or not
var elapsedTime : NSTimeInterval = 0.0 //time that has passed between pause/resume
var startTime = NSDate()
var duration : NSTimeInterval = 45.0 //arbitrary number. 45 seconds
override func willActivate(){
super.willActivate()
myTimer = NSTimer.scheduledTimerWithTimeInterval(duration, target: self, selector: Selector("timerDone"), userInfo: nil, repeats: false)
WKTimer.setDate(NSDate(timeIntervalSinceNow: duration ))
WKTimer.start()
}
#IBAction func pauseResumePressed() {
//timer is paused. so unpause it and resume countdown
if isPaused{
isPaused = false
myTimer = NSTimer.scheduledTimerWithTimeInterval(duration - elapsedTime, target: self, selector: Selector("timerDone"), userInfo: nil, repeats: false)
WKTimer.setDate(NSDate(timeIntervalSinceNow: duration - elapsedTime))
WKTimer.start()
startTime = NSDate()
pauseResumeButton.setTitle("Pause")
}
//pause the timer
else{
isPaused = true
//get how much time has passed before they paused it
let paused = NSDate()
elapsedTime += paused.timeIntervalSinceDate(startTime)
//stop watchkit timer on the screen
WKTimer.stop()
//stop the ticking of the internal timer
myTimer!.invalidate()
//do whatever UI changes you need to
pauseResumeButton.setTitle("Resume")
}
}
func timerDone(){
//timer done counting down
}

Make a service call at regular interval of time in swift

I am new to swift programming and I don't know how to call a method at regular interval of time. I have a demo app for service call but i don't know how can i call it at regular interval of time.
You can create an object of NSTimer() and call a function on definite time interval like this:
var updateTimer = NSTimer.scheduledTimerWithTimeInterval(15.0, target: self, selector: "callFunction", userInfo: nil, repeats: true)
this will call callFunction() every 15 sec.
func callFunction(){
print("function called")
}
Here is a simple example with start and stop functions:
private let kTimeoutInSeconds:NSTimeInterval = 60
private var timer: NSTimer?
func startFetching() {
self.timer = NSTimer.scheduledTimerWithTimeInterval(kTimeoutInSeconds,
target:self,
selector:Selector("fetch"),
userInfo:nil,
repeats:true)
}
func stopFetching() {
self.timer!.invalidate()
}
func fetch() {
println("Fetch called!")
}
If you get an unrecognized selector exception, make sure your class inherits from NSObject or else the timer's selector won't find the function!
Timer variant with a block (iOS 10, Swift 4)
let timer = Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { (timer) in
print("I am called every 5 seconds")
}
Do not forget call invalidate method
timer.invalidate()
GCD approach (will tend to drift a bit late over time)
func repeatMeWithGCD() {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 5) {
print("I am called every 5 seconds")
self.repeatMeWithGCD()//recursive call
}
}
Do not forget to create a return condition to prevent stackoverflow error

Resources