Why a NSTimer can be delayed in background mode? - ios

My app run in background and uses NSTimer to launch audio after a certain amount of time. Me and my testers have no problems with this, and audio can be launched event after several hours in the background (>10h).
My issue is that some users reports that the audio is often delayed, sometimes by a few minutes, sometimes by an hour.
I do something like that:
UIApplication * app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:nil];
dispatch_async(dispatch_get_main_queue(), ^{
theTimer = [NSTimer scheduledTimerWithTimeInterval:[theDateIWant timeIntervalSinceNow] target:self selector:#selector(playAudio) userInfo:nil repeats:NO];
});
My questions are :
-Does a NSTimer can be delayed that much by the system ? if so how to go around this problem ?
-How to reproduce this kind of issues ?
- Is it safer to use the following ?
theTimer = [[NSTimer alloc] initWithFireDate:theDateIWant interval:0.0 target:self selector:#selector(playAudio) userInfo:nil repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:theTimer
forMode:NSDefaultRunLoopMode];
Thanks for any help !

It seems to me that you understand background tasks a little bit wrong.
When the first snippet is performed (I assume that you place it in the applicationDidEnterBackground method), it says the system
I have a small task to perform, please wait for a while till i finish
it!
and the system will wait for about 5-10 minutes before suspend the application.
If you need kind of alarm, you can use LocalNotification to playback specific sound at specified time.

Related

Cancelling Parse retries on bad internet connection

I'm trying to do something quite simple : Stopping a parse.com query after a few seconds, with an NSTimer. I've read after some reasearch it's a good "trick" to use.
Here is how I create my timer :
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:6.0
target:self
selector:#selector(stopRetries:)
userInfo:#{#"query":query}
repeats:NO];
Because i'm running this on a background thread (and outside a viewcontroller class), the timer is inside a dispatch_sync(dispatch_get_main_queue());
But whatever I do, I cannot stop the query, because [query cancel] doesn't do anything. I can't pass it in the userInfo of the timer. Breakpoints show it has an address and is "there" but it looks like junk inside.
What can I be doing wrong and what should I be doing instead?
My main goal is to make the parse.com query stop faster than 30 seconds, and warn the user with an alert.
You can Try this [self performSelector:#selector(abc) withObject:nil afterDelay:6.0];
or
You can Invalidate the timer

scheduledTimerWithTimeInterval is not calling at correct intervals sometimes

I have two separate timers on for recording data at 0.25 seconds and other for recording location details at 1 sec as below
self.hardBrakingTimer = [NSTimer scheduledTimerWithTimeInterval:0.25
target:self
selector:#selector(timerFired:)
userInfo:nil
repeats:YES];
self.locationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(locationTimerFired:)
userInfo:nil
repeats:YES];
But sometimes these timers are not calling at given time intervals.
You are scheduling a timer in a run loop in the default mode. A Run Loop is basically a queue, scheduling tasks on a certain thread. The exact execution time of your timer depends whether there are already other tasks executing in the same run loop.
It's also possible, that another run loop runs "on top" or modifies the "mode" such that other "tasks" are deferred (e.g a user scrolls). You can read more about run loops and "modes" and their surprising behavior in the official documentation: Anatomy of a Run Loop.
You are also likely not getting a precise timer with NSTimer anyway, since - as already pointed out by #Cyrille - there is that "Timer coalescing" feature of iOS and OSX: Timer Coalescing. You can however implement a quite precise timer using dispatch lib, see a code sample of mine: RXTimer

Two NSTimers with one governing the activity of the other?

I've been looking at the thread re: voice detection (http://mobileorchard.com/tutorial-detecting-when-a-user-blows-into-the-mic/).
I'm looking to implement something similar, however, with the following variation.
The levelTimer in that example fires continuously every .03 seconds. I essentially need to wrap another timer around this so that the voice check process only runs for (say) 10 seconds.
I'm finding that having something like:
outerTimer = [NSTimer scheduledTimerWithTimeInterval:10.0
target:self
selector:#selector(outerTimerFinished:)
userInfo:nil
repeats:NO];
levelTimer = [NSTimer scheduledTimerWithTimeInterval:0.03
target:self
selector:#selector(listenForSounds:)
userInfo:nil
repeats:YES];
…where outerTimerFinished method calls [levelTimer invalidate] isn't working - maybe due to thread blocking?
So can someone help me determine a way for levelTimer to do its work, but only for a specific amount of time?
Thanks.
Put a counter in the listenForSounds: method, and increment it each time the method is called. If you want to run the levelTimer for 10 seconds say, then check for when the counter > 333 and then invalidate the timer.

How to perform an action at a certain time with NSTimer?

I'm trying to perform an action at a certain time of the day.
I don't want to use UILocalNotification as I don't want the users to be away if anything is happening.
My current code is:
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(fire:) userInfo:nil repeats:NO];
You could:
Extract the current local time each time your app starts using [NSDate date] in application:didFinishLaunchingWithOptions method.
Calculate the time left for your alarm time to occur using dateWithTimeInterval:sinceDate.
Start a NSTimer with interval = that_time_difference.
I'm not sure what do you mean by:
"I don't want the users to be away of anything happening."
I am assuming you don't want anything to happen if the app is not running. The only thing I can think of would be push notifications.

IOS: Polling for every 2 sec

One feature of my application is to retrieve live data (JSON object) every 2 sec and display it (only while app is in foreground). I can't use push notification service because of server limitations.
Could you please tell me effective way for polling in IOS?
Polling the network wastes battery, but one option may be a recurrent timer:
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self
selector:#selector(checkServer:) userInfo:nil repeats:YES];
Where you hit the network in your checkServer: function.
You can use a timer.
poolingTimer = [NSTimer timerWithTimeInterval:pollingPeriod target:self selector:#selector(timerRanOut:) userInfo:nil repeats:YES];
And then in your timerRanOut method you can ask for the new json, parse it, and reload the views.

Resources