Timers and Locked Screen/Sleep Issues

July 28th, 2009 by Marco Leave a reply »

We all know, on the iPhone and iPod touch there are certain limitations for us, as developers.

The first one, I’ve encountered, while developing my second app, named “TuneAlarm”, was that it’s not possible, to execute any timers, while an app is active, but the iPhone/iPod touch is in sleep mode (that is, the screen is turned off/locked).

The app is basically an alarm clock, that wakes you with your favorite song straight out of your iPod Music Library.

Of course I knew, it isn’t possible to run your app in the background, but I thought this wouldn’t be a problem for TuneAlarm, as long as the user doesn’t switch to another application. I didn’t have deep sleep on my bill.

Worst of all, I just noticed this, at the final test runs of my app, when I thought it was ready for release. The reason for that is, that the iPhone apparently sleeps differently, while being connected via the Dock Connector. This sleep doesn’t seem to be as deep as the one, when the iPhone is not connected via the Dock Connector. You can run your timers just fine, while the screen is turned off, and your iPhone is getting juice over its Dock Connector.

I didn’t test my app without my iPhone being connected before, so this came as a little shock to me. Was the work invested in my second app worthless now? Was it all for nothing?

I began thinking about possible solutions: Push Notifications first came to mind. So I thought about, how my problem could be solved with the help of Push Notifications.

Imagine this: iPhone is in sleep mode. Push Notification arrives.
What happens next? The notification cannot activate your app, or call a method, or something like that. The user has to tap on the message and unlock the screen. Not until then, your app can react and do something.

This was obviously not a solution for TuneAlarm, as I cannot expect my sleeping user, to do all that. So it was clear, I wouldn’t use Push Notifications, without even taking into account, that I would have to run my own server, to be able to use them, or possible network issues, that could render my alarm unreliable.

I googled and searched the devforums for another solution and found some posts about disabling the application’s idle timer via

[[UIApplication sharedApplication] setIdleTimerDisabled:YES]

that would result in the application not falling asleep, as long as the user doesn’t put his or her device to sleep manually. You could run your normal timers then, they would fire as expected.

This has certain downsides as well:
- The screen stays on the whole time. -> Battery life is quite bad.
- If the user (accidently) puts his or her device to sleep, you’re screwed. Your timers won’t fire.

I figured, these downsides are unpleasent, but I didn’t find any better solution, so I gave it a try.

I disabled the application’s idle timer, compiled and tested my app again (without my iPhone plugged in via USB). Now, the screen shouldn’t go black anymore, unless I put my iPhone to sleep manually, and my timers should fire correctly, right?

Reality was different. After a minute of not touching my iPhone, the screen went black. My timers wouldn’t fire anymore. BUMMER!

Turns out, disabling the application’s idle timer did work quite reliable with iPhone OS 2.2.1, but quite unreliable with iPhone OS 3.0.

UPDATE #1: Following workaround doesn’t really work. I just got lucky a few times in a row, while testing this out. I guess. Further testing revealed, that disabling the application’s idle timer simply doesn’t achieve any reliable results, when running iPhone OS 3.0. Sorry to disappoint. :(

After some testing and fiddling around, I think I got it to work. At least all of my testing implies that my workaround is working. (Excuse my overuse of “work” in the last 2 sentences, please.)

Here’s how I’ve done it: My app contains a MPMusicPlayerController. At the end of my app’s MainViewController’s -viewDidLoad method I just call [musicPlayer play] and [musicPlayer stop] after initialising the player.

Here’s the complete code I wrote to get it all working:

— code removed —

Finally I decided to go with this solution and advise my users, to connect their iPhones to their USB charger or dock, while using this app, as this helps against the downsides of bad battery life, or accidently locking the screen manually. It’s the best I can do for now, though it’s not a 100% satisfying.

Update #1: So I’m still not ready to release this app. I did some more digging and came across some audio hack, that involved playing a sound file silently in 15 second intervalls, to keep the iPhone from going to deep sleep. I haven’t tested it, yet, though. I’ll post another update on this, tomorrow.

Update #2: Today, I tried out the “audio hack”. It seems to work, reliable. The iPhone appears to be going to deep sleep after 10 seconds, so you have to play a silent sound at least every ten seconds, with a correctly set up audio session.

I don’t know the exact impact on battery life yet, I will test this tonight.

I will post a complete tutorial tomorrow.

I hope Apple changes the sleep behavior, and at least enables us developers to run some timers in sleep mode respectively enables apps to set the iPhone’s sleep mode to some sort of a “semi sleep mode”, if they need some background functionality, while the iPhone’s screen is locked or finally give us some (even limited) background process functionality.

There certainly is some sort of way, for Apple itself, to do this as we can see on the phone app: You are able to recieve calls, even if your iPhone is asleep.

What do you think about this? Did you encounter some of the same problems I have? How did you deal with it?

PS: If you could point me to some WordPress themes with better code displaying abilities, I would appreciate it. :)

Advertisement

12 comments

  1. cleo says:

    This is exactly how I felt. My application is using [[UIApplication sharedInstance] setIdleTimerDisabled:YES] as well, but I see that the application goes to standby mode once in a while after updating to OS3.0. I am not sure how to deal with it, so I am looking forward to your tutorial.

  2. marco says:

    Sorry for the delay. I didn’t get around to write the tutorial, yet.

    I will post it tomorrow, though. Promise! :)

    • marco says:

      The post is almost ready.

      I ran into a little problem with the rendering of the code quotes. I fixed this, but the post is not quite ready to be published, yet.

      I will upload the complete project I built to my github, too (including the needed silent sound file).

      Tomorrow it will all be online, double promise!

      Need to get some sleep now.

  3. I think I found a better way to fix this, rather than firing a silent sound every 10 seconds. You can try this, I used this with my alarm application. For whichever NSTimer you need to work while the iPod/iPhone is sleeping add this line of code

    [[NSRunLoop currentRunLoop] addTimer:exampleTimer forMode:NSRunLoopCommonModes];

  4. Anonymous says:

    Why do you use so many unnecessary commas when you write? Only read the first two paragraphs and couldn’t take it anymore.

  5. trsills says:

    nice tip. I didn’t connect the two cause I have a timer firing in one program that didn’t have this problem after adding the code to disable the idle timer but in another it seemed idle timer didn’t make a difference (on 3.0 of course). Seems like having something (unecessarily) ticking away solves it (ugh).

  6. loloviolo says:

    To Shabbir : This code is not working on my side (OS3.1.2)

  7. echobravo says:

    Any new developments on this? I am working on an app that has a stopwatch and I would like it to work when the screen is locked so that the user may hold the device however they choose without accidentally pressing the cancel button, but the timers are stalled as soon as the screen is manually locked.

Leave a Reply