blog.marcopeluso.com

In need of a clever subtitle.

Moving on From Feedburner

I know, I’ve not written something for a long time and this isn’t going to be some interesting post either. But given recent events around Google’s services, I just wanted you to know, that I think it’s time to move on from Google Feedburner. So if you are still subscribing to this blog in your feed reader and use the Feedburner feed, please update your feed to the canonical feed http://blog.marcopeluso.com/feed

Who knows, I might just write something again in the near future and you wouldn’t want to miss it, would you? No guarantees, though. ;)

How-to: Prevent iPhone From Deep Sleeping

As I have promised three weeks ago (and again a few days ago) I finally got around to writing this little how-to. I’m sorry for the delay. I hope this helps someone, anyway.

Mind you, this is my first attempt at writing a how-to and most of it was done while I was being tired. Also, English isn’t my native language, so please excuse any typos or other errors you may find.

If you have any suggestions on how to improve this how-to, please feel free to leave a comment.

Now, have fun!

Preface

We will create an iPhone application, that prevents the iPhone from deep sleeping while the app is running. It’s not a really useful application, as it does nothing but just that, but you can easily adapt the stuff you learn here and use it for your own application(s).

Here’s what we’re going to do: We will …

  • … create a new project in Xcode.
  • … add the necessary frameworks to this project.
  • … add a silent sound file to this project.
  • … create a class called “DeepSleepPreventer” and add it to this project.
  • … then use this class to prevent the iPhone from deep sleeping.

You can use this class in your apps to prevent the iPhone from deep sleeping, while they are running. Just add the needed frameworks and the DeepSleepPreventer class to your app and use the DeepSleepPreventer, whenever you need it.

Create a New Project in Xcode

I’m sure you all know how to do this, but for completeness’ sake, I’ll just go through the whole process, step by step.

Fire up Xcode. Click “File” -> “New Project”. We are gonna use the “View-based Application” template for this how-to, so select it and click “Choose …”.

how-to-1-1

Choose a location and name under which you want to save the project and click on “Save”. We are going to use “iPhoneInsomnia” as a name and save the project to the desktop (I know, I have a lot to tidy, judging by the screenshot).

how-to-1-2

Add the Necessary Frameworks

Now we’ll add the AVFoundation and the AudioToolbox frameworks to our project, as we will need them later on.

In our project window, we right-click (or control-click) our “Frameworks” group in the “Groups & Files” pane and click on “Add” -> “Existing Fameworks …”.

how-to-1-3

A file selection window should pop up now and should show you the content of the current iPhone SDK’s “Library” subfolder. If your starting up somewhere else, navigate to “/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk/System/Library” first (see screenshot).

how-to-1-4

Further go to the Frameworks folder and select “AudioToolbox.framework” and “AVFoundation.framework”. Then click on “Add”.

how-to-1-5

In the upcoming window uncheck the “Copy …” checkbox, if it is selected and choose “Relative to Current SDK” as reference type. Then click “Add”.

how-to-1-6

Add a Silent Sound File

Now we need to add a silent sound file. You can create it with a sound tool of your choice. I named it “noSound.wav”. You can just use the file I created, if you like. It is part of the Github repository I created for this whole project. iPhoneInsomnia @ Github

We will first create a new group, named “DeepSleepPreventer” in the “Groups & Files” pane. Because we are very tidy people, we will add the sound file and our later created custom class to this group, too.

Right-click on “iPhoneInsomnia” and click on “Add” -> “New Group” and name it “DeepSleepPreventer”.

how-to-1-10

Now right-click this group and click “Add” -> “Existing Files …” and add the sound file to the project.

how-to-1-13

Navigate to the location of your sound file, select it and click “Add”.

how-to-1-14

Check the “Copy items …” checkbox and set “Reference Type” to “Default”. Click Add.

how-to-1-15

Create our New Class

Now it’s time to finally write some code. Almost. First we need to add a new class to the project.

Right-click the “DeepSleepPreventer” group and click “Add” -> “New File …”.

how-to-1-16

Choose the “Objective-C class” template under “iPhone OS” -> “Cocoa Touch Class” and select “NSObject” on the “Subclass of” pull down. Click “Next”.

