Back to Social App

push-notification

scripts/push-notification/README.md

1.125.03.7 KB
Original Source

push-notification

Sends sample APNS payloads to a booted iOS simulator via xcrun simctl push. Useful for exercising useNotificationsHandler (src/lib/hooks/useNotificationHandler.ts) without a real APNS round-trip — foreground display behavior, tap responses, and notification-driven navigation.

What this does and doesn't cover

Covers — anything in useNotificationsHandler:

  • setNotificationHandler foreground behavior (banner, list, badge, sound flags)
  • addNotificationResponseReceivedListener tap handling
  • Account-switch flow when recipientDid differs from the signed-in account
  • Navigation routing for each reason (post threads, profiles, conversations)

Does not coverBlueskyNSE (the iOS Notification Service Extension):

  • Communication Notification styling for chat messages
  • Badge increment via mutateWithBadge
  • Custom dm.aiff sound for chat messages

The simulator does not reliably invoke NSEs for simctl push on recent iOS versions (verified bypassed on iOS 26.4). To test NSE behavior, run on a real device with a real APNS push, or unit-test NotificationService.didReceive directly in Xcode.

Setup

  1. Boot an iOS simulator and install the app:
    pnpm ios
    
  2. Sign in to the account you'll be testing against. The recipientDid in each payload is substituted at send time and must match the signed-in DID, otherwise:
    • Chat notifications trigger the account-switch flow
    • Other reasons are silently dropped by the handler
  3. Find your DID. Easiest: visit your profile in a web browser and copy it from the URL, or grep dev logs for currentAccount.

Usage

./send.sh <payload-name> [--did <did>] [--device <udid>] [--bundle <id>]

Pass the DID once via env var to avoid repeating it:

export BLUESKY_TEST_DID=did:plc:yourdidhere

./send.sh like
./send.sh chat-message
./send.sh follow

Defaults: --device booted, --bundle xyz.blueskyweb.app. Run ./send.sh --help for the full list of available payloads.

Foreground vs background

useNotificationsHandler behaves differently depending on app state:

  • ForegroundsetNotificationHandler.handleNotification decides whether to show a banner, play a sound, etc. For chat reasons, the banner is suppressed if payload.convoId === currentConvoId (you're already viewing that conversation).
  • Background or tappedaddNotificationResponseReceivedListener fires on tap and runs the navigation routing in notificationToURL.

To test the response listener, background the app first (cmd+shift+H in the sim), send the push, then tap the banner.

Available payloads

PayloadReasonNavigation target
like.apnslikepost thread (from subject)
reply.apnsreplypost thread (from uri)
follow.apnsfollowsender's profile (from uri.host)
chat-message.apnschat-messageMessagesConversation with convoId
chat-reaction.apnschat-reactionMessagesConversation with convoId

The subject AT URIs reference fake post rkeys, so the destination screens will fail to load real content — that's expected. Routing exercises the navigation path, not the data fetch.

Adding a new payload

  1. Copy an existing .apns file in payloads/ whose shape matches.
  2. Set aps.mutable-content: 1 and a real aps.alert.{title,body}.
  3. Match the payload shape to NotificationPayload in src/lib/hooks/useNotificationHandler.ts for the reason you're testing.
  4. Use __RECIPIENT_DID__ as the placeholder for the recipient — send.sh substitutes it at send time. Use it anywhere a DID needs to belong to the logged-in user (typically recipientDid, and post subject for likes/replies).