Skip to content
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

Add Appearance.setColorScheme support #35989

Closed
wants to merge 10 commits into from

Conversation

birkir
Copy link
Contributor

@birkir birkir commented Jan 27, 2023

Summary

Both Android and iOS allow you to set application specific user interface style, which is useful for applications that support both light and dark mode.

With the newly added Appearance.setColorScheme, you can natively manage the application's user interface style rather than keeping that preference in JavaScript. The benefit is that native dialogs like alert, keyboard, action sheets and more will also be affected by this change.

Implemented using Android X AppCompatDelegate.setDefaultNightMode and iOS 13+ overrideUserInterfaceStyle

Changelog

[GENERAL] [ADDED] - Added setColorScheme to Appearance module

Test Plan

This is a void function so testing is rather limited.

// Lets assume a given device is set to **dark** mode.

Appearance.getColorScheme(); // `dark`

// Set the app's user interface to `light`
Appearance.setColorScheme('light');

Appearance.getColorScheme(); // `light`

// Set the app's user interface to `unspecified`
Appearance.setColorScheme(null);

Appearance.getColorScheme() // `dark`

@facebook-github-bot facebook-github-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 Jan 27, 2023
@facebook-github-bot
Copy link
Contributor

@jacdebug has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@analysis-bot
Copy link

analysis-bot commented Jan 27, 2023

Platform Engine Arch Size (bytes) Diff
android hermes arm64-v8a 8,465,948 -8,400
android hermes armeabi-v7a 7,786,614 -9,381
android hermes x86 8,940,187 -10,050
android hermes x86_64 8,798,247 -9,643
android jsc arm64-v8a 9,649,210 -7,609
android jsc armeabi-v7a 8,383,474 -8,580
android jsc x86 9,711,166 -9,263
android jsc x86_64 10,188,251 -8,832

Base commit: 62fa6d9
Branch: main

@facebook-github-bot
Copy link
Contributor

@jacdebug has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@NickGerleman
Copy link
Contributor

NickGerleman commented Jan 27, 2023

Overall question I wanted to check, are we able to call the API early enough on the JS side to avoid fluttering between the settings when starting the app?

@birkir
Copy link
Contributor Author

birkir commented Jan 28, 2023

Thanks for the feedback, especially on the flow front. I didn’t want to add unspecified to the flow type because it wouldn’t be relevant in getColorScheme, but I will add it there, makes everything more clear.

Overall question I wanted to check, are we able to call the API early enough on the JS side to avoid fluttering between the settings when starting the app?

I have been doing this in multiple apps with my own native module, persisted with AsyncStorage, and haven’t had any issues with timing or fluttering.

I’ve persisted with UserDefaults in a native swift app, but this doesn’t bring any noticeable timing benefit.

Splash screen is always displayed in the system selected theme anyway.

@NickGerleman
Copy link
Contributor

NickGerleman commented Jan 29, 2023

I didn’t want to add unspecified to the flow type because it wouldn’t be relevant in getColorScheme, but I will add it there, makes everything more clear.

I don't think we should add "unspecified" to ColorScheme, since that type is being used for code listening to appearance changes. So they shouldn't need to handle that as a value. But instead we could either make this new API accept ColorScheme | "unspecified", or more idiomatically I think, just ?ColorScheme to allow null to reset.

@birkir
Copy link
Contributor Author

birkir commented Jan 30, 2023

Agree, I updated the PR to reflect this.
Feel free to add revised feedback if needed.

PS. I think the failing test is false positive

Copy link
Contributor

@NickGerleman NickGerleman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly LGTM. Still a little bit around nullability that isn't quite annotated correctly.

It matters less since it is an internal implementation details, but you would also be able to forward null to the native component, vs "unspecified".

Libraries/Utilities/Appearance.d.ts Outdated Show resolved Hide resolved
Libraries/Utilities/Appearance.js Outdated Show resolved Hide resolved
Libraries/Utilities/Appearance.js Outdated Show resolved Hide resolved
Libraries/Utilities/NativeAppearance.js Outdated Show resolved Hide resolved
birkir and others added 4 commits February 1, 2023 09:50
Co-authored-by: Nick Gerleman <nick@nickgerleman.com>
Co-authored-by: Nick Gerleman <nick@nickgerleman.com>
Co-authored-by: Nick Gerleman <nick@nickgerleman.com>
@birkir
Copy link
Contributor Author

birkir commented Feb 1, 2023

Thanks @NickGerleman 👍🏻

@facebook-github-bot
Copy link
Contributor

@jacdebug has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@@ -92,6 +106,17 @@ - (dispatch_queue_t)methodQueue
return std::make_shared<NativeAppearanceSpecJSI>(params);
}

