Skip to content

Commit cbaff1c

Browse files
zhongwuzwfacebook-github-bot
authored andcommitted
Fabric: Fixes crash of dynamic color when light/dark mode changed (#48496)
Summary: The reason is when light/dark mode changed, the `hash` value also changed because we used `color.getColor()`. leads to size balanced break. ``` Assertion failed: (index_.size() == lru_.size()), function size, file EvictingCacheMap.h, line 439. (lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT frame #0: 0x00000001089a9108 libsystem_kernel.dylib`__pthread_kill + 8 frame #1: 0x0000000105de3408 libsystem_pthread.dylib`pthread_kill + 256 frame #2: 0x000000018016c4ec libsystem_c.dylib`abort + 104 frame #3: 0x000000018016b934 libsystem_c.dylib`__assert_rtn + 268 * frame #4: 0x00000001073e386c React_FabricComponents`folly::EvictingCacheMap<facebook::react::AttributedString, std::__1::shared_ptr<void>, folly::HeterogeneousAccessHash<facebook::react::AttributedString, void>, folly::HeterogeneousAccessEqualTo<facebook::react::AttributedString, void>>::size(this=0x0000600003900348) const at EvictingCacheMap.h:439:5 frame #5: 0x00000001073e34f4 React_FabricComponents`void folly::EvictingCacheMap<facebook::react::AttributedString, std::__1::shared_ptr<void>, folly::HeterogeneousAccessHash<facebook::react::AttributedString, void>, folly::HeterogeneousAccessEqualTo<facebook::react::AttributedString, void>>::setImpl<facebook::react::AttributedString>(this=0x0000600003900348, key=0x000000016b9f20a8, value=nullptr, promote=true, pruneHook=folly::EvictingCacheMap<facebook::react::AttributedString, std::__1::shared_ptr<void>, folly::HeterogeneousAccessHash<facebook::react::AttributedString, void>, folly::HeterogeneousAccessEqualTo<facebook::react::AttributedString, void> >::PruneHookCall @ 0x000000016b9f1cc8) at EvictingCacheMap.h:674:27 frame #6: 0x00000001073deb88 React_FabricComponents`folly::EvictingCacheMap<facebook::react::AttributedString, std::__1::shared_ptr<void>, folly::HeterogeneousAccessHash<facebook::react::AttributedString, void>, folly::HeterogeneousAccessEqualTo<facebook::react::AttributedString, void>>::set(this=0x0000600003900348, key=0x000000016b9f20a8, value=ptr = 0x60000024ae20 strong=2 weak=1, promote=true, pruneHook=folly::EvictingCacheMap<facebook::react::AttributedString, std::__1::shared_ptr<void>, folly::HeterogeneousAccessHash<facebook::react::AttributedString, void>, folly::HeterogeneousAccessEqualTo<facebook::react::AttributedString, void> >::PruneHookCall @ 0x000000016b9f1d98) at EvictingCacheMap.h:346:5 frame #7: 0x00000001073d91dc React_FabricComponents`facebook::react::SimpleThreadSafeCache<facebook::react::AttributedString, std::__1::shared_ptr<void>, 256>::get(this=0x0000600003900348, key=0x000000016b9f20a8, generator= Lambda in File RCTTextLayoutManager.mm at Line 337) const at SimpleThreadSafeCache.h:40:12 frame #8: 0x00000001073d9058 React_FabricComponents`-[RCTTextLayoutManager _nsAttributedStringFromAttributedString:](self=0x0000600003900340, _cmd="_nsAttributedStringFromAttributedString:", attributedString=AttributedString @ 0x000000016b9f20a8) at RCTTextLayoutManager.mm:337:42 frame #9: 0x00000001073d6378 React_FabricComponents`-[RCTTextLayoutManager drawAttributedString:paragraphAttributes:frame:drawHighlightPath:](self=0x0000600003900340, _cmd="drawAttributedString:paragraphAttributes:frame:drawHighlightPath:", attributedString=AttributedString @ 0x000000016b9f23a8, paragraphAttributes=ParagraphAttributes @ 0x000000016b9f2378, frame=(origin = (x = 0, y = 0), size = (width = 92, height = 21.666748046875)), block=0x00000001061602d0) at RCTTextLayoutManager.mm:73:56 frame #10: 0x000000010616020c RCTFabric`-[RCTParagraphTextView drawRect:](self=0x000000012beb9dc0, _cmd="drawRect:", rect=(origin = (x = 0, y = 0.000081380208335701809), size = (width = 92, height = 21.666666666666664))) at RCTParagraphComponentView.mm:346:3 frame #11: 0x0000000186043e60 UIKitCore`-[UIView(CALayerDelegate) drawLayer:inContext:] + 584 frame #12: 0x000000018af40080 QuartzCore`CABackingStoreUpdate_ + 244 frame #13: 0x000000018b0bec88 QuartzCore`invocation function for block in CA::Layer::display_() + 108 frame #14: 0x000000018b0b5524 QuartzCore`-[CALayer _display] + 1596 frame #15: 0x000000018b0c7e74 QuartzCore`CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 392 frame #16: 0x000000018affca50 QuartzCore`CA::Context::commit_transaction(CA::Transaction*, double, double*) + 464 frame #17: 0x000000018b02b260 QuartzCore`CA::Transaction::commit() + 652 frame #18: 0x000000018b02c7b4 QuartzCore`CA::Transaction::flush_as_runloop_observer(bool) + 68 frame #19: 0x0000000185ad6c1c UIKitCore`_UIApplicationFlushCATransaction + 48 frame #20: 0x0000000185a07ccc UIKitCore`__setupUpdateSequence_block_invoke_2 + 352 frame #21: 0x000000018505d28c UIKitCore`_UIUpdateSequenceRun + 76 frame #22: 0x0000000185a07670 UIKitCore`schedulerStepScheduledMainSection + 168 frame #23: 0x0000000185a06aa8 UIKitCore`runloopSourceCallback + 80 frame #24: 0x000000018041b7c4 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24 frame #25: 0x000000018041b70c CoreFoundation`__CFRunLoopDoSource0 + 172 frame #26: 0x000000018041ae70 CoreFoundation`__CFRunLoopDoSources0 + 232 frame #27: 0x00000001804153b4 CoreFoundation`__CFRunLoopRun + 788 frame #28: 0x0000000180414c24 CoreFoundation`CFRunLoopRunSpecific + 552 frame #29: 0x000000019020ab10 GraphicsServices`GSEventRunModal + 160 frame #30: 0x0000000185ad82fc UIKitCore`-[UIApplication _run] + 796 frame #31: 0x0000000185adc4f4 UIKitCore`UIApplicationMain + 124 frame #32: 0x0000000104521f68 RNTester.debug.dylib`main(argc=1, argv=0x000000016b9f5af8) at main.m:15:12 frame #33: 0x00000001045b9410 dyld_sim`start_sim + 20 frame #34: 0x0000000104796274 dyld`start + 2840 ``` ## Changelog: [IOS] [FIXED] - Fabric: Fixes crash of dynamic color when light/dark mode changed Pull Request resolved: #48496 Test Plan: RNTester -> PlatformColor example -> changed the dark/light mode in the system settings -> go back to App and pop and push the PlatformColor example, it would crash: ![Simulator Screen Recording - iPhone 16 - 2025-01-05 at 15 46 08](https://github.com/user-attachments/assets/f6faaf80-ad03-49c6-9a56-b1117bdc2659) Reviewed By: sammy-SC Differential Revision: D68157559 Pulled By: cipolleschi fbshipit-source-id: 01959845b742ce748186d3877b2792f0f9132ff5
1 parent ff2e403 commit cbaff1c

File tree

7 files changed

+167
-20
lines changed

7 files changed

+167
-20
lines changed

packages/react-native/ReactCommon/react/renderer/graphics/platform/ios/react/renderer/graphics/HostPlatformColor.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,12 @@ struct Color {
2424
Color(int32_t color);
2525
Color(const DynamicColor& dynamicColor);
2626
Color(const ColorComponents& components);
27-
Color(std::shared_ptr<void> uiColor);
27+
Color() : uiColor_(nullptr){};
2828
int32_t getColor() const;
29+
int32_t getUIColorHash() const;
30+
31+
static Color createSemanticColor(std::vector<std::string>& semanticItems);
32+
2933
std::shared_ptr<void> getUIColor() const {
3034
return uiColor_;
3135
}
@@ -48,6 +52,7 @@ struct Color {
4852
}
4953

5054
private:
55+
Color(std::shared_ptr<void> uiColor);
5156
std::shared_ptr<void> uiColor_;
5257
};
5358

@@ -59,7 +64,7 @@ namespace HostPlatformColor {
5964
#define NO_DESTROY
6065
#endif
6166

62-
NO_DESTROY static const facebook::react::Color UndefinedColor = Color(nullptr);
67+
NO_DESTROY static const facebook::react::Color UndefinedColor = Color();
6368
} // namespace HostPlatformColor
6469

6570
inline Color
@@ -103,8 +108,6 @@ inline float blueFromHostPlatformColor(Color color) {
103108
template <>
104109
struct std::hash<facebook::react::Color> {
105110
size_t operator()(const facebook::react::Color& color) const {
106-
auto seed = size_t{0};
107-
facebook::react::hash_combine(seed, color.getColor());
108-
return seed;
111+
return color.getUIColorHash();
109112
}
110113
};

packages/react-native/ReactCommon/react/renderer/graphics/platform/ios/react/renderer/graphics/HostPlatformColor.mm

Lines changed: 109 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
*/
77

88
#import "HostPlatformColor.h"
9+
#import "UIColor+Graphics.h"
910

1011
#import <Foundation/Foundation.h>
1112
#import <UIKit/UIKit.h>
13+
#import <react/renderer/graphics/RCTPlatformColorUtils.h>
1214
#import <react/utils/ManagedObjectWrapper.h>
1315
#import <string>
1416

@@ -19,13 +21,31 @@
1921
namespace facebook::react {
2022

2123
namespace {
24+
25+
bool UIColorIsP3ColorSpace(const std::shared_ptr<void> &uiColor)
26+
{
27+
UIColor *color = unwrapManagedObject(uiColor);
28+
CGColorSpaceRef colorSpace = CGColorGetColorSpace(color.CGColor);
29+
30+
if (CGColorSpaceGetModel(colorSpace) == kCGColorSpaceModelRGB) {
31+
CFStringRef name = CGColorSpaceGetName(colorSpace);
32+
if (name != NULL && CFEqual(name, kCGColorSpaceDisplayP3)) {
33+
return true;
34+
}
35+
}
36+
return false;
37+
}
38+
2239
UIColor *_Nullable UIColorFromInt32(int32_t intColor)
2340
{
2441
CGFloat a = CGFloat((intColor >> 24) & 0xFF) / 255.0;
2542
CGFloat r = CGFloat((intColor >> 16) & 0xFF) / 255.0;
2643
CGFloat g = CGFloat((intColor >> 8) & 0xFF) / 255.0;
2744
CGFloat b = CGFloat(intColor & 0xFF) / 255.0;
28-
return [UIColor colorWithRed:r green:g blue:b alpha:a];
45+
46+
UIColor *color = [UIColor colorWithRed:r green:g blue:b alpha:a];
47+
color.reactHash = facebook::react::hash_combine(intColor, 0);
48+
return color;
2949
}
3050

3151
UIColor *_Nullable UIColorFromDynamicColor(const facebook::react::DynamicColor &dynamicColor)
@@ -56,6 +76,7 @@
5676
}
5777
}
5878
}];
79+
color.reactHash = facebook::react::hash_combine(dark, light, highContrastDark, highContrastLight, 0);
5980
return color;
6081
} else {
6182
return nil;
@@ -64,37 +85,95 @@
6485
return nil;
6586
}
6687

