Skip to content

feat(go): iOS push notifications via Firebase Cloud Messaging#376

Merged
zomux merged 1 commit into
openagents-org:developfrom
baryhuang:feat/push-notifications-ios
May 12, 2026
Merged

feat(go): iOS push notifications via Firebase Cloud Messaging#376
zomux merged 1 commit into
openagents-org:developfrom
baryhuang:feat/push-notifications-ios

Conversation

@baryhuang
Copy link
Copy Markdown
Collaborator

Summary

Client half of the iOS push-notification system whose backend ships in #375. Pairs the iPhone app with FCM so users receive banners for agent chat, @mentions, terminal status transitions, and errors when they're not actively looking at the affected chat.

  • project.yml — Firebase iOS SDK via SPM (FirebaseCore + FirebaseMessaging, iOS-only), `UIBackgroundModes: remote-notification`, `aps-environment: development` entitlement.
  • AppDelegate.swift (iOS only) — `FirebaseApp.configure()`, permission prompt on first launch, APNs↔FCM token bridging, foreground delegate (banner suppressed when user is on the same chat) + tap-to-deep-link.
  • PushSink.swift — bridges UIKit AppDelegate ↔ SwiftUI router. Fans the FCM token out to every workspace in `WorkspaceHistory` on receipt, so notifications flow from any backend the user has connected to.
  • WorkspaceAPI.registerDeviceToken — `POST /v1/devices/register` mirroring the existing X-Workspace-Token auth pattern.
  • GoogleService-Info.plist placeholder committed; replace with the real plist from the Firebase Console (openagentsweb project, bundle id `com.openagents.go`) before TestFlight / App Store builds.

All push code is wrapped in `#if os(iOS)`; macOS deferred and continues to build clean.

Dependencies

  • Depends on feat(workspace-backend): iOS push-notification fan-out #375 being deployed to whichever backend(s) the iOS app talks to. Older backends will 404 on `POST /v1/devices/register`; `PushSink` logs and moves on.
  • Manual one-time setup before first end-to-end test:
    1. Apple Developer portal → create APNs auth key (.p8).
    2. Firebase Console → openagentsweb → Cloud Messaging → upload the .p8 (Key ID + Team ID, bundle `com.openagents.go`).
    3. Firebase Console → Add iOS app → bundle `com.openagents.go` → download `GoogleService-Info.plist` → replace the placeholder.
    4. Deploy: ensure `FIREBASE_CREDENTIALS_JSON` on every iOS-serving backend is an `openagentsweb` service account with messaging scope.

Test plan

  • iOS Debug builds clean against iOS Simulator destination
  • macOS Debug builds clean (no regression from new SPM deps / entitlements)
  • Real iPhone, real Firebase plist: first-launch permission prompt → Xcode console shows FCM token
  • `SELECT * FROM device_tokens` on backend shows the registered row
  • Foreground, viewing the chat: incoming message → no banner; chat updates via existing polling
  • Foreground, on different chat: incoming message → iOS banner; tap → switches to source channel
  • Backgrounded / locked: lock-screen banner; unlock + tap → app opens to channel
  • @mention in any channel fires regardless of which channel is active
  • /restart → single push for terminal "Session restarted"; no spam for thinking heartbeats
  • Reinstall app → original `device_tokens` row pruned on next push attempt (FCM UNREGISTERED)

Adds the client half of the push-notification system whose backend
shipped in PR openagents-org#375. Pairs the iPhone app with FCM so users receive
banners for agent chat, @mentions, terminal status transitions, and
errors when they're not actively looking at the affected chat.

- project.yml: add Firebase iOS SDK (SPM) with FirebaseCore +
  FirebaseMessaging as iOS-only target dependencies; UIBackgroundModes
  remote-notification and aps-environment development entitlement.
- AppDelegate.swift (iOS only): FirebaseApp.configure(), permission
  prompt on first launch, APNs↔FCM token bridging, foreground/banner
  delegate. Adopted via @UIApplicationDelegateAdaptor.
- PushSink.swift: bridges UIKit AppDelegate ↔ SwiftUI router. Fans
  the FCM token out to every workspace in WorkspaceHistory on receipt,
  so notifications flow from any backend the user has connected to.
- WorkspaceAPI.registerDeviceToken: POST /v1/devices/register.
- GoogleService-Info.plist placeholder committed; replace with the
  real plist from the Firebase Console (openagentsweb project, bundle
  com.openagents.go) before TestFlight/App Store builds.

Verified iOS Debug and macOS Debug builds.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 11, 2026

@baryhuang is attempting to deploy a commit to the Raphael's projects Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Copy Markdown
Contributor

@zomux zomux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Clean Firebase/APNs integration with proper #if os(iOS) gating. Placeholder plist is appropriate — no secrets committed. Minor notes (non-blocking): AppDelegate is included in macOS target build phase (compiles to nothing, just noise), and aps-environment entitlement will need switching to production for release builds.

@zomux zomux merged commit a0cbbaf into openagents-org:develop May 12, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants