Finding Distributed Notifications on macOS Catalina
Tags: notifications, distributed, macos, catalina
Published: 2020-04-13 17:02:31

---
title: Finding Distributed Notifications on macOS Catalina
published: true
date: 2020-04-13 16:42:00 UTC
tags: notifications,distributed,macos,catalina
canonical_url: https://medium.com/@technocoder/finding-distributed-notifications-on-macos-catalina-b2a292aac5a1
---

With a new macOS update comes new breaking changes. One particular breaking change on Catalina is how observing distributed notifications works.

### But first, what are distributed notifications?

Many people will be familiar with the concept of regular notifications. You know, that alert that pops up somewhere on your screen that gives you some information about something.

![](https://cdn-images-1.medium.com/max/728/1*T5ulbX3UeAKqlpJM44QVdQ.png)<figcaption>A notification generated using AppleScript</figcaption>

Distributed notifications are similar to regular notifications (in fact, they send the same `NSNotification` structure) but instead of showing them to the user they can be broadcasted to _other processes_ that listen for that particular notification. More generally, distributed notifications are a form of interprocess communication.

### What can we use distributed notifications for?

One very useful notification that iTunes issues is the `com.apple.iTunes.playerInfo` notification. This notification is issued whenever the user changes the state of the current track including when they stop playing or select a new track. Associated with the notification is a bunch of useful fields about the change such as the track name, artist, and total track length.

By registering an observer to this notification I created an iTunes Discord Rich Presence client that automatically updates what song I am currently playing (somewhat similar to Spotify’s Rich Presence).

![](https://cdn-images-1.medium.com/max/578/1*nnMMtEUjodZBJTRJ1DU2Wg.png)<figcaption>iTunes Discord Rich Presence</figcaption>

The main benefit of this compared to polling the iTunes player state with AppleScript is that it uses less processor time and is hence more energy efficient.

### The problem with macOS Catalina

Catalina replaced the beloved iTunes application with Apple Music (which is incidentally a lot slower and laggier but that is a story for another time). Of course, that means the name of any iTunes related distributed notifications have changed.

The fix is simple. Just replace the old notification name with the new one. One of the parameters for [registering the notification observer](https://developer.apple.com/documentation/foundation/distributednotificationcenter/1414136-addobserver) is:

> `notificationName`

> The name of the notification for which to register the observer; that is, only notifications with this name are delivered to the observer. When `nil`, the notification center doesn’t use a notification’s name to decide whether to deliver it to the observer.

Perfect! All we have to do is leave the name as `nil` and that will let us listen to all of the distributed notifications. Then we just have to find the one emitted by changing a track.

![](https://cdn-images-1.medium.com/max/904/1*-3GIbBRqFpy3SvPoki3D-A.png)<figcaption>Code that listens for all distributed notifications</figcaption>

All right, let’s start it up and wait for all the data to pour in.

![](https://cdn-images-1.medium.com/max/232/1*rJ_dCRpV-aplJNF-7AYVKQ.png)<figcaption>It certainly looks like it’s running!</figcaption>

Nothing’s happening?

Here is a hint. The problem isn’t with my code. **This method does not work on macOS Catalina**. The behaviour of the `addObserver` method has changed so that [leaving the name as](https://mjtsai.com/blog/2019/10/04/nsdistributednotificationcenter-no-longer-supports-nil-names/)[`nil` will do nothing if the process is not privileged](https://mjtsai.com/blog/2019/10/04/nsdistributednotificationcenter-no-longer-supports-nil-names/). Incidentally, running the code under `sudo` does not seem to make it work either.

### Finding the name of the notification

At first I considered two main options:

- Tracing the Music application’s system calls
- Brute forcing every possible notification name

The former option was quickly ruled out when `dtruss` (a system call tracer) failed to start due to System Integrity Protection (and I could not be bothered turning it off). The latter option seemed quite feasible but when I did the math:

- 26 lowercase and 26 uppercase letters
- At least ten characters in the name
- 52¹⁰ is about 144 quadrillion operations

Yeah, that is not going to work.

That is when I realised there was another option that finds a lot of use in reverse engineering: analysing the executable itself. Since the notification name is a string we can use the strings tool to find all the strings in the executable.

To test out my idea I tried running `strings` on Spotify first. The bulk of the actual executable is located inside of the application bundle at: `Contents/MacOS/Spotify`. We know that [one of Spotify’s notification names is](https://stackoverflow.com/questions/15659253/receiving-an-event-when-other-applications-itunes-or-spotify-do-something)[`com.spotify.client.PlaybackStateChanged`](https://stackoverflow.com/questions/15659253/receiving-an-event-when-other-applications-itunes-or-spotify-do-something) so we can match that against the output from `strings Spotify`:

![](https://cdn-images-1.medium.com/max/1024/1*55WDudUrp1PAagoi2crRvQ.png)<figcaption>Searching for Spotify’s notification</figcaption>

Great! Let us try doing the same with Apple Music now.

On Catalina the Music application is located in `/System/Applications`. Even though we don’t know the exact name of the notification we can filter out a lot of the strings by matching against `com.apple.Music`. It seems that all of the notifications have to contain the application’s bundle identifier.

![](https://cdn-images-1.medium.com/max/658/1*PmUZWSrbYgyGBne9FtQVOQ.png)<figcaption>List of strings containing the bundle identifier</figcaption>

One of those names looks suspiciously similar to an iTunes notification. In particular: `com.apple.Music.playerInfo`. Let’s try it out!

![](https://cdn-images-1.medium.com/max/954/1*USa_q8CV8rNga3Ir1NGsrA.png)<figcaption>Updated code for a particular notification</figcaption>

![](https://cdn-images-1.medium.com/max/892/1*0LzzgziGikzYqfqAtwSVoQ.png)<figcaption>Output from the notification listener</figcaption>

Brilliant! We finally get some data printed out. It is a little bit different from the iTunes notification (for example, there is no `Display Line 0` field anymore) but fixing that is a comparatively easy task.

### In summary

- Distributed notifications are a way for different processes to send messages to each other
- Catalina breaks using `nil` as a value for the name parameter in `addObserver` to listen for all distributed notifications
- The `strings` tool can be used to find a list of potential notification names for an application by filtering against the bundle identifier

Ideally, Apple should update the documentation to reflect this change in behaviour but it seems that it is too niche to warrant a mention. I am just glad I got my Music Rich Presence working again.