67-
int32_t ColorFromUIColor(UIColor *color)
88+
int32_t ColorFromColorComponents(const facebook::react::ColorComponents &components)
6889
{
6990
float ratio = 255;
91+
auto color = ((int32_t)round((float)components.alpha * ratio) & 0xff) << 24 |
92+
((int)round((float)components.red * ratio) & 0xff) << 16 |
93+
((int)round((float)components.green * ratio) & 0xff) << 8 | ((int)round((float)components.blue * ratio) & 0xff);
94+
return color;
95+
}
96+
97+
int32_t ColorFromUIColor(UIColor *color)
98+
{
7099
CGFloat rgba[4];
71100
[color getRed:&rgba[0] green:&rgba[1] blue:&rgba[2] alpha:&rgba[3]];
72-
return ((int32_t)round((float)rgba[3] * ratio) & 0xff) << 24 | ((int)round((float)rgba[0] * ratio) & 0xff) << 16 |
73-
((int)round((float)rgba[1] * ratio) & 0xff) << 8 | ((int)round((float)rgba[2] * ratio) & 0xff);
101+
return ColorFromColorComponents({(float)rgba[0], (float)rgba[1], (float)rgba[2], (float)rgba[3]});
74102
}
75103

76-
int32_t ColorFromUIColor(const std::shared_ptr<void> &uiColor)
104+
int32_t ColorFromUIColorForSpecificTraitCollection(
105+
const std::shared_ptr<void> &uiColor,
106+
UITraitCollection *traitCollection)
77107
{
78108
UIColor *color = (UIColor *)unwrapManagedObject(uiColor);
79109
if (color) {
80-
UITraitCollection *currentTraitCollection = [UITraitCollection currentTraitCollection];
81-
color = [color resolvedColorWithTraitCollection:currentTraitCollection];
110+
color = [color resolvedColorWithTraitCollection:traitCollection];
82111
return ColorFromUIColor(color);
83112
}
84113

85114
return 0;
86115
}
87116

