Jami Notifications on MacOS client

Here is some ai slop for you guys. But i was able to find the cause and get a patch working on my local systems m4 macbook and m4 mini hopefully someone can use this to fix the notifications. Also i tried to post this to the git issues tracker but you can’t sign up anymore so i couldn’t make one.

# Desktop notifications never appear on macOS (dead `NSUserNotification` API)

**Component:** jami-client-qt (macOS)

**Affected build:** Jami 2.42, `CFBundleVersion` 2026082009, bundle id `cx.ring`

**Platform:** macOS 26.5.2 (arm64, Apple M4), also applies to any macOS 11+

**Qt:** 6.10.1 (bundled)

## Summary

On macOS, Jami never shows a notification banner and never plays a sound for an incoming

message, and Jami never appears in System Settings → Notifications. This is not a

configuration problem and not user error: the client’s notification path terminates in an

Apple API that has been non-functional for several macOS releases.

The client’s own logic is correct end to end. The message is received, the unread

interaction is raised, the tray-icon path is taken, and Qt is called. The notification is

lost in the last step, inside Qt’s cocoa platform plugin, which still uses the removed

`NSUserNotification` API.

## Root cause

The call chain for an incoming message is:

```

ConversationModel::slotMessageReceived (libclient)

-> BehaviorController::newUnreadInteraction

-> ConversationsAdapter::onNewUnreadInteraction   (src/app/conversationsadapter.cpp)

  -> SystemTray::showNotification                 (src/app/systemtray.cpp)

    -> QSystemTrayIcon::showMessage               (QtWidgets)

      -> QCocoaSystemTrayIcon::showMessage        (libqcocoa.dylib, Qt 6.10.1)

        -> \[\[NSUserNotificationCenter defaultUserNotificationCenter\]

                                    deliverNotification:\]     <-- dead API

```

`NSUserNotification` / `NSUserNotificationCenter` were deprecated in macOS 10.14 and no

longer deliver anything on current macOS. Apple’s replacement is

`UserNotifications.framework` (`UNUserNotificationCenter`). Because the app never touches

the modern framework, it also never registers with the notification system, which is why it

is absent from System Settings → Notifications — there is nothing for the user to enable.

`deliverNotification:` returns normally, so there is no error anywhere. The notification is

silently discarded.

## Evidence

**1. The shipped app links no modern notification framework.**

```

$ otool -L Jami.app/Contents/MacOS/Jami | grep -i UserNotifications

(no output)

$ nm -arch arm64 Jami.app/Contents/MacOS/Jami | grep -c UNUserNotificationCenter

0

```

Nor does any of the 88 bundled frameworks. The only references anywhere in the bundle are

to the *deprecated* classes, and they are confined to `libqcocoa.dylib`.

**2. Qt’s cocoa plugin uses the deprecated API.**

Disassembly of `QCocoaSystemTrayIcon::showMessage` in the bundled

`Contents/PlugIns/platforms/libqcocoa.dylib`, with the `__objc_stubs` selectors resolved:

```

+48 objc_alloc_init → NSUserNotification

+80 setTitle:

+100 setInformativeText:

+128 setContentImage:

+140 defaultUserNotificationCenter → NSUserNotificationCenter (class method)

+152 setDelegate:

+172 deliverNotification: ← the dead delivery call

```

**3. The client-side logic is confirmed working.** With lldb breakpoints on each link and a

real incoming message, every stage executes:

```

22:10:51 ConversationModel::slotMessageReceived

22:10:51 BehaviorController::newUnreadInteraction / onNewUnreadInteraction

22:10:51 SystemTray::showNotification

22:10:51 QSystemTrayIcon::showMessage d->visible = 1

22:10:51 QCocoaSystemTrayIcon::showMessage (reached)

```

`Settings::EnableNotifications` is at its default of `true` (confirmed by reading the

defaults jump table in `AppSettingsManager`), and the tray icon is visible (`d->visible ==

1`), so neither of the two guards in this path is responsible.

**4. Confirmed fixable at exactly that seam.** Interposing `deliverNotification:` at

runtime and re-emitting the same title/body through `UNUserNotificationCenter` produces a

correct banner with sound for every incoming message, with no other change to Jami. That is

a local workaround, not a proposed fix, but it isolates the defect to this one call.

## Steps to reproduce

1. Install Jami 2.42 on macOS 11 or newer.

2. Ensure notifications are enabled in Jami’s settings (they are by default).

3. Leave Jami running in the background, with the conversation not selected.

4. Have a contact send a message.

**Expected:** a notification banner with sound; Jami listed in System Settings →

Notifications.

**Actual:** no banner, no sound; Jami absent from System Settings → Notifications. The

message arrives and the unread badge updates normally.

## Suggested fix

The durable fix is for the client to post macOS notifications through

`UserNotifications.framework` rather than relying on `QSystemTrayIcon::showMessage`, which

cannot work on macOS until Qt replaces its own implementation.

A proposed implementation is included alongside this report:

- `macosnotification.h` / `macosnotification.mm` — a small backend that requests

authorization once, posts `UNNotificationRequest`s, and routes clicks back to a callback.

- `systemtray-macos.patch` — the change to `SystemTray::showNotification` that uses it on

macOS and leaves every other platform untouched.

Notes for whoever picks this up:

- `UNUserNotificationCenter` requires the app to be a properly signed bundle. Jami already

is; no entitlement changes are needed. Verified that even an ad-hoc signature with no Team

ID is granted authorization, so this does not depend on the Developer ID.

- A `UNUserNotificationCenterDelegate` implementing `willPresentNotification:` is

**required**, otherwise macOS suppresses banners whenever Jami is the frontmost

application. This is a real behavioural difference from the old API.

- Authorization should be requested once at startup, not at the first message, so the

permission prompt does not appear at a surprising moment.

- `UserNotifications.framework` must be added to the macOS link libraries.

## Related: this is also a Qt bug

`QCocoaSystemTrayIcon::showMessage` in Qt 6.10.1 still uses `NSUserNotification`, so

`QSystemTrayIcon::showMessage()` is a silent no-op on modern macOS for **every** Qt

application, not just Jami. That is worth reporting separately at

https://bugreports.qt.io (component: QPA: Cocoa). Fixing it upstream in Qt would fix Jami

without any client change, but Jami would still be carrying a broken notification path

until it ships a Qt that includes the fix — so the client-side backend above is worth

having regardless.

1 Like