Repository navigation
feat(apple): deliver push through a Notification Service Extension - #2012
Merged
ArnabChatterjee20k merged 6 commits intoOct 13, 2026
Merged
Conversation
A silent push never reaches an app the user swiped away from recents, but a visible push with mutable-content always runs the app's Notification Service Extension. AppwriteNotificationService is that extension's base class: for a placeholder push it connects over MQTT with the app's session, takes the backlog the broker replays, and shows one message in the placeholder's place, queueing the rest for the pushes that follow. No content passes through APNs. The app and the extension share, through the App Group named by AppwritePushAppGroup: the push session and its credential, the delivered-message store, a heartbeat while the app is connected, and the messages the extension fetched. While the app is live the extension does not connect; it replaces the app's latest banner instead, which Push now posts under the message id. On its next start Push delivers what the extension fetched to the subscription callbacks without banners.
🟢 Tier S · Ready to mergeAdds an Apple Notification Service Extension base class that fetches pending push messages over MQTT and substitutes them into visible placeholder notifications. It also persists shared session, heartbeat, pending-message, and handoff state for coordination between the app and extension, and updates the Apple SDK generator and tests accordingly.
📂 Walkthrough · 4
🔇 Filtered out · 1Findings Hansi considered but did not post.
Reviewed |
The project header is added by each generated service call, not kept on the client, so the raw target requests reached the server without it and were refused as an unknown origin.
A killed app keeps a fresh heartbeat for a few seconds, so the extension took it as live and left the placeholder in place without fetching. It now fetches whenever there is no recent app banner to replace.
The extension filled later placeholders from messages it had queued even after the app had taken them over, showing stale content in place of the new message. The app clears the queue when it takes the hand-off, and entries older than 30 seconds are dropped.
…keep queued banners An NSLock only serializes one process, so the app and its extension could overwrite each other's hand-offs and delivered ids. Shared state now also takes a file lock in the App Group container. Clearing the queue on hand-off left the placeholders that follow without their content when the app opened first. Queued messages are kept, used before the live-app check, and dropped after 30 seconds.
Member
Author
|
@hansi-codes review |
ArnabChatterjee20k
merged commit Oct 13, 2026
6a5f51b
into
feat/apple-push-apns
57 of 61 checks passed
23 of 35 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Delivers push to an iOS app that was swiped away from recents, without the message content passing through APNs.
A silent push (#2000, appwrite/appwrite#14302) wakes a suspended app, but iOS never launches an app the user swiped away: "the system does not automatically launch your app if the user has force-quit it" (Apple). A visible push with
mutable-content: 1always shows, and iOS runs the app's Notification Service Extension for it first, even after a swipe-away. This PR makes that extension a one-line class:Usage
push.subscribe(…, background: true)as before.How
Pushkeeps the endpoint, push endpoint, project, client id, and background topics with their titles, while it has background subscriptions.Pushstamps a heartbeat every 3 s while connected; a suspended app stops. While the stamp is fresh (< 10 s), the extension doesn't connect, since the session allows one connection. Instead it replaces the app's latest banner, whichPushnow posts under the message id and marks as its own, so the user sees one banner.subscribe,Pushdelivers the messages the extension fetched to the matching callbacks without banners, since iOS already showed them.Server
The SDK side works with any visible
mutable-contentpush. The server half is on appwrite/appwrite#14302, still to do:Test Plan
swift buildon macOS, iOS Simulator, and Linux (Swift 6.1, the CI image);swift format lint --strict.apns-unique-id 812409cc-…), with no app process running.mutable-contentwork in the Simulator, unlike silent pushes, so nothing here is Simulator-only, but the extension's memory and time limits should be checked on a device.Related PRs and Issues
appwrite init apnsadding the extension target and App Group.