117+
int32_t ColorFromUIColor(const std::shared_ptr<void> &uiColor)
118+
{
119+
return ColorFromUIColorForSpecificTraitCollection(uiColor, [UITraitCollection currentTraitCollection]);
120+
}
121+
88122
UIColor *_Nullable UIColorFromComponentsColor(const facebook::react::ColorComponents &components)
89123
{
124+
UIColor *uiColor = nil;
90125
if (components.colorSpace == ColorSpace::DisplayP3) {
91-
return [UIColor colorWithDisplayP3Red:components.red
92-
green:components.green
93-
blue:components.blue
94-
alpha:components.alpha];
126+
uiColor = [UIColor colorWithDisplayP3Red:components.red
127+
green:components.green
128+
blue:components.blue
129+
alpha:components.alpha];
130+
} else {
131+
uiColor = [UIColor colorWithRed:components.red green:components.green blue:components.blue alpha:components.alpha];
132+
}
133+
134+
auto color = ColorFromColorComponents(components);
135+
uiColor.reactHash = facebook::react::hash_combine(color, components.colorSpace == ColorSpace::DisplayP3);
136+
137+
return uiColor;
138+
}
139+
140+
int32_t hashFromUIColor(const std::shared_ptr<void> &uiColor)
141+
{
142+
if (uiColor == nullptr) {
143+
return 0;
95144
}
96-
return [UIColor colorWithRed:components.red green:components.green blue:components.blue alpha:components.alpha];
145+
146+
static UITraitCollection *darkModeTraitCollection =
147+
[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
148+
auto darkColor = ColorFromUIColorForSpecificTraitCollection(uiColor, darkModeTraitCollection);
149+
150+
static UITraitCollection *lightModeTraitCollection =
151+
[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight];
152+
auto lightColor = ColorFromUIColorForSpecificTraitCollection(uiColor, lightModeTraitCollection);
153+
154+
static UITraitCollection *darkModeAccessibilityContrastTraitCollection =
155+
[UITraitCollection traitCollectionWithTraitsFromCollections:@[
156+
darkModeTraitCollection,
157+
[UITraitCollection traitCollectionWithAccessibilityContrast:UIAccessibilityContrastHigh]
158+
]];
159+
auto darkAccessibilityContrastColor =
160+
ColorFromUIColorForSpecificTraitCollection(uiColor, darkModeAccessibilityContrastTraitCollection);
161+
162+
static UITraitCollection *lightModeAccessibilityContrastTraitCollection =
163+
[UITraitCollection traitCollectionWithTraitsFromCollections:@[
164+
lightModeTraitCollection,
165+
[UITraitCollection traitCollectionWithAccessibilityContrast:UIAccessibilityContrastHigh]
166+
]];
167+
auto lightAccessibilityContrastColor =
168+
ColorFromUIColorForSpecificTraitCollection(uiColor, lightModeAccessibilityContrastTraitCollection);
169+
return facebook::react::hash_combine(
170+
darkColor,
171+
lightColor,
172+
darkAccessibilityContrastColor,
173+
lightAccessibilityContrastColor,
174+
UIColorIsP3ColorSpace(uiColor));
97175
}
176+
98177
} // anonymous namespace
99178

