36

Is there way to prevent a Mac from going to sleep programmatically using Objective-C? The I/O kit fundamentals section on Apple's dev site tells me that a driver gets notified of an idle / system sleep, but I can't find a way of preventing the system from sleeping. Is it even possible?

I've come across some other solutions using Caffeine, jiggler, sleepless and even AppleScript, but I want to do this in Objective-C. Thanks.

user698769
  • 511
  • 1
  • 4
  • 8

3 Answers3

35

Here is the official Apple documentation (including code snippet):
Technical Q&A QA1340 - How to I prevent sleep?

Quote: Preventing sleep using I/O Kit in Mac OS X 10.6 Snow Leopard:

#import <IOKit/pwr_mgt/IOPMLib.h>

// kIOPMAssertionTypeNoDisplaySleep prevents display sleep,
// kIOPMAssertionTypeNoIdleSleep prevents idle sleep

// reasonForActivity is a descriptive string used by the system whenever it needs 
// to tell the user why the system is not sleeping. For example, 
// "Mail Compacting Mailboxes" would be a useful string.

// NOTE: IOPMAssertionCreateWithName limits the string to 128 characters. 
CFStringRef* reasonForActivity= CFSTR("Describe Activity Type");

IOPMAssertionID assertionID;
IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, 
                                    kIOPMAssertionLevelOn, reasonForActivity, &assertionID); 
if (success == kIOReturnSuccess)
{
    //  Add the work you need to do without 
    //  the system sleeping here.

    success = IOPMAssertionRelease(assertionID);
    //  The system will be able to sleep again. 
}

For older OSX version, check the following:
Technical Q&A QA1160 - How can I prevent system sleep while my application is running?

Quote: Example usage of UpdateSystemActivity (the canonical way for < 10.6)

#include <CoreServices/CoreServices.h>

void
MyTimerCallback(CFRunLoopTimerRef timer, void *info)
{
    UpdateSystemActivity(OverallAct);
}


int
main (int argc, const char * argv[])
{
    CFRunLoopTimerRef timer;
    CFRunLoopTimerContext context = { 0, NULL, NULL, NULL, NULL };

    timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 30, 0, 0, MyTimerCallback, &context);
    if (timer != NULL) {
        CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes);
    }

    /* Start the run loop to receive timer callbacks. You don't need to
    call this if you already have a Carbon or Cocoa EventLoop running. */
    CFRunLoopRun();

    CFRunLoopTimerInvalidate(timer);
    CFRelease(timer);

    return (0);
}
rogerdpack
  • 62,887
  • 36
  • 269
  • 388
Anne
  • 26,765
  • 9
  • 65
  • 71
  • I don't think this work for instance when the macbook lid is closed... How would you prevent sleep then? – David Karlsson Dec 30 '13 at 21:56
  • @DavidKarlsson There are two types of sleep; __idle__ and __forced__. __Idle__ can be controlled by your app, whereas __forced__ cannot. Closing the MacBook lid forces sleep. – Andreas is moving to Codidact Oct 01 '17 at 21:53
  • This answer works to "prevent" sleep if the monitor is currently lit. For the question of how to wake up an already sleeping display see https://stackoverflow.com/questions/10598809/how-do-i-wake-from-display-sleep-in-osx-10-7-4/47838364#47838364 For the question of "lid closed" see https://stackoverflow.com/questions/3315685/how-to-wake-from-sleep-programmatically-if-lid-closed – rogerdpack Dec 15 '17 at 19:01
11

Apple's Q&A1340 replaces Q&A1160. The latest Q&A answers the question "Q: How can my application get notified when the computer is going to sleep or waking from sleep? How do I prevent sleep?"

Listing 2 of Q&A1340:

#import <IOKit/pwr_mgt/IOPMLib.h>

// kIOPMAssertionTypeNoDisplaySleep prevents display sleep,
// kIOPMAssertionTypeNoIdleSleep prevents idle sleep

//reasonForActivity is a descriptive string used by the system whenever it needs 
//  to tell the user why the system is not sleeping. For example, 
//  "Mail Compacting Mailboxes" would be a useful string.

//  NOTE: IOPMAssertionCreateWithName limits the string to 128 characters. 
CFStringRef* reasonForActivity= CFSTR("Describe Activity Type");

IOPMAssertionID assertionID;
IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, 
                                    kIOPMAssertionLevelOn, reasonForActivity, &assertionID); 
if (success == kIOReturnSuccess)
{

    //Add the work you need to do without 
    //  the system sleeping here.

    success = IOPMAssertionRelease(assertionID);
    //The system will be able to sleep again. 
}

Note that you can only stop idle time sleep, not sleep triggered by the user.

For applications supporting Mac OS X 10.6 and later, use the new IOPMAssertion family of functions. These functions allow other applications and utilities to see your application's desire not to sleep; this is critical to working seamlessly with third party power management software.

Graham Miln
  • 2,724
  • 3
  • 33
  • 33
  • On the `CFStringRef*` assignment, XCode gives me "incompatible pointer types". I had to add `(CFStringRef *)` before the `CFSTR()` call to fix it. Also, you might want to mention that one needs to add the `IOKit.framework` to their project. Am I correct on both of these? – Volomike Mar 25 '16 at 19:52
  • Also, in the `IOPMAssertionCreateWithName()` call, I had to add an asterisk `*reasonForActivity` in order to get it to compile. – Volomike Mar 25 '16 at 20:13
  • @Volomike if you feel the code is wrong, please can you [report a bug directly to Apple](http://bugreport.apple.com) as the code listed is from Q&A1340. Once reported, feel free to add the bug number here to allow others to duplicate or reference the bug in their own reports to Apple. – Graham Miln Mar 25 '16 at 20:50
  • @Volomike maybe the corrections you made have the code get compiled, but the IOReturn won't be 0. Instead try to remove the asterisk in the var definition: CFStringRef * reasonForActivity -> CFStringRef reasonForActivity – Jim75 Apr 07 '18 at 20:24
  • 1
    @Volomike: yes, it's a bug in the Apple example. The asterisk should not be there. It must have been `CFStringRef reasonForActivity= CFSTR("Describe Activity Type");`. I'm surprised that no one cared to adjust it all this time. – c00000fd May 18 '23 at 12:44
3

Just create an NSTimer that fires a function with this

UpdateSystemActivity(OverallAct);

I'm pretty sure that that's exactly what Caffeine does.

edc1591
  • 10,146
  • 6
  • 40
  • 63
  • 3
    Please avoid this trick. Instead use the Apple endorsed techniques documented in Q&A1340. – Graham Miln Dec 11 '11 at 01:04
  • 4
    I think he has a point. The “amazing” technique described by Apple is a very poor and crappy solution because you have to embed your code on that thing, making it complex. Now imagine if the code is asynchronous. Also, Apple could not even bother to write the code without errors. Zero stars for Apple. – Duck Dec 18 '14 at 21:53
  • This is deprecated in OSX 10.8. – Volomike Mar 25 '16 at 22:05
  • 1
    @SpaceDog actually is not so complex. You don't have to put your code into the if clause as stated in the sample code comments. Keep the assertionID in a variable and run the IOPMAssertionRelease(assertionID) function later instead, whenever you'd want to reactivate de idle functionality, or never at all. – Jim75 Apr 08 '18 at 13:41