Skip to content

fix(apple): push edge cases from review - #2024

Merged
ArnabChatterjee20k merged 9 commits into
feat/apple-push-apnsfrom
feat/apple-push-edge-fixes
Oct 14, 2026
Merged

ArnabChatterjee20k merged 9 commits into
feat/apple-push-apnsfrom
feat/apple-push-edge-fixes

Conversation

@ArnabChatterjee20k

@ArnabChatterjee20k ArnabChatterjee20k commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

What does this PR do?

Fixes edge cases found in a review of #2000's push code. Stacked on #2000.

# Edge case Finding Change
1 App live, its banner not posted yet (for example a data-only message) Real. The extension fetched with the same session and client id, and took the app's MQTT connection over. The extension waits up to one heartbeat (3 s + 1 s) for the app's banner or its next heartbeat. Either means the app is running and has the message, so the extension doesn't connect. It fetches only when neither comes, as after a kill. The running app clears the placeholders it has the messages for on each heartbeat.
2 Sign-out ordering: close(), then deleteSession before the target DELETE Not an orphan. DELETE /account/sessions/:id queues DELETE_TYPE_SESSION_TARGETS, and the server deletes the session's targets and their subscribers. The SDK's own DELETE is then redundant (401, ignored). None. Revoking sessions through the Users API queues no cleanup; that's on the appwrite/appwrite#14302 list.
3 AppwritePushAppGroup missing or signed out Real. The extension has no session and leaves the placeholder, silently. A background subscription without the App Group warns once, and so does the extension when it has no session.
4 Background filters change between launches Real. A placeholder was cleared only once all its topics were subscribed again, so one for a dropped or renamed filter stayed. The launch's reconcile (#2022) also clears placeholders for topics no background subscription claims.
5 Environment detection defaults to production Intended. App Store builds have no embedded.mobileprovision and must be production. Only a profile that exists but can't be parsed hits the default. None.
6 A token change re-registers a Push with no background subscription Real. It could re-create a target the reconcile or close() had deleted. A token change registers only Push objects with background subscriptions.
7 Dedup needs a messageId Holds. The server stamps every message with one, and the hand-off relies on the broker's per-session delivery. None.

It also includes #2025 (merged in): the Swift and Apple clients now send Accept-Encoding: gzip. NIO asked for deflate, gzip, and Appwrite answers deflate with raw deflate rather than zlib, so every compressed response failed with DecompressionError.inflationError(-3). That was one of the errors that stopped APNs registration in the device test on #2000. Against a local Appwrite: 60 of 60 account.get() calls failed before the change, and none after.

The PR also puts Deliveries' doc comment back on Deliveries; #2022 had left it above LinkedTopics.

Test Plan

  • 26 Swift Testing tests (1 new: the last heartbeat is kept).
  • swift format lint --strict; macOS, iOS Simulator and Linux (swift:6.1-jammy, from the e2e fixture) builds; Generation suite and Twig lint.
  • Simulator against a real Appwrite, for 1, 3 and 4. The local stack is down; to rerun once it's back up with tools/runbook.php in the test app.

Related PRs and Issues

With the app live but its banner not posted yet, as for a data-only
message, the extension fetched with the same session and took the
app's connection over. It now waits up to a heartbeat for the app's
banner or its next heartbeat, either of which means the app is running
and has the message, and fetches only when neither comes, as after a
kill. The running app clears the placeholders it has the messages for
on each heartbeat.
A placeholder was cleared only once every topic it was for was
subscribed again, so one for a topic a later launch dropped, or whose
filter changed, stayed. The launch's reconcile now clears those too.
…ound subscription

A token change registered every Push that had ever subscribed in the
background, re-creating a target that had been deleted for having none.
Without AppwritePushAppGroup the extension has no session and leaves
the placeholder, silently. A background subscription without it now
warns once, and so does the extension.
@hansi-codes

hansi-codes Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

🟢 Tier S · Ready to merge

The reviewed changes have no actionable defects.

The Apple push templates coordinate notification-extension fetching with app heartbeats, diagnose missing App Group or session setup, and reconcile stale placeholders and targets as background subscriptions change. They also avoid registering APNs targets for Push instances without background subscriptions. The Apple and Swift HTTP clients now explicitly request gzip for decompression-enabled requests while preserving any caller-supplied Accept-Encoding header.

Latest changes: The newest commits add an explicit gzip Accept-Encoding header in both Apple and Swift clients when no caller-provided encoding is present, with Apple respecting its compression setting.

Verdict New comments Fixed Still open
✅ Approved 0 0 0
📂 Walkthrough · 5
File Change
templates/apple/Sources/Client.swift.twig Adds an explicit gzip request header when compression is enabled and no Accept-Encoding header was supplied.
templates/apple/Sources/Services/Push.swift.twig Adds push setup diagnostics and reconciles background subscriptions, APNs targets, and stale placeholders.
templates/apple/Sources/Services/PushServiceExtension.swift.twig Waits for app banner or heartbeat activity before the extension fetches push content.
templates/apple/Tests/PushTests.swift.twig Tests that the latest heartbeat timestamp is retained and read.
templates/swift/Sources/Client.swift.twig Adds an explicit gzip request header when no Accept-Encoding header was supplied.

Reviewed the commits since ea709e6 · Details · Comment @hansi-codes review to re-run, or mention @hansi-codes with a question.

@hansi-codes hansi-codes Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Tier A · See the inline comments. Summary

Comment thread templates/apple/Sources/Services/Push.swift.twig Outdated
…d subscription

The reconcile cleared placeholders only after finding a Push with
background topics, so a launch that dropped them all kept every marked
placeholder. It now clears them before deciding, with nothing claimed.

@hansi-codes hansi-codes Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟢 Tier S · Looks good to merge. Summary

ArnabChatterjee20k and others added 3 commits October 14, 2026 13:48
With no Accept-Encoding of its own, NIO's decompressor asks for
"deflate, gzip". Appwrite answers deflate with a raw deflate stream,
not the zlib stream HTTP's deflate names, so every compressed response
failed with DecompressionError.inflationError(-3). The Swift and Apple
clients now ask for gzip unless the app sets Accept-Encoding.
fix(swift): ask for gzip so compressed responses decode
@ArnabChatterjee20k
ArnabChatterjee20k merged commit 5700d17 into feat/apple-push-apns Oct 14, 2026
58 of 61 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.

1 participant