ReviewOS

also looking at this

craft-native/craft

feat(ios): tell the page about a notification that arrives while the app is open

#259
Merged glennmichael123 wants to merge feat/ios-notification-received into main
11 files +410 -58
8 days ago

Closes #256.

When a push, or any notification, arrived while the app was open, iOS showed the banner through willPresent and the page heard nothing. It only found out if someone tapped the banner. So a page on screen couldn't refresh what the push was about.

Change

  • willPresent now also hands the notification's userInfo to CraftEventManager.handleNotificationReceived, which dispatches craftNotificationReceived. The banner still shows: the page is told, not asked. Taps and arrivals now share one pageData helper, so both have the same shape and one handler can read either.
  • Page: craft.notifications.onReceive(cb) returns an unsubscribe. It's installed next to onTap, so it survives the contract's Object.assign rebuild of craft.notifications. A test reads it off the final object.
  • SDK: pushNotifications.onReceive(cb) goes through the bridge's onReceive, and falls back to the bare event on a bridge that predates it.

Choices

  • Live only, with no replay. A tap that launched the app is held for a late subscriber (#255), because the page wasn't there for it. An arrival matters only to a page that's running. One that subscribes later will load fresh state anyway, so holding arrivals would only replay stale news. A test pins this.
  • The page can't suppress the banner. willPresent must answer through its completion handler right away, and a round trip to the page would delay every banner. If an app wants in-app handling without the banner, that's a separate, opt-in decision.
  • iOS only, like the rest of this push work. On Android the SDK falls back to an event nothing dispatches yet.

Tests

Unit tests: page-script.test.ts (3), index.test.ts (1) and mobile.test.ts (2). Four mutations are each caught:

MutationFails
willPresent doesn't call the managerindex.test.ts
no onReceive on the page3 page-script tests
onReceive holds an early arrival like a tappage-script "does not hold an arrival…"
the SDK fallback listens for the tap eventmobile.test.ts

End to end: the #257 UI test goes on. The tap has left the app in front, so it asks the host for a second simctl push and reads what onReceive handed the page. protocol.ts judges it. The page must be handed that push exactly once, with its route. It must not be handed the cold push, which arrived while the app wasn't running. And it must not be counted as a tap: onTap still has one. Both legs pass locally (iOS 26.5 simulator). With the willPresent call removed, both legs fail, and the shim leg says a page that was open when a push arrived was handed [] by onReceive, expected that push once. 5 new judge cases bring protocol.test.ts to 67.

Harness fixes found while running it

The machine was under heavy load at the time, with a load average of up to 16 from an unrelated job, and three things came up:

  • iOS can remember a notification grant across a reinstall. One leg got granted with no prompt, and the test was waiting for an Allow button that never came. It now taps Allow if the prompt appears, and otherwise goes by the page's answer.

  • The receipt report now exists from the moment the page subscribes. Before, if nothing arrived, the only failure was a timeout. Now it's the precise "handed []" message.

  • The cold banner can come down before its tap lands. A tap waits for SpringBoard to go idle and then looks the banner up again, and under that load it missed once. I tried two fixes and dropped both:

    • Notification Center. On iOS 26 it opens as the Lock Screen, where a tap only offers Open.
    • Retrying the push around a missed tap. A tap that misses ends the test method even inside XCTExpectFailure, and XCTest then reported the test as passed. The harness still failed the leg, because it judges the page's report and not XCTest's verdict.

    So the test taps once, as it did on CI in #257, and a miss fails with a named cause.

Also filed while scoping: #258. A local notification's data is dropped on iOS, so a tap on one hands the page {}.

11 changed files on the files tab, with 0 review threads.

Sign in to comment on this pull request.