Skip to content

Add an internal option to skip Native Animated frames while inactive - #58295

Closed
zeyap wants to merge 1 commit into
react:mainfrom
zeyap:export-D118188111
Closed

Add an internal option to skip Native Animated frames while inactive#58295
zeyap wants to merge 1 commit into
react:mainfrom
zeyap:export-D118188111

Conversation

@zeyap

@zeyap zeyap commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Summary:
This is to investigate a crash on ios in C++ Animated rollout.

On iOS, Native Animated drives frames from a CADisplayLink on the main run
loop. While the app is inactive — which includes the whole
UIApplicationWillEnterForegroundUIApplicationDidBecomeActive transition
— it is not presenting, so a frame rendered then is never seen. Its commit and
synchronous per-view updates still run on the main thread though, competing
with the work the app must complete to become responsive.

Adds initWithSkipFramesDuringForegroundTransition: to
RCTAnimatedModuleProvider, declared in a new
RCTAnimatedModuleProvider+Private.h. When YES, _onDisplayLinkTick returns
early while applicationState == UIApplicationStateInactive.

The public API is unchanged. RCTAnimatedModuleProvider.h is untouched and
the C++ API snapshots have no delta — +Private.h is in the ReactApple
exclude_patterns. Plain init still exists and defaults to NO, so every
existing caller is unaffected; only hosts that opt in via the private header see
different behaviour.

Two properties worth being explicit about:

  • The clock is not stopped, only the frame is skipped. AnimationDriver
    computes progress from a timestamp
    (timeDeltaMs = frameTimeMs - startFrameTimeMs_), so the first frame after
    activation resolves to the value the animation should have reached rather
    than resuming from where it was suspended.
  • Frames are not skipped while backgroundedBackground is not
    Inactive. Completion handlers, and any app logic they drive, are therefore
    delayed by at most the length of the transition, not by the time spent in the
    background.

Reading applicationState rather than tracking lifecycle notifications also
avoids a failure mode: a mirrored flag must be cleared on every path out of the
transition, including an abandoned foregrounding (a willEnterForeground with
no following didBecomeActive), or frames are skipped indefinitely. There is no
such state to get stuck here.

UIApplicationStateInactive also covers other non-presenting moments — Control
Center, the app switcher, an incoming call banner. Skipping frames there is
harmless for the same reason: the clock keeps running and the first frame after
activation is correct.

The check sits inside the file's existing TARGET_OS_OSX guard, since
UIApplication is iOS-only and this translation unit also builds for macOS.

Changelog:
[Internal]

Reviewed By: javache, christophpurrer

Differential Revision: D118188111

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Sep 2, 2026
@meta-codesync

meta-codesync Bot commented Sep 2, 2026

Copy link
Copy Markdown

@zeyap has exported this pull request. If you are a Meta employee, you can view the originating Diff in D118188111.

Summary:
This is to investigate a crash on ios in C++ Animated rollout.

On iOS, Native Animated drives frames from a `CADisplayLink` on the main run
loop. While the app is inactive — which includes the whole
`UIApplicationWillEnterForeground` → `UIApplicationDidBecomeActive` transition
— it is not presenting, so a frame rendered then is never seen. Its commit and
synchronous per-view updates still run on the main thread though, competing
with the work the app must complete to become responsive.

Adds `initWithSkipFramesDuringForegroundTransition:` to
`RCTAnimatedModuleProvider`, declared in a new
`RCTAnimatedModuleProvider+Private.h`. When YES, `_onDisplayLinkTick` returns
early while `applicationState == UIApplicationStateInactive`.

**The public API is unchanged.** `RCTAnimatedModuleProvider.h` is untouched and
the C++ API snapshots have no delta — `+Private.h` is in the ReactApple
`exclude_patterns`. Plain `init` still exists and defaults to NO, so every
existing caller is unaffected; only hosts that opt in via the private header see
different behaviour.

Two properties worth being explicit about:

- **The clock is not stopped, only the frame is skipped.** `AnimationDriver`
  computes progress from a timestamp
  (`timeDeltaMs = frameTimeMs - startFrameTimeMs_`), so the first frame after
  activation resolves to the value the animation should have reached rather
  than resuming from where it was suspended.
- **Frames are not skipped while backgrounded** — `Background` is not
  `Inactive`. Completion handlers, and any app logic they drive, are therefore
  delayed by at most the length of the transition, not by the time spent in the
  background.

Reading `applicationState` rather than tracking lifecycle notifications also
avoids a failure mode: a mirrored flag must be cleared on every path out of the
transition, including an abandoned foregrounding (a `willEnterForeground` with
no following `didBecomeActive`), or frames are skipped indefinitely. There is no
such state to get stuck here.

`UIApplicationStateInactive` also covers other non-presenting moments — Control
Center, the app switcher, an incoming call banner. Skipping frames there is
harmless for the same reason: the clock keeps running and the first frame after
activation is correct.

The check sits inside the file's existing `TARGET_OS_OSX` guard, since
`UIApplication` is iOS-only and this translation unit also builds for macOS.

Changelog:
[Internal]

Reviewed By: javache, christophpurrer

Differential Revision: D118188111
@meta-codesync meta-codesync Bot changed the title Add an option to skip Native Animated frames while the app is inactive Add an internal option to skip Native Animated frames while inactive Sep 3, 2026
@zeyap
zeyap force-pushed the export-D118188111 branch from e2a7568 to c29cfeb Compare September 3, 2026 12:59
@meta-codesync meta-codesync Bot closed this in 3f9f385 Sep 3, 2026
@meta-codesync meta-codesync Bot added the Merged This PR has been merged. label Sep 3, 2026
@meta-codesync

meta-codesync Bot commented Sep 3, 2026

Copy link
Copy Markdown

This pull request has been merged in 3f9f385.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged. meta-exported p: Facebook Partner: Facebook Partner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant