You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Pro 1.8.1 / iOS + Android] setTheme without a transition preset blocks the JS thread on the main thread to snapshot the window, then discards it #745
Uniwind.setTheme(theme)without a transition preset still makes the JS thread wait synchronously on the main thread while a full-window snapshot is taken, and that snapshot is then discarded without ever being shown.
Call path (Pro 1.8.1):HybridUniwindConfig::setTheme → NativePlatform.prepareTransition → (when the effective theme changes) NativePlatformTransition.prepareTransition(type, origin), which never looks at type:
iOS: runs in onMainThread { … } (= DispatchQueue.main.sync from the JS thread), then window.snapshotView(afterScreenUpdates: false) and adds it as an overlay.
Android: runs in onMainThread(activity) { … } (runOnUiThread + CountDownLatch.await(2s)), allocates a full-window ARGB_8888 bitmap, fills it via PixelCopy (also latched) or decorView.draw, and adds a RevealOverlay.
animateTransition for ThemeTransitionPreset.NONE (both platforms) then just removes that overlay and runs preAnimationCallback 16 ms later. The overlay never covers a visual change, so for NONE the snapshot plus the blocking JS→main hop is pure overhead.
Impact (iPhone 17, release build, pure cold launch after a device reboot): we call setTheme twice right after first paint. Instruments shows the JS thread parked in prepareTransition → _dispatch_sync_f_slow / __DISPATCH_WAIT_FOR_QUEUE__ while the main thread is still mounting the first frame; a release Hermes wall-clock profile (profiler on) attributes ~480 ms of the JS thread to the setTheme host function, delaying everything queued behind it. We worked around it by deferring our call until the first screen is interactive, but every non-animated setTheme (including following the system appearance) pays this cost.
Suggested fix: in NativePlatformTransition.prepareTransition, return early for .none / NONE (no snapshot/bitmap) and clean up any leftover overlay with a non-blocking main.async / post. animateTransition already handles a missing overlay and still calls overrideAppearance, so NONE behaviour is unchanged. For animated presets the synchronous wait could likely become async too.
Measured on iOS 27 (iPhone 17); the Android path is from reading the 1.8.1 sources, not profiled on a device. I couldn't check 1.9.0's native sources (fetched at install time) — apologies if it's already fixed.
Steps to Reproduce
In a release build, call Uniwind.setTheme('dark') (no transition preset) from JS while the current effective theme is light, ideally right after the first paint on a cold start.
Profile with Instruments (Time Profiler / thread states) or a Hermes sampling profile.
Observe the JS thread blocked in prepareTransition waiting on the main queue (__DISPATCH_WAIT_FOR_QUEUE__) while window.snapshotView runs on the main thread.
Observe that animateTransition with NONE immediately removes the snapshot overlay — it is never visible.
Activity