100179
Color::Color(int32_t color)
@@ -114,14 +193,20 @@ int32_t ColorFromUIColor(const std::shared_ptr<void> &uiColor)
114193

115194
Color::Color(std::shared_ptr<void> uiColor)
116195
{
196+
UIColor *color = ((UIColor *)unwrapManagedObject(uiColor));
197+
if (color && color.reactHash == 0) {
198+
auto colorHash = hashFromUIColor(uiColor);
199+
color.reactHash = colorHash;
200+
}
117201
uiColor_ = std::move(uiColor);
118202
}
119203

120204
bool Color::operator==(const Color &other) const
121205
{
122206
return (!uiColor_ && !other.uiColor_) ||
123207
(uiColor_ && other.uiColor_ &&
124-
[unwrapManagedObject(getUIColor()) isEqual:unwrapManagedObject(other.getUIColor())]);
208+
((UIColor *)unwrapManagedObject(getUIColor())).reactHash ==
209+
((UIColor *)unwrapManagedObject(other.getUIColor())).reactHash);
125210
}
126211

127212
bool Color::operator!=(const Color &other) const
@@ -142,6 +227,17 @@ int32_t ColorFromUIColor(const std::shared_ptr<void> &uiColor)
142227
return static_cast<float>(rgba[channelId]);
143228
}
144229

