Skip to content

Commit 5d2b382

Browse files
authored
fix: use dynamics to cover string and number (#600)
* fix: use dynamics to cover string and number * fix: change values in JS too
1 parent 9983f22 commit 5d2b382

5 files changed

+120
-10
lines changed

ios/RNCPicker.mm

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
*/
77

88
#import "RNCPicker.h"
9+
#ifdef RCT_NEW_ARCH_ENABLED
10+
#import "RNCPickerFabricConversions.h"
11+
#endif
912

1013
#import <React/RCTConvert.h>
1114
#import <React/RCTUtils.h>
@@ -170,7 +173,7 @@ - (void)pickerView:(__unused UIPickerView *)pickerView
170173
std::dynamic_pointer_cast<const facebook::react::RNCPickerEventEmitter>(eventEmitter)
171174
->onChange(facebook::react::RNCPickerEventEmitter::OnChange{
172175
.newIndex = (int)row,
173-
.newValue = RCTStringFromNSString(_items[row][@"value"]),
176+
.newValue = RNCPickerRNCPickerConvertIdToFollyDynamic(_items[row][@"value"]),
174177
});
175178
}
176179
}

ios/RNCPickerComponentView.mm

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#import "RNCPickerComponentView.h"
44
#import "RNCPicker.h"
5+
#import "RNCPickerFabricConversions.h"
56

67
#import <React/RCTFabricComponentsPlugins.h>
78
#import <react/renderer/components/rnpicker/ComponentDescriptors.h>
@@ -54,8 +55,8 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
5455
for (RNCPickerItemsStruct item : newProps.items)
5556
{
5657
NSMutableDictionary *dictItem = [NSMutableDictionary new];
57-
dictItem[@"value"] = RCTNSStringFromString(item.value);
58-
dictItem[@"label"] = RCTNSStringFromStringNilIfEmpty(item.label);
58+
dictItem[@"value"] = RNCPickerConvertFollyDynamicToId(item.value);
59+
dictItem[@"label"] = RNCPickerConvertFollyDynamicToId(item.label);
5960
dictItem[@"textColor"] = RCTUIColorFromSharedColor(item.textColor);
6061
dictItem[@"testID"] = RCTNSStringFromStringNilIfEmpty(item.testID);
6162
[items addObject:dictItem];

ios/RNCPickerFabricConversions.h

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#import <folly/dynamic.h>
2+
#import <Foundation/Foundation.h>
3+
#import <objc/runtime.h>
4+
5+
// copied from RCTFollyConvert
6+
static id RNCPickerConvertFollyDynamicToId(const folly::dynamic &dyn)
7+
{
8+
// I could imagine an implementation which avoids copies by wrapping the
9+
// dynamic in a derived class of NSDictionary. We can do that if profiling
10+
// implies it will help.
11+
12+
switch (dyn.type()) {
13+
case folly::dynamic::NULLT:
14+
return nil;
15+
case folly::dynamic::BOOL:
16+
return dyn.getBool() ? @YES : @NO;
17+
case folly::dynamic::INT64:
18+
return @(dyn.getInt());
19+
case folly::dynamic::DOUBLE:
20+
return @(dyn.getDouble());
21+
case folly::dynamic::STRING:
22+
return [[NSString alloc] initWithBytes:dyn.c_str() length:dyn.size() encoding:NSUTF8StringEncoding];
23+
case folly::dynamic::ARRAY: {
24+
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:dyn.size()];
25+
for (const auto &elem : dyn) {
26+
id value = RNCPickerConvertFollyDynamicToId(elem);
27+
if (value) {
28+
[array addObject:value];
29+
}
30+
}
31+
return array;
32+
}
33+
case folly::dynamic::OBJECT: {
34+
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:dyn.size()];
35+
for (const auto &elem : dyn.items()) {
36+
id key = RNCPickerConvertFollyDynamicToId(elem.first);
37+
id value = RNCPickerConvertFollyDynamicToId(elem.second);
38+
if (key && value) {
39+
dict[key] = value;
40+
}
41+
}
42+
return dict;
43+
}
44+
}
45+
}
46+
47+
static folly::dynamic RNCPickerRNCPickerConvertIdToFollyDynamic(id json)
48+
{
49+
if (json == nil || json == (id)kCFNull) {
50+
return nullptr;
51+
} else if ([json isKindOfClass:[NSNumber class]]) {
52+
const char *objCType = [json objCType];
53+
switch (objCType[0]) {
54+
// This is a c++ bool or C99 _Bool. On some platforms, BOOL is a bool.
55+
case _C_BOOL:
56+
return (bool)[json boolValue];
57+
case _C_CHR:
58+
// On some platforms, objc BOOL is a signed char, but it
59+
// might also be a small number. Use the same hack JSC uses
60+
// to distinguish them:
61+
// https://phabricator.intern.facebook.com/diffusion/FBS/browse/master/fbobjc/xplat/third-party/jsc/safari-600-1-4-17/JavaScriptCore/API/JSValue.mm;b8ee03916489f8b12143cd5c0bca546da5014fc9$901
62+
if ([json isKindOfClass:[@YES class]]) {
63+
return (bool)[json boolValue];
64+
} else {
65+
return [json longLongValue];
66+
}
67+
case _C_UCHR:
68+
case _C_SHT:
69+
case _C_USHT:
70+
case _C_INT:
71+
case _C_UINT:
72+
case _C_LNG:
73+
case _C_ULNG:
74+
case _C_LNG_LNG:
75+
case _C_ULNG_LNG:
76+
return [json longLongValue];
77+
78+
case _C_FLT:
79+
case _C_DBL:
80+
return [json doubleValue];
81+
82+
// default:
83+
// fall through
84+
}
85+
} else if ([json isKindOfClass:[NSString class]]) {
86+
NSData *data = [json dataUsingEncoding:NSUTF8StringEncoding];
87+
return std::string(reinterpret_cast<const char *>(data.bytes), data.length);
88+
} else if ([json isKindOfClass:[NSArray class]]) {
89+
folly::dynamic array = folly::dynamic::array;
90+
for (id element in json) {
91+
array.push_back(RNCPickerRNCPickerConvertIdToFollyDynamic(element));
92+
}
93+
return array;
94+
} else if ([json isKindOfClass:[NSDictionary class]]) {
95+
__block folly::dynamic object = folly::dynamic::object();
96+
97+
[json enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, __unused BOOL *stop) {
98+
object.insert(RNCPickerRNCPickerConvertIdToFollyDynamic(key), RNCPickerRNCPickerConvertIdToFollyDynamic(value));
99+
}];
100+
101+
return object;
102+
}
103+
104+
return nil;
105+
}
106+