RCT_EXPORT_METHOD(setColorScheme : (NSString *)style) {
UIUserInterfaceStyle userInterfaceStyle = [RCTConvert UIUserInterfaceStyle:style];
NSArray<__kindof UIWindow*>* windows = [[UIApplication sharedApplication] windows];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am trying to import this and hit this error in our internal CI, can you investigate it please?

xplat/js/react-native-github/React/CoreModules/RCTAppearance.mm:112:59: error: 'sharedApplication' is unavailable: not available on iOS (App Extension) - Use view controller based solutions where appropriate instead.
NSArray<__kindof UIWindow *> *windows = [[UIApplication sharedApplication] windows];

Copy link
Contributor

@jacdebug jacdebug left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see the error got while importing.

@birkir
Copy link
Contributor Author

birkir commented Feb 6, 2023

Fixed. Wasn't aware of this change - see #34787

Using RCTSharedApplication now that I believe is fail-safe.

@facebook-github-bot
Copy link
Contributor

@jacdebug has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@facebook-github-bot facebook-github-bot added the Merged This PR has been merged. label Feb 7, 2023
@facebook-github-bot
Copy link
Contributor

@jacdebug merged this pull request in 0a4dcb0.

@@ -66,6 +67,17 @@ private String colorSchemeForCurrentConfiguration(Context context) {
return "light";
}

@Override
public void setColorScheme(String style) {
if (style == "dark") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@birkir I missed that birkir@e633876 changed this from .equals() to ==.

== against two Java strings does a reference comparison, instead of a value comparison. So it is not safe for using to compare string value.

Would you be willing to make a followup change to move this back to .equals()?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good spot and @birkir can you follow up with a test case to cover this please.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there @birkir @jacdebug. Because I think this change may not work, and we don't have any RNTester page or automated tests to verify a fix, I'm going to revert this change.

Do feel free to resubmit this change, with a fix, and test collateral. I do think this API makes sense as part of Appearance.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there @birkir @jacdebug. Because I think this change may not work, and we don't have any RNTester page or automated tests to verify a fix, I'm going to revert this change

Hopefully we'll have it soon (cc @kelset :))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure thing, added RNTester and fixed the bug in a new PR #36122

In retrospect, I think the strict equals bug came about because of coding style change (yoda) and the fact Kotlin uses == the same as Java equals.

facebook-github-bot pushed a commit that referenced this pull request Feb 9, 2023
Summary:
See #35989 (comment)

Changelog:
[General][Fixed] - Back out "Add Appearance.setColorScheme support"

Reviewed By: jacdebug

Differential Revision: D43148056

fbshipit-source-id: 823ab8276207f243b788ce7757839a3e95bdbe07
OlimpiaZurek pushed a commit to OlimpiaZurek/react-native that referenced this pull request May 22, 2023
Summary:
Both Android and iOS allow you to set application specific user interface style, which is useful for applications that support both light and dark mode.

With the newly added `Appearance.setColorScheme`, you can natively manage the application's user interface style rather than keeping that preference in JavaScript. The benefit is that native dialogs like alert, keyboard, action sheets and more will also be affected by this change.

Implemented using Android X [AppCompatDelegate.setDefaultNightMode](https://developer.android.com/reference/androidx/appcompat/app/AppCompatDelegate#setDefaultNightMode(int)) and iOS 13+ [overrideUserInterfaceStyle](https://developer.apple.com/documentation/uikit/uiview/3238086-overrideuserinterfacestyle?language=objc)

## Changelog

[GENERAL] [ADDED] - Added `setColorScheme` to `Appearance` module

Pull Request resolved: facebook#35989

Test Plan:
This is a void function so testing is rather limited.

```tsx
// Lets assume a given device is set to **dark** mode.

Appearance.getColorScheme(); // `dark`

// Set the app's user interface to `light`
Appearance.setColorScheme('light');

Appearance.getColorScheme(); // `light`

// Set the app's user interface to `unspecified`
Appearance.setColorScheme(null);

Appearance.getColorScheme() // `dark`
 ```

Reviewed By: NickGerleman

Differential Revision: D42801094

Pulled By: jacdebug

fbshipit-source-id: ede810fe9ee98f313fd3fbbb16b60c84ef8c7204
OlimpiaZurek pushed a commit to OlimpiaZurek/react-native that referenced this pull request May 22, 2023
Summary:
See facebook#35989 (comment)

Changelog:
[General][Fixed] - Back out "Add Appearance.setColorScheme support"

Reviewed By: jacdebug

Differential Revision: D43148056

fbshipit-source-id: 823ab8276207f243b788ce7757839a3e95bdbe07
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.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants