Skip to content

Commit 337dff4

Browse files
authored
fix: implement RCTAppearanceProxy (#2729)
1 parent 90ce605 commit 337dff4

File tree

5 files changed

+154
-20
lines changed

5 files changed

+154
-20
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (c) Microsoft Corporation.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#if TARGET_OS_OSX // [macOS
9+
#import <AppKit/AppKit.h>
10+
11+
NS_ASSUME_NONNULL_BEGIN
12+
13+
@interface RCTAppearanceProxy : NSObject
14+
15+
+ (instancetype)sharedInstance;
16+
17+
/*
18+
* Property to access the current appearance.
19+
* Thread safe.
20+
*/
21+
@property (nonatomic, readonly) NSAppearance *currentAppearance;
22+
23+
- (void)startObservingAppearance;
24+
25+
@end
26+
27+
NS_ASSUME_NONNULL_END
28+
#endif // macOS]
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright (c) Microsoft Corporation.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#if TARGET_OS_OSX // [macOS
9+
#import "RCTAppearanceProxy.h"
10+
11+
#import <React/RCTConstants.h>
12+
#import <React/RCTUtils.h>
13+
14+
#import <mutex>
15+
16+
@implementation RCTAppearanceProxy {
17+
BOOL _isObserving;
18+
std::mutex _mutex;
19+
NSAppearance *_currentAppearance;
20+
}
21+
22+
+ (instancetype)sharedInstance
23+
{
24+
static RCTAppearanceProxy *sharedInstance = nil;
25+
static dispatch_once_t onceToken;
26+
dispatch_once(&onceToken, ^{
27+
sharedInstance = [RCTAppearanceProxy new];
28+
});
29+
return sharedInstance;
30+
}
31+
32+
- (instancetype)init
33+
{
34+
self = [super init];
35+
if (self) {
36+
_isObserving = NO;
37+
_currentAppearance = [NSApp effectiveAppearance];
38+
}
39+
return self;
40+
}
41+
42+
- (void)startObservingAppearance
43+
{
44+
RCTAssertMainQueue();
45+
std::lock_guard<std::mutex> lock(_mutex);
46+
if (!_isObserving) {
47+
_isObserving = YES;
48+
[[NSNotificationCenter defaultCenter] addObserver:self
49+
selector:@selector(_appearanceDidChange:)
50+
name:RCTUserInterfaceStyleDidChangeNotification
51+
object:nil];
52+
}
53+
}
54+
55+
- (NSAppearance *)currentAppearance
56+
{
57+
{
58+
std::lock_guard<std::mutex> lock(_mutex);
59+
if (_isObserving) {
60+
return _currentAppearance;
61+
}
62+
}
63+
64+
__block NSAppearance *appearance = nil;
65+
if (RCTIsMainQueue()) {
66+
appearance = [NSApp effectiveAppearance];
67+
} else {
68+
dispatch_sync(dispatch_get_main_queue(), ^{
69+
appearance = [NSApp effectiveAppearance];
70+
});
71+
}
72+
return appearance;
73+
}
74+
75+
- (void)_appearanceDidChange:(NSNotification *)notification
76+
{
77+
std::lock_guard<std::mutex> lock(_mutex);
78+
79+
NSDictionary *userInfo = [notification userInfo];
80+
if (userInfo) {
81+
NSAppearance *appearance = userInfo[RCTUserInterfaceStyleDidChangeNotificationAppearanceKey];
82+
if (appearance != nil) {
83+
_currentAppearance = appearance;
84+
return;
85+
}
86+
}
87+
88+
_currentAppearance = [NSApp effectiveAppearance];
89+
}
90+
91+
@end
92+
#endif // macOS]

packages/react-native/React/Base/UIKitProxies/RCTInitializeUIKitProxies.mm

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
#import "RCTKeyWindowValuesProxy.h"
1111
#import "RCTTraitCollectionProxy.h"
1212
#import "RCTWindowSafeAreaProxy.h"
13+
#if TARGET_OS_OSX // [macOS
14+
#import "RCTAppearanceProxy.h"
15+
#endif // macOS]
1316

1417
void RCTInitializeUIKitProxies(void)
1518
{
@@ -19,7 +22,9 @@ void RCTInitializeUIKitProxies(void)
1922
#if !TARGET_OS_OSX // [macOS]
2023
[[RCTTraitCollectionProxy sharedInstance] startObservingTraitCollection];
2124
[[RCTInitialAccessibilityValuesProxy sharedInstance] recordAccessibilityValues];
22-
#endif // [macOS]
25+
#else // [macOS
26+
[[RCTAppearanceProxy sharedInstance] startObservingAppearance];
27+
#endif // macOS]
2328
[[RCTKeyWindowValuesProxy sharedInstance] startObservingWindowSizeIfNecessary];
2429
});
2530
}

packages/react-native/React/CoreModules/RCTAppearance.mm

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414

1515
#import "CoreModulesPlugins.h"
1616

