Can APNs wake a sleeping Mac for a third-party app?

I'm building a macOS app and trying to confirm whether there's a way for me to remotely wake a Mac so my app can do a small amount of work (using APNs silent notifications or any other technique).

Here's what I want to happen:

  1. User runs my app on their Mac
  2. User puts the Mac to sleep (Apple menu > Sleep)
  3. 30 minutes later, my server sends a push notification (content-available: 1, apns-push-type: background, apns-priority: 5) via APNs to the Mac
    • Note: Power Nap and Wake for Network Access are enabled
  4. The Mac dark-wakes, delivers the notification to my app via application(_:didReceiveRemoteNotification:), my app gets ~30 seconds to open a WebSocket, do some work, and return
  5. Mac goes back to sleep

So far, I've been able to send silent push notifications to a sleeping Mac, but my app only gets to take action on them after the Mac has been awoken manually. I've tried both silent pushes (content-available: 1, priority 5) and alert pushes (priority 10) with the same result.

After trying every option I can find, I don't believe notifications can wake a sleeping Mac and allow my third-party app to process data, but I really want to be wrong. Can anyone confirm whether or not this is possible?

Answered by DTS Engineer in 890086022

After trying every option I can find, I don't believe notifications can wake a sleeping Mac and allow my third-party app to process data, but I really want to be wrong. Can anyone confirm whether or not this is possible?

I'll cover a few details below but the short summary is that, no, I don't think this will really work, at least not in a way that's reliably tied to the notification itself.

Getting into some specifics, let me start with some background context:

Wake for Network Access

This particular setting actually controls a ethernet hardware level feature called "Wake-on-LAN" (WoL). How this actually works is that the ethernet controller itself passively monitors the bus watching for a very particular packet (the "magic packet"), waking the machine up if it ever receives it.

The critical point to that is that this ISN'T a generic "wake me up for any traffic" mechanism. It has very specific contents (notably, the device MAC address) and generally only originates from with the local network, not from a remote host. It certainly couldn't be used by APNS to wake the device up, at least not in the general case.

Power Nap

...and that's why Power Nap exists. It lets the machine wake up periodically to take care of maintenance tasks which including push notifications. However, the timing of that does mean that there can be significant latency between when you send the push and when it reaches the device.

However, even IF the push reaches the device, I'm not sure this will work:

The Mac dark-wakes, delivers the notification to my app via application(_:didReceiveRemoteNotification:)

Dark wake has always been an odd edge case for macOS. Historically, macOS didn't implement app suspension (it does to some extent to day, but it's still not widely "used"), which mean sleep/wake are largely "system wide" events. That is, as soon as the system "wakes up", your app is "running", at least in theory. However, the system doesn't actually want apps "doing stuff" during Dark Wake- partly for power reasons, but mostly because of the risk that apps could unintentionally alter the interface state (for example, changing the foreground app) because they thought the user was interacting with the system.

Preventing those kinds of issues are what causes this:

So far, I've been able to send silent push notifications to a sleeping Mac, but my app only gets to take action on them after the Mac has been awoken manually.

The push reached your app during dark wake, but the system had disabled normal event delivery to minimize the disruptions dark wake would have otherwise created. Note that this assume your app is an AppKit app. If your MacCatalyst (and possibly SwiftUI), then your would was already suspended and never woke up at all.

Now, what might work is a Notification Service Extension, as our API contract requires the NSE to run before presentation and the easiest place to run it is when receive the notification, even if we don't plan on immediately presenting it. However, that won't run your main app, so there are limits to what that enables.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Accepted Answer

After trying every option I can find, I don't believe notifications can wake a sleeping Mac and allow my third-party app to process data, but I really want to be wrong. Can anyone confirm whether or not this is possible?

I'll cover a few details below but the short summary is that, no, I don't think this will really work, at least not in a way that's reliably tied to the notification itself.

Getting into some specifics, let me start with some background context:

Wake for Network Access

This particular setting actually controls a ethernet hardware level feature called "Wake-on-LAN" (WoL). How this actually works is that the ethernet controller itself passively monitors the bus watching for a very particular packet (the "magic packet"), waking the machine up if it ever receives it.

The critical point to that is that this ISN'T a generic "wake me up for any traffic" mechanism. It has very specific contents (notably, the device MAC address) and generally only originates from with the local network, not from a remote host. It certainly couldn't be used by APNS to wake the device up, at least not in the general case.

Power Nap

...and that's why Power Nap exists. It lets the machine wake up periodically to take care of maintenance tasks which including push notifications. However, the timing of that does mean that there can be significant latency between when you send the push and when it reaches the device.

However, even IF the push reaches the device, I'm not sure this will work:

The Mac dark-wakes, delivers the notification to my app via application(_:didReceiveRemoteNotification:)

Dark wake has always been an odd edge case for macOS. Historically, macOS didn't implement app suspension (it does to some extent to day, but it's still not widely "used"), which mean sleep/wake are largely "system wide" events. That is, as soon as the system "wakes up", your app is "running", at least in theory. However, the system doesn't actually want apps "doing stuff" during Dark Wake- partly for power reasons, but mostly because of the risk that apps could unintentionally alter the interface state (for example, changing the foreground app) because they thought the user was interacting with the system.

Preventing those kinds of issues are what causes this:

So far, I've been able to send silent push notifications to a sleeping Mac, but my app only gets to take action on them after the Mac has been awoken manually.

The push reached your app during dark wake, but the system had disabled normal event delivery to minimize the disruptions dark wake would have otherwise created. Note that this assume your app is an AppKit app. If your MacCatalyst (and possibly SwiftUI), then your would was already suspended and never woke up at all.

Now, what might work is a Notification Service Extension, as our API contract requires the NSE to run before presentation and the easiest place to run it is when receive the notification, even if we don't plan on immediately presenting it. However, that won't run your main app, so there are limits to what that enables.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Thanks for clarifying all the bits that I had jumbled together in my mind. Sleep has always been confusing, so this answer was helpful.

I implemented an NSE but after testing it on two Macs, the NSE runs as the Mac is being awoken, right before notification presentation, but long after the push was sent.

I think I'll just need to accept that when a Mac is sleeping, it's really sleeping and any communication Thanks for your help!

Thanks for clarifying all the bits that I had jumbled together in my mind. Sleep has always been confusing, so this answer was helpful.

One more point here that might help clarify where some of the confusion here comes from. The issue here is, as originally designed, the concept of "sleep" meant VERY different things on macOS and iOS:

  1. macOS -> Turn off as MUCH of the machine as possible, effectively "turning off" the entire machine. Basically, the system’s ONLY goal is that it needs to be able to wake up again and not lose any data. Nothing else really needs to work.

  2. iOS -> Do whatever we can to reduce the system’s overall power usage, while still allowing the system to perform critical communication functions (receive phone calls, messages, etc...).

Over time things like DarkWake have moved macOS somewhat "closer" to iOS, but the basic divergence remains— a "sleeping" iOS device is always doing FAR more than a "sleeping" macOS device. Similarly, that's driven as much by user patterns and requirements, not just an artificial system behavior. macOS devices tend to be used for large blocks of time, broken up by long blocks of complete downtime, while iOS devices tend to be used for shorter amounts of time, much more frequently. macOS sleep is about ensuring the machine is still usable after an "arbitrarily" long time gap (like "the weekend"), while iOS sleep is about extending the device’s "usefulness" over an expected usage cycle.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Can APNs wake a sleeping Mac for a third-party app?
 
 
Q