June 24, 2026

Keeping a Mac Awake Without Breaking Sleep: IOKit Power Assertions vs pmset

Why 'sudo pmset disablesleep' is a trap for a shippable app, and how a single IOKit power assertion keeps a Mac awake with zero privileges and automatic cleanup on crash.

  • Swift
  • macOS
  • IOKit
Illustration of a battery emitting Zzz sleep symbols being held awake

The naive way, and why it’s dangerous

I built ChargeBeep, a tiny menu-bar app that keeps a Mac awake while it charges and chimes when it hits a target percentage. The single hardest design decision in the whole app was also the least glamorous: how do you stop a Mac from sleeping?

The first answer everyone reaches for is pmset:

sudo pmset disablesleep 1   # ... and later ...
sudo pmset disablesleep 0

This works, and it’s a trap. Two problems, one fatal:

  1. It needs root. Every invocation wants a password, which is a miserable UX for an app that’s supposed to be invisible.
  2. It’s global mutable system state with no owner. If your app sets disablesleep 1 and then crashes — or gets force-quit, or the machine loses power mid-run — before it can set it back to 0, the user’s Mac now never sleeps, forever, with no visible cause. You’ve silently broken their computer and there’s nothing pointing back at you.

Shipping something that can leave the OS in a broken state if your process dies is not acceptable for a paid app. So pmset was out for the default behavior.

The right tool: a power assertion

macOS has a purpose-built API for exactly “please don’t idle-sleep while I’m doing something”: IOKit power assertions.

import IOKit.pwr_mgt

var assertionID: IOPMAssertionID = 0

func keepAwake(_ on: Bool) {
    if on {
        let name = "ChargeBeep keeps the Mac awake while charging" as CFString
        IOPMAssertionCreateWithName(
            kIOPMAssertionTypePreventUserIdleSystemSleep as CFString,
            IOPMAssertionLevel(kIOPMAssertionLevelOn),
            name, &assertionID)
    } else if assertionID != 0 {
        IOPMAssertionRelease(assertionID)
        assertionID = 0
    }
}

Three things make this the correct choice:

  • No privileges. No password, no sudo, nothing. Any app can create an assertion for its own process.
  • It’s owned by your process. The moment your process exits — cleanly, by crash, by kill -9 — the kernel releases every assertion it held. Sleep returns to normal automatically. There is no “stuck” state to leak.
  • It’s honest. The human-readable name you pass shows up in pmset -g assertions, so a curious user can see exactly who’s keeping their Mac awake and why.

The named-assertion string isn’t decoration — it’s the accountability. “Anonymous process prevents sleep” is a support ticket; “ChargeBeep keeps the Mac awake while charging” is self-documenting.

When you genuinely need pmset anyway

A power assertion of type PreventUserIdleSystemSleep keeps the Mac awake with the lid open. It does not keep it awake with the lid closed — that’s clamshell sleep, a different mechanism, and the only way to override it really is pmset disablesleep.

ChargeBeep exposes that as an opt-in “powerful mode.” The trick to making it tolerable is to not prompt for a password every time. Instead, on enabling the mode once, it installs a single narrowly-scoped sudoers rule:

username ALL=(root) NOPASSWD: /usr/bin/pmset disablesleep *

written to /etc/sudoers.d/chargebeep via one admin prompt. After that, the app can toggle only that one command without a password — not a blanket NOPASSWD, just pmset disablesleep. Disabling the mode or uninstalling removes the file and restores sleep. It’s the difference between “trust me with root forever” and “let me run exactly this one command.”

The takeaway

When you need the OS to behave differently, look for the API that scopes the change to your process’s lifetime before you reach for the global flag. pmset disablesleep and IOPMAssertionCreateWithName do almost the same thing — but only one of them cleans up after you when everything goes wrong, and “when everything goes wrong” is exactly when it matters.