17+
#if TARGET_OS_OSX // [macOS
18+
#import <React/RCTUtils.h>
19+
20+
#import "RCTAppearanceProxy.h"
21+
#endif // macOS]
22+
1723
using namespace facebook::react;
1824

1925
NSString *const RCTAppearanceColorSchemeLight = @"light";
@@ -119,7 +125,7 @@ - (instancetype)init
119125
UITraitCollection *traitCollection = [RCTTraitCollectionProxy sharedInstance].currentTraitCollection;
120126
_currentColorScheme = RCTColorSchemePreference(traitCollection);
121127
#else // [macOS
122-
NSAppearance *appearance = RCTSharedApplication().appearance;
128+
NSAppearance *appearance = [RCTAppearanceProxy sharedInstance].currentAppearance;
123129
_currentColorScheme = RCTColorSchemePreference(appearance);
124130
#endif // macOS]
125131
[[NSNotificationCenter defaultCenter] addObserver:self
@@ -134,7 +140,11 @@ - (instancetype)init
134140

135141
+ (BOOL)requiresMainQueueSetup
136142
{
143+
#if !TARGET_OS_OSX // [macOS]
137144
return NO;
145+
#else // [macOS
146+
return YES;
147+
#endif // macOS]
138148
}
139149

140150
- (dispatch_queue_t)methodQueue
@@ -160,13 +170,15 @@ - (dispatch_queue_t)methodQueue
160170
window.overrideUserInterfaceStyle = userInterfaceStyle;
161171
}
162172
#else // [macOS
163-
NSAppearance *appearance = nil;
164-
if ([style isEqualToString:@"light"]) {
165-
appearance = [NSAppearance appearanceNamed:NSAppearanceNameAqua];
166-
} else if ([style isEqualToString:@"dark"]) {
167-
appearance = [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua];
168-
}
169-
RCTSharedApplication().appearance = appearance;
173+
RCTExecuteOnMainQueue(^{
174+
NSAppearance *appearance = nil;
175+
if ([style isEqualToString:@"light"]) {
176+
appearance = [NSAppearance appearanceNamed:NSAppearanceNameAqua];
177+
} else if ([style isEqualToString:@"dark"]) {
178+
appearance = [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua];
179+
}
180+
RCTSharedApplication().appearance = appearance;
181+
});
170182
#endif // macOS]
171183
}
172184

@@ -177,10 +189,7 @@ - (dispatch_queue_t)methodQueue
177189
UITraitCollection *traitCollection = [RCTTraitCollectionProxy sharedInstance].currentTraitCollection;
178190
_currentColorScheme = RCTColorSchemePreference(traitCollection);
179191
#else // [macOS
180-
__block NSAppearance *appearance = nil;
181-
RCTUnsafeExecuteOnMainQueueSync(^{
182-
appearance = RCTKeyWindow().appearance;
183-
});
192+
NSAppearance *appearance = [RCTAppearanceProxy sharedInstance].currentAppearance;
184193
_currentColorScheme = RCTColorSchemePreference(appearance);
185194
#endif // macOS]
186195
}
@@ -190,23 +199,19 @@ - (dispatch_queue_t)methodQueue
190199

191200
- (void)appearanceChanged:(NSNotification *)notification
192201
{
202+
#if !TARGET_OS_OSX // [macOS
193203
NSDictionary *userInfo = [notification userInfo];
194-
#if !TARGET_OS_OSX // [macOS]
195204
UITraitCollection *traitCollection = nil;
196205
if (userInfo) {
197206
traitCollection = userInfo[RCTUserInterfaceStyleDidChangeNotificationTraitCollectionKey];
198207
}
199208
NSString *newColorScheme = RCTColorSchemePreference(traitCollection);
200209
#else // [macOS
201-
NSAppearance *appearance = nil;
202-
if (userInfo) {
203-
appearance = userInfo[RCTUserInterfaceStyleDidChangeNotificationAppearanceKey];
204-
}
205-
NSString *newColorScheme = RCTColorSchemePreference(appearance);
210+
NSString *newColorScheme = RCTColorSchemePreference([RCTAppearanceProxy sharedInstance].currentAppearance);
206211
#endif // macOS]
207212
if (![_currentColorScheme isEqualToString:newColorScheme]) {
208213
_currentColorScheme = newColorScheme;
209-
[self sendEventWithName:@"appearanceChanged" body:@{@"colorScheme" : newColorScheme}];
214+
[self sendEventWithName:@"appearanceChanged" body:@{ @"colorScheme" : newColorScheme }];
210215
}
211216
}
212217

packages/react-native/React/CoreModules/RCTDeviceInfo.mm

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ - (instancetype)init
5050

5151
+ (BOOL)requiresMainQueueSetup
5252
{
53+
#if !TARGET_OS_OSX // [macOS]
5354
return NO;
55+
#else // [macOS
56+
return YES;
57+
#endif // macOS]
5458
}
5559

5660
- (dispatch_queue_t)methodQueue

0 commit comments

Comments
 (0)