how-to-1-17

Name the file “DeepSleepPreventer.m”, Check the “Also create .h” checkbox and click “Finish”.

how-to-1-18

Now modify “DeepSleepPreventer.h” to look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#import <Foundation/Foundation.h>

@class AVAudioPlayer;

@interface DeepSleepPreventer : NSObject {
    AVAudioPlayer *audioPlayer;
    NSTimer *preventSleepTimer;
}
@property (nonatomic, retain) AVAudioPlayer *audioPlayer;
@property (nonatomic, retain) NSTimer *preventSleepTimer;
- (void)playPreventSleepSound;
- (void)startPreventSleep;
- (void)stopPreventSleep;
@end

We declared two properties here: an AVAudioPlayer, that will play our silent sound file and an NSTimer that will call -playPreventSleepSound. We actually create this timer in -startPreventSleep and invalidate it in -stopPreventSleep later.

Now we edit “DeepSleepPreventer.m” step by step.

First we import some header files, synthesize our accessors and mutators (or getters and setters, if you prefer to call them that):

1
2
3
4
5
6
7
8
#import "DeepSleepPreventer.h"
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>

@implementation DeepSleepPreventer

@synthesize audioPlayer;
@synthesize preventSleepTimer;

Modify -init to set up all the stuff we need to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
- (id)init
{
    if ((self = [super init])) {
        // Activate audio session
        AudioSessionSetActive(true);
        // Set up audio session, to prevent iPhone from deep sleeping, while playing sounds
        UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
        AudioSessionSetProperty (
            kAudioSessionProperty_AudioCategory,
            sizeof (sessionCategory),
            &#038;sessionCategory
        );

        // Set up sound file
        NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:@"noSound"
                                                                  ofType:@"wav"];
        NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:soundFilePath];

        // Set up audio player with sound file
        self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
        [self.audioPlayer prepareToPlay];

        // You may want to set this to 0.0 even if your sound file is silent.
        // I don't know exactly, if this affects battery life, but it can't hurt.
        [self.audioPlayer setVolume:0.0];
    }
    return self;
}

Now we implement -playPreventSleepSound:

1
2
3
- (void)playPreventSleepSound {
    [self.audioPlayer play];
}

This should be self explanatory.

Now -startPreventSleep (this is the method we will call, if we want the iPhone to stay awake):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void)startPreventSleep {
    // We need to play a sound at least every 10 seconds to keep the iPhone awake.
    // We create a new repeating timer, that begins firing now and then every ten seconds.
    // Every time it fires, it calls -playPreventSleepSound
    self.preventSleepTimer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:0]
                                                      interval:10.0
                                                        target:self
                                                      selector:@selector(playPreventSleepSound)
                                                      userInfo:nil
                                                       repeats:YES];
    // We add this timer to the current run loop
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    [runLoop addTimer:self.preventSleepTimer forMode:NSDefaultRunLoopMode];
}

-stopPreventSleep stops the sleep preventing.

1
2
3
4
- (void)stopPreventSleep {
    [self.preventSleepTimer invalidate];
    self.preventSleepTimer = nil;
}

Finally -dealloc to clean up our mess:

1
2
3
4
5
6
7
8
- (void)dealloc {
    // memory management
    [preventSleepTimer release];
    [audioPlayer release];
    [super dealloc];
}
// Don't forget the matching @end to our @implementation
@end

Use the DeepSleepPreventer Class

Now switch to “iPhoneInsomniaAppDelegate.h”. We will add a property if the type or our class DeepSleepPreventer to the application delegate now. Modify “iPhoneInsomniaAppDelegate.h”, to look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#import <UIKit/UIKit.h>

@class iPhoneInsomniaViewController;
@class DeepSleepPreventer;

