fix: over reporting trait changes#39439
Conversation
cffdb07 to
5715b14
Compare
There was a problem hiding this comment.
We should avoid using dispatch_sync as much as possible, as it can lead to deadlocks.
This module is marked as requiresMainQueueSetup, which means the constructor is called from the main thread. Is there any way we can access this attribute there?
Also, as applies to most classes in UIKit, this class is not safe to use outside the main thread. We should grab just the userInterfaceStyle prop, and not leak anything else.
There was a problem hiding this comment.
Something like this?
@implementation RCTAppearance {
NSString *_currentColorScheme;
}
- (instancetype)init
{
if ((self = [super init])) {
UITraitCollection *traitCollection = RCTSharedApplication().delegate.window.traitCollection;
_currentColorScheme = RCTColorSchemePreference(traitCollection);
}
return self;
}There was a problem hiding this comment.
Perhaps we should set-up the observer here by default (now in startObserving). Since otherwise _currentColorScheme will go out of date if there's no listeners on the JS side.
18ce003 to
68a4887
Compare
|
7888338 was just backed out, could you rebase this on top of that? |
|
@javache - Yes, that's done, just waiting on CI to finish |
| [[NSNotificationCenter defaultCenter] addObserver:self | ||
| selector:@selector(appearanceChanged:) | ||
| name:RCTUserInterfaceStyleDidChangeNotification | ||
| object:nil]; | ||
| } |
There was a problem hiding this comment.
You should move the logic from stopObserving to invalidate since we always want to keep the listener.
|
@javache has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator. |
| if (RCTSharedApplication().applicationState == UIApplicationStateBackground) { | ||
| return; | ||
| } | ||
|
|
There was a problem hiding this comment.
To confirm: where do we update the trait collection when the application foregrounds? If you change to dark mode while the app is backgrounded, do we pick it up on foreground?
There was a problem hiding this comment.
We'll get it on the foreground but we're in the foreground state before the user sees it so the change isn't visible to them
There was a problem hiding this comment.
The main issue I was trying to solve is when _currentColorScheme was set to nil initially, the default color scheme was light so on first render it would be light regardless of the users settings. So if the users device was in dark mode on initial render it would be reported as light until you toggled it and then it synced up
There was a problem hiding this comment.
Right, the changes to _currentColorScheme makes sense, I'm just a bit confused about how the RCTRootView changes are related here. Is it required as part of this PR?
We'll get it on the foreground but we're in the foreground state
Where does this happen? Does traitCollectionDidChange when the application is foregrounded?
There was a problem hiding this comment.
Yes, currently traitCollectionDidChangewill fire twice when the app moves to the background because the OS is taking the snapshots for the app switcher. It'll take one using the current scheme. Switch to the opposite and then switch back to the original. The changes in RCTRootView is just to ignore those changes.
|
This pull request was successfully merged by @alanjhughes in 6118aff. When will my fix make it into a release? | Upcoming Releases |
Summary: Fixes #49330. We can keep the same logic as paper arch , please see #39439. ## Changelog: [IOS] [FIXED] - Fabric: Fix over reporting trait changes Pull Request resolved: #49431 Test Plan: Repro in #49330. Reviewed By: rshest Differential Revision: D69740128 Pulled By: javache fbshipit-source-id: 2946df68ab7eb88809c21dfcf33ce23f4cd6cb41
Summary:
Closes #35972
Closes #36713
This PR addresses a couple of issues with
useColorSchemeand theAppearanceAPI.RCTExecuteOnMainQueuewas a mistake as we need this to happen synchronously to return the result. Doing it async causes thetraitCollectionto remain uninitialized.useColorSchemehook is updating when the app is in the background on iOS and the OS is taking the snapshots for the app switcher. This causes a flash when returning to the app as the correct color is set again. Here, we can check for the app state intraitCollectionDidChangeand not send these events when in the background.Changelog:
[IOS] [FIXED] - Don't send the
RCTUserInterfaceStyleDidChangeNotificationwhen the app is in the background.Test Plan:
Tested on
rn-tester, logged the changes wheneveruseColorSchemeupdates. It no longer happens when the app is in the background. The returned interface style on the initial render is always correct now.