timer in iOS not working - ios

I have this code for a repeated timer in my app:
(inside viewDidAppear)
NSDate *date = [NSDate dateWithTimeIntervalSinceNow: 3];
NSTimer *restrictionTimer = [[NSTimer alloc] initWithFireDate: date
interval: 3
target: self
selector:#selector(changeRestriction)
userInfo:nil repeats:YES];
and
- (void) changeRestriction
{
NSLog(#"inside !!");
}
However, I dont see any output, any help ?

If you're using -initWithFireDate:.. you have to manually add the timer to the NSRunLoop. However you could just use +scheduledTimerWithTimeInterval:.. and it will automatically trigger.

Related

NSTimeInterval not always firing

I'm working on a simple radio alarm clock app for a non-mainstream stream I'd like to wake up to. It uses the opting out of background trick to stay running when the phone is locked, similar to Rise/Sleepcycle.
Originally the alarm wouldn't fire the next day if I was to set the alarm before midnight but would always fire pretty much on the turn of the minute if set ahead of the current time but before 00:00
So I've made some adjustments so that the date is also checked. I also included conditions so that it's impossible to set a time before the current time. Now while this takes care of the next day issue, I'm finding the fire itself is temperamental. Sometimes it works perfectly, sometimes there is a huge delay (up to 10 minutes in cases) sometimes it never fires at all. I really cannot figure out for the life of me why this happens.
Below is my code. Does anyone have any ideas as to why it's not consistent?
- (void)viewDidLoad {
[super viewDidLoad];
radio = [[Radio alloc] initWithUserAgent:#"my app"];
[radio connect:STREAM_URL withDelegate:self withGain:(1.0)];
// dateTimePicker.date = [NSDate date];
dateTimePicker.date =
[[ NSDate alloc ] initWithTimeIntervalSinceNow: (NSTimeInterval) 2];
dateTimePicker.minimumDate =
[[ NSDate alloc ] initWithTimeIntervalSinceNow: (NSTimeInterval) 0 ];
}
-(void) presentMessage:(NSString *)message {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Radio Alarm" message: message delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alert show];
}
- (IBAction)alarmSetButtonTapped:(id)sender {
NSLog( #"Alarm Set Button Tapped");
NSDateFormatter * dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.timeZone = [NSTimeZone defaultTimeZone];
dateFormatter.timeStyle = NSDateFormatterShortStyle;
dateFormatter.dateStyle = NSDateFormatterShortStyle;
NSString * dateTimeString = [dateFormatter stringFromDate: dateTimePicker.date];
NSLog( #"Alarm Set Button Tapped : %#", dateTimeString);
[self presentMessage:[NSString stringWithFormat:#"Alarm set for %#", dateTimeString]];
NSDate *alarmFireDate = dateTimePicker.date;
NSTimeInterval differenceFromNow = [alarmFireDate timeIntervalSinceNow];
//[alarmFireDate performSelector:#selector(PlayAlarm:) withObject:nil afterDelay:differenceFromNow];
[self performSelector:#selector(PlayAlarm:)
withObject:(self)
afterDelay:(differenceFromNow)];
you can use
[NSTimer scheduledTimerWithTimeInterval:differenceFromNow
target:self
selector:#selector(PlayAlarm:)
userInfo:nil
repeats:NO];
This timer is quite accurate.
If you need to abort the timer, just save the returned NSTimer* from this in a property and call
[myTimer invalidate];

How to start and stop timer at particular times in iOS

My app detect the audio at particular intervel like 10:00AM(startTime) -10:30AM(endTime),etc.Now when the current time reaches start time(means current time is also 10:00 AM) how to call audio detection process and stop detection process at (endTime)10:30 AM . suppose another interval start at 10:15AM(startTime) - 10:45 AM(endTime) how to update endTime from 10:30AM to 10:45AM.how to check when current time reaches startTime and endTime.Please help me its urgent.Thanks here my code is
ViewController.h
#interface ViewController : UIViewController
{
BOOL IsListening;
}
//session array is array of dictionaries contains program start and end time.
ViewController.m
-(void)viewDidLoad
{
[super viewDidLoad];
NSTimer* listener_timer = [NSTimer scheduledTimerWithTimeInterval: 10.0 target: self
selector: #selector(listeningHandler:) userInfo: nil repeats: YES];
}
-(void)listeningHandler:(NSTimer*)time
{
if (!IsListening)
{
double currentTime=[[NSDate date] timeIntervalSince1970];
for (NSDictionary * session in SessionsArray)
{
double startTime=[[session objectForKey:#“startTime”] doubleValue];
double endTime=[[session objectForKey:#“endTime”] doubleValue];
if (currentTime > startTime && currentTime < endTime)
{
//start listening.
IsListening =YES;
/*
//detection process code
*/
NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:endTime];
NSTimer *timer = [[NSTimer alloc] initWithFireDate:fireDate
interval:5
target:nil
selector:#selector(fireEndtimeAlarm)
userInfo:nil
repeats:YES];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
}
}
}
else
{
double currentTime=[[NSDate date] timeIntervalSince1970];
for (NSDictionary * session in SessionsArray)
{
double startTime=[[session objectForKey:#"startTime"] doubleValue];
double endTime=[[session objectForKey:#"endTime”] doubleValue];
if (currentTime > startTime && currentTime < endTime)
{
// in between session
IsListening =YES;
/*
//detection process code
*/
}
}
}
}
-(void)fireEndtimeAlarm
{
IsListening = NO;
//stop detecting process at endTime
}
Start Timer & Stop Timer in ios
start Timer
timer= [NSTimer scheduledTimerWithTimeInterval:60.0f
target: self
selector: #selector(methodFromTimer)
userInfo: nil
repeats: YES];
stop timer
[timer invalidate];
timer=nil;
You can achieve this using 2 Local notifications
First of All schedule a specific notification at a specific time when time is reached then a trigger will run and at that time Start audio and
Schedule a second notification after 30 min when that will be fired then stop audio
For local notifications you can follow these
Link1
Link2

Timer in Today Extension Freezes Extension

I have a timer to a date counting down in my today widget. I am gathering the date from my View Controller and then starting a timer in the extension and from there I am displaying the dates in three labels, Hours, Minutes, and Seconds. In my app... this is perfect, but not so true in the notification center. The countdown loads correctly and works great for about 20 seconds. After that it skips a second and freezes, then it starts again in about 4 seconds and then it will reset the whole widget (everything disappears) and then about 2 seconds later the whole widget resets. Is this a memory warning or something?
View Controller:
mainTextLabel.text = 436496400
NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.nicmac.nextgame"];
[sharedDefaults setObject:mainTextLabel.text forKey:#"MyNumberKey"];
[sharedDefaults synchronize];
Today View Controller
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(userDefaultsDidChange:)
name:NSUserDefaultsDidChangeNotification
object:nil];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.preferredContentSize = CGSizeMake(320, 300);
[self performSelector:#selector(updateNumberLabelText) withObject:nil afterDelay:1.0];
}
- (void)userDefaultsDidChange:(NSNotification *)notification {
[self performSelector:#selector(updateNumberLabelText) withObject:nil afterDelay:1.0];
}
- (void)updateNumberLabelText {
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.nicmac.nextgame"];
NSString *date = [defaults stringForKey:#"MyNumberKey"];
self.numberLabel.text = [NSString stringWithFormat:#"%#", date];
NSString *doubleString = self.numberLabel.text;
double value = [doubleString doubleValue];
destinationDate = [NSDate dateWithTimeIntervalSince1970:value];
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateNumberLabelText) userInfo:nil repeats:YES];
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
int units = NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
NSDateComponents *components = [calendar components:units fromDate:[NSDate date] toDate:destinationDate options:0];
[hour setText:[NSString stringWithFormat:#"%ld", (long)[components day] * 24 + (long)[components hour]]];
[minute setText:[NSString stringWithFormat:#"%ld", (long)[components minute]]];
[second setText:[NSString stringWithFormat:#"%ld", (long)[components second]]];
}
Thanks!
The problem here lies in the - (void)updateNumberLabelText method.
Specifically, here:
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateNumberLabelText) userInfo:nil repeats:YES];
This means that every time you're calling this method, it's scheduling another timer to call this method every second (every second because repeats:YES). That means, every second (with a micro offset because of any lag in the computer), x number of scheduled timers is producing x MORE number of scheduled timers, meaning that every second you have double the number of timers. This is called exponential growth, and can be modelled to 2^x.
So to find the number of timers in 20 seconds:
2^1+2^2+2^3+2^4+2^5+2^6+2^7+2^8+2^9+2^10+2^11+2^12+2^13+2^14+2^15+2^16+2^17+2^18+2^19+2^20
Ew, can we represent this better - we can! We can use sigma notation like so:
<no. of seconds>
Σ 2^i
i=1
But enough math. That averages out to around a whopping 2,000,000 timers initialised in 20 seconds. That is a lot of timers, and the widget eventually struggles to catch breath (no more memory!) and dies/crashes/restarts itself.
Here's how to fix it:
If you really want this method to loop itself, call
[self performSelector:#selector(updateNumberLabelText) withObject:nil afterDelay:1.0];
in the method instead.
You could do it the way you're currently doing it and do repeats:NO instead, but I don't think that the timer will invalidate itself after the 1.0 second, and creating that many timers is not very efficient.

NSTimer with NSUserDefaults

How do I have an NSTimer with a UIButton that changes its text from Start to Stop, Also another UIButton will be added to pause the timer. So, You press start and then the timer will show in a label in hours, minutes and seconds. While the timer is running, is there a way to pause the timer ( I heard you have to use NSUserDefaults). Also is there a way to save the time at which the start button was pressed and then to save the time that the button was pressed again to stop? Also when the pause button is pressed to pause the timer and when pause is pressed again it will resume the timer?
If you need a timer that doesn't support pausing, you only need to know the NSDate at which the timer started so that you can calculate the time elapsed. Create an instance variable:
#property (strong, nonatomic) NSDate *timerStartDate;
When the button is tapped:
- (void)startTimer
{
self.timerStartDate = [NSDate date];
}
Your NSTimer is not to keep track of the time, but to periodically update the label. Create an instance variable:
#property (strong, nonatomic) NSTimer *labelUpdateTimer;
and update the -startTimer method:
- (void)startTimer
{
self.timerStartDate = [NSDate date];
// start timer to update label
if (!self.labelUpdateTimer) {
self.labelUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(updateLabel)
userInfo:nil
repeats:YES];
}
}
- (void)updateLabel
{
NSTimeInterval secondsElapsedSinceTimerStart = 0;
if (self.timerStartDate) {
secondsElapsedSinceTimerStart = [[NSDate date] timeIntervalSinceDate:self.timerStartDate];
}
NSString *formattedTime = <format time elapsed the way you like>;
self.label.text = formattedTime;
}
- (void)dealloc
{
// make sure timer is not firing anymore!
if (_labelUpdateTimer) {
[_labelUpdateTimer invalidate];
_labelUpdateTimer = nil;
}
}
However, if you want to be able to pause the timer, besides calculating the time elapsed since the timer last started, you'll need to store the time previously elapsed (if you started/paused the timer before). Create an instance variable:
#property (nonatomic) NSTimeInterval previouslyElapsedSeconds;
And when you pause the timer:
- (void)pauseTimer
{
// update elapsedSeconds
if (self.timerStartDate) {
self.previouslyElapsedSeconds += [[NSDate date] timeIntervalSinceDate:self.timerStartDate];
self.timerStartDate = nil;
}
[self.labelUpdateTimer invalidate];
self.labelUpdateTimer = nil;
}
Update -updateLabel:
- (void)updateLabel
{
NSTimeInterval secondsElapsedSinceTimerStart = 0;
if (self.timerStartDate) {
secondsElapsedSinceTimerStart = [[NSDate date] timeIntervalSinceDate:self.timerStartDate];
}
// account for previously elapsed time
NSTimeInterval totalSecondsElapsed = self.previouslyElapsedSeconds + secondsElapsedSinceTimerStart;
NSString *formattedTime = <format time elapsed the way you like>;
self.label.text = formattedTime;
}
NSUserDefaults will be required only if you want to keep timing even if the app is shut down (not just backgrounded). In that case store previouslyElapsedSeconds and timerStartDate in NSUserDefaults instead of as instance variables.
assuming you have NSDate objects startTime and stopTime
[[NSUserDefaults standardUserDefaults] setObject:startTime forKey:#"startTime"];
[[NSUserDefaults standardUserDefaults] setObject:stopTime forKey:#"stopTime"];
Or you can use floats startTimef and stopTimef
[[NSUserDefaults standardUserDefaults] setFloat:startTimef forKey:#"startTime"];
[[NSUserDefaults standardUserDefaults] setFloat:stopTimef forKey:#"stopTime"];
Check out the NSUserDefaults docs.
Here's a great set of tutorials.

How to update label once a day?

I'd like to have a label in my app that gets updated once a day at a specific time (i.e. 12 a.m.). If you could help me out I'd really appreciate it, thanks.
This is what i currently have but it only works if the app is opened between 12 and 1 am.
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"HH"];
NSString *stringDate = [formatter stringFromDate:[NSDate date]];
if ([stringDate isEqualToString:#"00"]) {
NSLog(#"its working");
[self changeLabel];
}
To clarify, I'm trying to change the text of a label once a day. I'd like to have the label update at a specific time like 12 am so that it updates with the start of each new day. I've tried a few ways of doing this but haven't gotten the desired result. If anyone knows of a way to do this I'd appreciate your input.
If you only need to determine the last time the application was opened, you can use NSUserDefaults to save the date and then check that the approprate number of seconds (86400 = 1 day) have passed.
Something along the lines of the following should point you in the right direction:
//gather last time app was opened
NSDate *lastUpdated = [[NSUserDefaults standardUserDefaults] objectForKey:#"app_last_updated"];
//check date is valid
if(lastUpdated)
{
//determine number of seconds that have passed
NSTimeInterval timeInterval = [lastUpdated timeIntervalSinceNow];
//check time interval is greater than 1 day
if(timeInterval > 86400)
{
//update label
}
}
//save current time
[[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:#"app_last_updated"];
It depends on what you want to do. On iOS, most likely your app is not running in the foreground all the time. So I would just update the label when the application is active in applicationDidBecomeActive:. Then I would create a timer to fire at the specific date/time I want.
For example:
NSDate *d = // create the next date/time
updateTimer_ = [[NSTimer alloc] initWithFireDate: d
interval: 0
target: self
selector:#selector(updateLabel:)
userInfo:nil repeats:NO];
NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer:updateTimer_ forMode: NSDefaultRunLoopMode];
I know this is an old post but a few days ago I needed just what Dave123 asked and I came up with this solution after searching a lot on the web:
- (void)applicationDidBecomeActive:(UIApplication *)application {
NSLog(#"applicationDidBecomeActive");
self.masterviewController = [[PBMasterViewController alloc] init];
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
NSLog(#"Seconds --------> %.f",[[NSDate date] timeIntervalSinceDate:[[NSUserDefaults standardUserDefaults] objectForKey:#"OLD_DATE"]]);
NSLog(#"old date: %#", [[NSUserDefaults standardUserDefaults] objectForKey:#"OLD_DATE"]);
// 24 hours must elapse between each update check
if ([[NSDate date] timeIntervalSinceDate:[[NSUserDefaults standardUserDefaults] objectForKey:#"OLD_DATE"]] > 86400) {
NSLog(#"Time elapsed since last update is MORE than 1 day. Check for updates.");
[self.masterviewController checkForUpdates];
} else {
NSLog(#"Time elapsed since last update is LESS than 1 day. Don't check for updates.");
}
// Here I convert the seconds into hours with two decimals, if you want to show the time elapsed in a UILabel
NSString *stringCheckInterval = [NSString stringWithFormat:#"%.02f hours since last update", [[NSDate date] timeIntervalSinceDate:[[NSUserDefaults standardUserDefaults] objectForKey:#"OLD_DATE"]] / 60 / 60];
NSLog(#"%#", stringCheckInterval);
}
is there someone that has the app opened form more than 24h??
Anyway, you can simply use an NSTimer with 86400 as delay time.
When you start the app, set up a timer that will fire at 12:00:01 AM and update your label.

Resources