230+
int32_t Color::getUIColorHash() const
231+
{
232+
return [(UIColor *)unwrapManagedObject(uiColor_) reactHash];
233+
}
234+
235+
Color Color::createSemanticColor(std::vector<std::string> &semanticItems)
236+
{
237+
auto semanticColor = RCTPlatformColorFromSemanticItems(semanticItems);
238+
return Color(wrapManagedObject(semanticColor));
239+
}
240+
145241
} // namespace facebook::react
146242

147243
NS_ASSUME_NONNULL_END

packages/react-native/ReactCommon/react/renderer/graphics/platform/ios/react/renderer/graphics/PlatformColorParser.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ SharedColor parsePlatformColor(const ContextContainer &contextContainer, int32_t
5656
auto items = (std::unordered_map<std::string, RawValue>)value;
5757
if (items.find("semantic") != items.end() && items.at("semantic").hasType<std::vector<std::string>>()) {
5858
auto semanticItems = (std::vector<std::string>)items.at("semantic");
59-
return {wrapManagedObject(RCTPlatformColorFromSemanticItems(semanticItems))};
59+
return SharedColor(Color::createSemanticColor(semanticItems));
6060
} else if (
6161
items.find("dynamic") != items.end() &&
6262
items.at("dynamic").hasType<std::unordered_map<std::string, RawValue>>()) {

packages/react-native/ReactCommon/react/renderer/graphics/platform/ios/react/renderer/graphics/RCTPlatformColorUtils.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,15 @@
88
#pragma once
99

1010
#import <UIKit/UIKit.h>
11-
#import <react/renderer/graphics/HostPlatformColor.h>
1211
#import <vector>
1312

13+
namespace facebook {
14+
namespace react {
15+
struct ColorComponents;
16+
struct Color;
17+
} // namespace react
18+
} // namespace facebook
19+
1420
facebook::react::ColorComponents RCTPlatformColorComponentsFromSemanticItems(
1521
std::vector<std::string>& semanticItems);
1622
UIColor* RCTPlatformColorFromSemanticItems(

packages/react-native/ReactCommon/react/renderer/graphics/platform/ios/react/renderer/graphics/RCTPlatformColorUtils.mm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#import <Foundation/Foundation.h>
1111
#import <UIKit/UIKit.h>
12+
#import <react/renderer/graphics/HostPlatformColor.h>
1213
#import <react/utils/ManagedObjectWrapper.h>
1314

1415
#include <string>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
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+
#pragma once
9+
10+
#import <UIKit/UIKit.h>
11+
12+
NS_ASSUME_NONNULL_BEGIN
13+
14+
@interface UIColor (Graphics)
15+
@property (nonatomic, assign) int32_t reactHash;
16+
@end
17+
18+
NS_ASSUME_NONNULL_END
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
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+
#import <objc/runtime.h>
9+
#import "UIColor+Graphics.h"
10+
11+
@implementation UIColor (Graphics)
12+
13+
- (int32_t)reactHash
14+
{
15+
return [objc_getAssociatedObject(self, _cmd) intValue];
16+
}
17+
18+
- (void)setReactHash:(int32_t)reactHash
19+
{
20+
objc_setAssociatedObject(self, @selector(reactHash), @(reactHash), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
21+
}
22+
23+
@end

0 commit comments

Comments
 (0)