js/PickerIOS.ios.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,12 @@ const PickerIOSWithForwardedRef: React.AbstractComponent<
137137
if (child === null) {
138138
return null;
139139
}
140-
if (String(child.props.value) === String(selectedValue)) {
140+
if (child.props.value === selectedValue) {
141141
selectedIndex = index;
142142
}
143143
return {
144-
value: String(child.props.value),
145-
label: String(child.props.label),
144+
value: child.props.value,
145+
label: child.props.label,
146146
textColor: processColor(child.props.color),
147147
testID: child.props.testID,
148148
};
@@ -162,7 +162,7 @@ const PickerIOSWithForwardedRef: React.AbstractComponent<
162162
child: $FlowFixMe,
163163
index: number,
164164
) {
165-
if (String(child.props.value) === String(selectedValue)) {
165+
if (child.props.value === selectedValue) {
166166
jsValue = index;
167167
}
168168
});

js/RNCPickerNativeComponent.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNati
2323
import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands';
2424

2525
type PickerIOSChangeEvent = $ReadOnly<{|
26-
newValue: string,
26+
newValue: UnsafeMixed,
2727
newIndex: Int32,
2828
|}>;
2929

3030
type RNCPickerIOSTypeItemType = $ReadOnly<{|
31-
label: ?string,
32-
value: ?string,
31+
label: ?UnsafeMixed,
32+
value: ?UnsafeMixed,
3333
textColor: ?ColorValue,
3434
testID: ?string,
3535
|}>;

0 commit comments

Comments
 (0)