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.