@interface iPhoneInsomniaAppDelegate : NSObject  {
    UIWindow *window;
    iPhoneInsomniaViewController *viewController;
    DeepSleepPreventer *deepSleepPreventer;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet iPhoneInsomniaViewController *viewController;
@property (nonatomic, retain) DeepSleepPreventer *deepSleepPreventer;

@end

Now move on to “iPhoneInsomniaAppDelegate.m”. We will instantiate and use our DeepSleepPreventer in -applicationDidFinishLaunching:. In your own app, you would do this in a place, where it fits best.

First we need to import our class header file and synthesize our new property:

1
2
3
4
5
6
7
8
9
#import "iPhoneInsomniaAppDelegate.h"
#import "iPhoneInsomniaViewController.h"
#import "DeepSleepPreventer.h"

@implementation iPhoneInsomniaAppDelegate

@synthesize window;
@synthesize viewController;
@synthesize deepSleepPreventer;

Now modify -applicationDidFinishLaunching: to look like this:

1
2
3
4
5
6
7
8
- (void)applicationDidFinishLaunching:(UIApplication *)application {
    // Here we create our deepSleepPreventer and get it to keep our iPhone from deep sleeping
    self.deepSleepPreventer = [[DeepSleepPreventer alloc] init];
    [self.deepSleepPreventer startPreventSleep];

    [window addSubview:viewController.view];
    [window makeKeyAndVisible];
}

And finally the rest of “iPhoneInsomniaAppDelegate.m”. Release our property in -dealloc:

1
2
3
4
5
6
7
8
9
- (void)dealloc {
    [deepSleepPreventer release];
    [viewController release];
    [window release];
    [super dealloc];
}

// Don't forget the matching @end to our @implementation
@end

Well, that’s it! We are done! :)

Use This in Your Own App

Just add the two frameworks “AVFoundation.framework” and “AudioToolbox.framework” to your own app, add the sound file “noSound.wav” and the files “DeepSleepPreventer.h” and “DeepSleepPreventer.m” to it and instantiate and use an object of the “DeepSleepPreventer” class where it fits best, and you’re ready to go.

License

The source code of this how-to is licensed under the New BSD License. Here comes the license text:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Copyright (c) 2009-2010, Marco Peluso - marcopeluso.com
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice,
     this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in the
     documentation and/or other materials provided with the distribution.

  3. Neither the name of the copyright holders nor the names of its
     contributors may be used to endorse or promote products derived from
     this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Issues and Epilog

I couldn’t start my MPMusicPlayer in my music alarm clock app, while I was playing the silent sound with the AVAudioPlayer. So I’m guessing, it’s not possible to play other sounds while this sound plays, at least not via the MPMediaPlayer class.

As I didn’t use any other sounds in my applications yet, I don’t know if this assumption is correct. If you have any more experience with this or any more other useful or relevant information, please leave a comment. I will then update this how-to.

Also, as Shabbir Vijapura pointed out in a comment to my previous post there might be an easier way, to run timers on the iPhone, while it is asleep. I haven’t tested this myself, yet, though, so I cannot guarantee, that this really works. I will test it in the the next couple of days and then update this post, if I find the time.

Update: Apparently this method, doesn’t work, as Shabbir pointed out in the comments.

Furthermore, if you have any experience yourself, dealing with the iPhone’s deep sleep behaviour and would like to share, or if you have any other suggestions or questions, please feel free to leave a comment.

Thanks for reading. I hope this is useful to someone out there.

Downloads

You can download a drop-in piece of code and the silent sound file from GitHub: MMPDeepSleepPreventer on Github

Or you can download the whole demo project, including all source code and the silent sound file: iPhoneInsomnia on GitHub

All the code is licensed under the New BSD License. Feel free, to use it in your own apps. Some attribution would be appreciated.

Timers and Locked Screen/Sleep Issues

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

The first one I’ve encountered while developing my second app “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 is 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 an 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:

There was some code here, that has been 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 #2: 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 #3: Today, I tried out the “audio hack”. It seems to work reliably. 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. :)

Hi There! :)

Thanks for checking by and welcome to my website!

My name is Marco Peluso and I’m an indie software developer. I live in Germany near Frankfurt am Main.

I mainly create apps for iPhone and OS X.

My Twitter name is @marco_peluso. I tweet about my apps, the process of creating them and just random stuff. You’re invited to follow me.

I’m planning on blogging about my development experiences, as well as about everything I may find relevant or simply entertaining, so stay tuned. There’s much more to come!

Hope to see you around. :)