Skip to content

Add auto-dismiss capability to Remote Messaging Framework#4816

Open
SabrinaTardio wants to merge 10 commits into
release/ios/7.220.0from
sabrina/rmf-auto-dismiss
Open

Add auto-dismiss capability to Remote Messaging Framework#4816
SabrinaTardio wants to merge 10 commits into
release/ios/7.220.0from
sabrina/rmf-auto-dismiss

Conversation

@SabrinaTardio
Copy link
Copy Markdown
Contributor

@SabrinaTardio SabrinaTardio commented May 11, 2026

Task/Issue URL: https://app.asana.com/1/137249556945/project/72649045549333/task/1214525802264426
Tech Design URL: https://app.asana.com/1/137249556945/project/481882893211075/task/1214488238400075
CC: N/A

Description

  • Add displayConditions wrapper object to RMF message JSON config, nesting dismissAfterDaysShown and a new trigger field
  • Add MessageTrigger enum and DisplayConditions struct to the domain model; messages with unknown triggers are silently discarded for forward compatibility
  • Extend fetchScheduledRemoteMessage with an optional trigger parameter for filtering messages by their trigger context
  • Thread idle-return context through iOS: HomePageConfiguration.refresh(openedAfterIdle:) passes trigger: .afterIdle when the user returns from idle, and NewTabPageMessagesModel forwards this via an isOpenedAfterIdle closure
  • After-idle messages now appear in both the main NTP and the focussed NTP (suggestion tray), by passing hasEscapeHatch to the focussed NTP in SuggestionTrayViewController
  • Auto-dismiss via dismissAfterDaysShown continues to work as before, now nested under displayConditions

Testing Steps

Test 1 -- Regular message (no trigger, no auto-dismiss):

  1. Apply config from https://pastebin.com/raw/zTS5cAE1 via Debug > Remote Messages > Set source URL, then reset messages and reload
  2. Verify the "Regular Message" appears on the NTP on normal launch
  3. Dismiss it and verify it stays dismissed

Test 2 -- After-idle trigger only:

  1. Apply config from https://pastebin.com/raw/MmpU87dY via Debug > Remote Messages > Set source URL, then reset messages and reload
  2. Kill and relaunch the app normally -- the message should not appear
  3. Trigger idle return (background app, wait for threshold, return) -- the "Welcome Back!" message should appear on the main NTP
  4. Tap the address bar -- the message should also appear in the focussed NTP
  5. Dismiss the escape hatch, open a new tab -- the message should not appear (no idle context)

Test 3 -- Normal app usage after idle return:

  1. After idle return, verify the message appears
  2. Navigate to a URL, then open a new tab -- the message should not appear (no idle context)

Test 4 -- After-idle + auto-dismiss combo:

  1. Apply config from https://pastebin.com/raw/azMhR7mc
  2. Trigger idle return -- "Time-limited idle message" should appear
  3. Move the device date forward by 2+ days, trigger idle return again -- the message should no longer appear (auto-dismissed)

Test 5 -- Unknown trigger (forward compatibility):

  1. Apply config from https://pastebin.com/raw/RhCG3Kaj
  2. The message should never appear -- the unknown trigger causes it to be discarded during mapping

Impact and Risks

Impact Level: Low

What could go wrong?

  • If the escape hatch state is not set before the focussed NTP's onAppear fires, the after-idle message may not show in focussed mode. Mitigated by the fact that setEscapeHatch is called before the view is installed in the hierarchy
  • Messages with unknown triggers are silently dropped, which is the intended forward-compatibility behavior but could cause confusion if a trigger name is misspelled in the server config

Quality Considerations

  • displayConditions is fully backward compatible; messages without it behave identically to before
  • Unknown triggers are discarded at the mapping layer to avoid showing messages at wrong times
  • Unit tests cover: trigger mapping, unknown trigger discard, backward compat, store trigger filtering, auto-dismiss with trigger combo, iOS wiring (HomePageConfiguration and NewTabPageMessagesModel)

Notes to Reviewer

  • The displayConditions wrapper replaces the top-level dismissAfterDaysShown field in the JSON schema. The domain model groups both fields under DisplayConditions
  • The focussed NTP fix was a one-line addition in SuggestionTrayViewController to forward the escape hatch state
  • This is a re-open of Add auto-dismiss capability to Remote Messaging Framework #4743 which was closed when release/ios/7.219.0 was deleted

Made with Cursor


Note

Medium Risk
Medium risk because it changes the Core Data schema and message selection logic (new firstShownDate, trigger-based filtering, and auto-dismiss behavior), which can affect when messages appear or are dismissed.

Overview
Adds display-based delivery controls to remote messages via a new displayConditions JSON object, introducing MessageTrigger/DisplayConditions in the domain model and mapping that discards messages with unknown triggers.

Extends scheduled message fetching to optionally filter by trigger context and to auto-dismiss messages whose dismissAfterDaysShown has elapsed, persisting a new firstShownDate Core Data field (model version bumped to RemoteMessaging 3) and recording it on first show.

Threads the “opened after idle” context through iOS new tab/home message refresh flows so after-idle messages can be surfaced appropriately (including focused NTP), and updates mocks/tests to cover mapping validation, trigger filtering, and auto-dismiss scenarios.

Reviewed by Cursor Bugbot for commit fd39930. Bugbot is set up for automated code reviews on this repo. Configure here.

Add optional dismissAfterDaysShown field to RMF messages, allowing
messages to be automatically dismissed after a configurable number
of days from their first impression.
Wrap dismissAfterDaysShown under a new displayConditions JSON object and add
a trigger field (after_idle) that controls when messages are shown. Thread
idle-return context through iOS HomePageConfiguration and NTP so after-idle
messages appear in both the main and focussed New Tab Page.
- Move auto-dismiss write from read-only context to self.context via
  dismissExpiredMessage helper to avoid stale in-memory state
- Add @available(iOS 16, *) and .timeLimit(.minutes(1)) to new Swift
  Testing tests in HomePageConfigurationTests
- Fix MockRemoteMessagingConfigFetcher to use displayConditions
- Fix stray closing brace in mapper tests
- Change isOpenedAfterIdle from closure to plain Bool since the NTP is
  recreated on every state change and the value is fixed for its lifetime
- Extract displayConditions mapping into mapToDisplayConditions using
  MappingValidator for cleaner parsing
- Add mapEnumIfPresent to MappingValidator for optional enum fields
- Clamp dismissAfterDaysShown values <= 0 to 1
- Add tests for mapEnumIfPresent and dismissAfterDaysShown clamping
Avoids nested performAndWait on the write context from within a
read-only context's performAndWait block. The fetch already skips
expired messages via continue, so the dismiss is a background
side-effect. Updated test to verify the fetch behavior rather than
the synchronous dismiss state.
The fire-and-forget Task means the dismiss may not have completed
when fetchDismissedRemoteMessageIDs is called. The test already
verifies the important behavior: the expired message is not returned
by fetchScheduledRemoteMessage.
@github-actions
Copy link
Copy Markdown
Contributor

Warnings
⚠️ PR has 634 lines of added code (excluding Xcode projects and assets). Consider splitting into smaller PRs if possible.

Generated by 🚫 dangerJS against fd39930

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