12

Using Objective-C, how can I programmatically check the state of the system "Do Not Disturb" setting on OS X? I'm fine with using hacks or private APIs since I don't need to submit to the Mac App Store.

Erik
  • 161
  • 1
  • 5

4 Answers4

7

You can (and should) simply use UserDefaults:

let theDefaults = UserDefaults(suiteName: "com.apple.notificationcenterui")
print(theDefaults?.bool(forKey: "doNotDisturb"))

For time-controlled switching you should check if the minute of the day lies between dndStart and dndEnd:

let theDefaults = UserDefaults(suiteName: "com.apple.notificationcenterui")
let theDate = Date()
let theCalendar = Calendar.current

let theHour = calendar.component(.hour, from: theDate)
let theMinute = calendar.component(.minute, from: theDate)
let theMinuteOfDay = Double(theHour * 60 + theMinute)

if theMinuteOfDay >= theDefaults.double(forKey: "dndStart") && 
    theMinuteOfDay <= theDefaults.double(forKey: "dndEnd") {
    // ...
}
clemens
  • 16,716
  • 11
  • 50
  • 65
  • It works for me in Catalina, too. You should probably check the keys `dndStart` and `dndEnd` for time-controlled switching. – clemens Feb 09 '20 at 15:20
  • thanks for the prompt response, baffling, not working at all. Tried dndStart / dndEnd / doNotDisturb, also tried the `CFPreferencesCopyValue` variant to see if that gets me anything. Nothing. Yet, if I try this on terminal, it works. Could it be something to do with sandboxing? – strangetimes Feb 09 '20 at 15:37
  • If I print out the dictionaryRepresentation of the allocated NSUserDefaults, I can see dozens of keys / values but nothing to do with DND. – strangetimes Feb 09 '20 at 15:38
  • Yes, Sandboxing might definitely a problem. – clemens Feb 09 '20 at 15:41
  • I've verified that this only works if you remove the "App Sandbox" capability from your project's "Signing & Capabilities" settings. Unfortunately removing that capability also means that your app cannot be distributed on the App Store. – alexpls Feb 12 '20 at 08:31
  • 2
    This no longer works in Big Sur. `last-analytics-stamp` is the only entry in `com.apple.notificationcenterui` regardless of DnD settings. – Kornel Feb 13 '21 at 20:10
  • @Kornel have you found a solution for Big Sur? – Paulius Liekis Sep 22 '21 at 14:38
  • @PauliusLiekis Nope. Big Oof. – Kornel Sep 30 '21 at 20:19
  • @Kornel I eventually found a script that works, see this: https://stackoverflow.com/a/69287975/217022 but otherwise the internet is full of solutions that do not :D – Paulius Liekis Sep 30 '21 at 21:22
2

This answer describes how to read and write the state of Do Not Disturb using the command line.

Note that the filename contains your Mac's Hardware UUID. For simplicity, it's a constant in the code below. You can figure it out using the built-in System Information app. There are also different ways to get it programmatically, like this, which I haven’t tried yet.

Using Swift, the content of the plist file can be read as NSDictionary as follows:

import Foundation

// Get path to file
let uuid = "00000000-0000-0000-0000-000000000000"
let filepath = "~/Library/Preferences/ByHost/com.apple.notificationcenterui.\(uuid).plist".stringByExpandingTildeInPath

// Load file as `NSDictionary`
if let dict = NSDictionary(contentsOfFile: filepath) {

    // Get state of Do Not Disturb
    let doNotDisturbState = dict["doNotDisturb"] as? Bool
    println(doNotDisturbState)
}

When I tested it, it sometimes took several seconds for the content of the plist file to be updated, hence you won't get the new state immediately after it changed.

Community
  • 1
  • 1
NexD.
  • 784
  • 6
  • 11
  • 1
    Do you know what the necessary sandbox exceptions for this method would be? – Ben Lachman Mar 11 '16 at 21:23
  • 1
    You should never access user defaults by file directly, because macOS uses a service to manage. Thus, the file's content and the answer of `NSUserDefaults` `valueForKey` may differ. – clemens Nov 15 '16 at 07:13
2

In Objective-C, you can access the value like this:

NSUserDefaults* defaults = [[NSUserDefaults alloc]initWithSuiteName:@"com.apple.notificationcenterui"];
BOOL dnd = [defaults boolForKey:@"doNotDisturb"];
eskimwier
  • 1,037
  • 13
  • 16
  • This no longer works in Big Sur. `last-analytics-stamp` is the only entry in `com.apple.notificationcenterui` regardless of DnD settings. – Kornel Feb 13 '21 at 20:11
0

Swift 4

UserDefaults(suiteName: "com.apple.notificationcenterui")?.bool(forKey: "doNotDisturb")
JanApotheker
  • 1,746
  • 1
  • 17
  • 23
  • 4
    it always returns false – Usman Nisar Apr 11 '18 at 06:42
  • 2
    There is difference between A) DND scheduled via Preferences>Notifications, B) DND turned on manually in the right-side notification area (scroll up to show the DND switch). The B) variant is probably detected correctly, but the A) variant is probably ignored. – Filip Happy Jun 28 '19 at 11:33