-
Notifications
You must be signed in to change notification settings - Fork 24.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: over reporting trait changes #39439
fix: over reporting trait changes #39439
Conversation
cffdb07
to
5715b14
Compare
return RCTSharedApplication().delegate.window.traitCollection; | ||
} else { | ||
__block UITraitCollection* traitCollection = nil; | ||
dispatch_sync(dispatch_get_main_queue(), ^{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like this?
@implementation RCTAppearance {
NSString *_currentColorScheme;
}
- (instancetype)init
{
if ((self = [super init])) {
UITraitCollection *traitCollection = RCTSharedApplication().delegate.window.traitCollection;
_currentColorScheme = RCTColorSchemePreference(traitCollection);
}
return self;
}
if ((self = [super init])) { | ||
UITraitCollection *traitCollection = RCTSharedApplication().delegate.window.traitCollection; | ||
_currentColorScheme = RCTColorSchemePreference(traitCollection); | ||
} | ||
return self; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should move the logic from stopObserving
to invalidate
since we always want to keep the listener.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@javache has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator. |
@@ -368,6 +368,9 @@ - (void)contentViewInvalidated | |||
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection | |||
{ | |||
[super traitCollectionDidChange:previousTraitCollection]; | |||
if (RCTSharedApplication().applicationState == UIApplicationStateBackground) { | |||
return; | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, currently traitCollectionDidChange
will 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: Closes facebook#35972 Closes facebook#36713 This PR addresses a couple of issues with `useColorScheme` and the `Appearance` API. - facebook#38214 introduced a regression. Using to `RCTExecuteOnMainQueue` was a mistake as we need this to happen synchronously to return the result. Doing it async causes the `traitCollection` to remain uninitialized. - The `useColorScheme` hook 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 in `traitCollectionDidChange` and not send these events when in the background. - Removed a line that was left over after some OS version checks were removed when support for iOS 12 was dropped. ## Changelog: [IOS] [FIXED] - Don't send the `RCTUserInterfaceStyleDidChangeNotification` when the app is in the background. Pull Request resolved: facebook#39439 Test Plan: Tested on `rn-tester`, logged the changes whenever `useColorScheme` updates. It no longer happens when the app is in the background. The returned interface style on the initial render is always correct now. Reviewed By: NickGerleman Differential Revision: D49454281 Pulled By: javache fbshipit-source-id: 87e24158a49c50608c79e73fb484442f5aad36a6
Summary:
Closes #35972
Closes #36713
This PR addresses a couple of issues with
useColorScheme
and theAppearance
API.RCTExecuteOnMainQueue
was a mistake as we need this to happen synchronously to return the result. Doing it async causes thetraitCollection
to remain uninitialized.useColorScheme
hook 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 intraitCollectionDidChange
and not send these events when in the background.Changelog:
[IOS] [FIXED] - Don't send the
RCTUserInterfaceStyleDidChangeNotification
when the app is in the background.Test Plan:
Tested on
rn-tester
, logged the changes wheneveruseColorScheme
updates. It no longer happens when the app is in the background. The returned interface style on the initial render is always correct now.