Skip to content

Commit

Permalink
fix: use dynamics to cover string and number (#600)
Browse files Browse the repository at this point in the history
* fix: use dynamics to cover string and number

* fix: change values in JS too
  • Loading branch information
WoLewicki authored Nov 28, 2024
1 parent 9983f22 commit 5d2b382
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 10 deletions.
5 changes: 4 additions & 1 deletion ios/RNCPicker.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
*/

#import "RNCPicker.h"
#ifdef RCT_NEW_ARCH_ENABLED
#import "RNCPickerFabricConversions.h"
#endif

#import <React/RCTConvert.h>
#import <React/RCTUtils.h>
Expand Down Expand Up @@ -170,7 +173,7 @@ - (void)pickerView:(__unused UIPickerView *)pickerView
std::dynamic_pointer_cast<const facebook::react::RNCPickerEventEmitter>(eventEmitter)
->onChange(facebook::react::RNCPickerEventEmitter::OnChange{
.newIndex = (int)row,
.newValue = RCTStringFromNSString(_items[row][@"value"]),
.newValue = RNCPickerRNCPickerConvertIdToFollyDynamic(_items[row][@"value"]),
});
}
}
Expand Down
5 changes: 3 additions & 2 deletions ios/RNCPickerComponentView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#import "RNCPickerComponentView.h"
#import "RNCPicker.h"
#import "RNCPickerFabricConversions.h"

#import <React/RCTFabricComponentsPlugins.h>
#import <react/renderer/components/rnpicker/ComponentDescriptors.h>
Expand Down Expand Up @@ -54,8 +55,8 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
for (RNCPickerItemsStruct item : newProps.items)
{
NSMutableDictionary *dictItem = [NSMutableDictionary new];
dictItem[@"value"] = RCTNSStringFromString(item.value);
dictItem[@"label"] = RCTNSStringFromStringNilIfEmpty(item.label);
dictItem[@"value"] = RNCPickerConvertFollyDynamicToId(item.value);
dictItem[@"label"] = RNCPickerConvertFollyDynamicToId(item.label);
dictItem[@"textColor"] = RCTUIColorFromSharedColor(item.textColor);
dictItem[@"testID"] = RCTNSStringFromStringNilIfEmpty(item.testID);
[items addObject:dictItem];
Expand Down
106 changes: 106 additions & 0 deletions ios/RNCPickerFabricConversions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#import <folly/dynamic.h>
#import <Foundation/Foundation.h>
#import <objc/runtime.h>

// copied from RCTFollyConvert
static id RNCPickerConvertFollyDynamicToId(const folly::dynamic &dyn)
{
// I could imagine an implementation which avoids copies by wrapping the
// dynamic in a derived class of NSDictionary. We can do that if profiling
// implies it will help.

switch (dyn.type()) {
case folly::dynamic::NULLT:
return nil;
case folly::dynamic::BOOL:
return dyn.getBool() ? @YES : @NO;
case folly::dynamic::INT64:
return @(dyn.getInt());
case folly::dynamic::DOUBLE:
return @(dyn.getDouble());
case folly::dynamic::STRING:
return [[NSString alloc] initWithBytes:dyn.c_str() length:dyn.size() encoding:NSUTF8StringEncoding];
case folly::dynamic::ARRAY: {
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:dyn.size()];
for (const auto &elem : dyn) {
id value = RNCPickerConvertFollyDynamicToId(elem);
if (value) {
[array addObject:value];
}
}
return array;
}
case folly::dynamic::OBJECT: {
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:dyn.size()];
for (const auto &elem : dyn.items()) {
id key = RNCPickerConvertFollyDynamicToId(elem.first);
id value = RNCPickerConvertFollyDynamicToId(elem.second);
if (key && value) {
dict[key] = value;
}
}
return dict;
}
}
}

static folly::dynamic RNCPickerRNCPickerConvertIdToFollyDynamic(id json)
{
if (json == nil || json == (id)kCFNull) {
return nullptr;
} else if ([json isKindOfClass:[NSNumber class]]) {
const char *objCType = [json objCType];
switch (objCType[0]) {
// This is a c++ bool or C99 _Bool. On some platforms, BOOL is a bool.
case _C_BOOL:
return (bool)[json boolValue];
case _C_CHR:
// On some platforms, objc BOOL is a signed char, but it
// might also be a small number. Use the same hack JSC uses
// to distinguish them:
// 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
if ([json isKindOfClass:[@YES class]]) {
return (bool)[json boolValue];
} else {
return [json longLongValue];
}
case _C_UCHR:
case _C_SHT:
case _C_USHT:
case _C_INT:
case _C_UINT:
case _C_LNG:
case _C_ULNG:
case _C_LNG_LNG:
case _C_ULNG_LNG:
return [json longLongValue];

case _C_FLT:
case _C_DBL:
return [json doubleValue];

// default:
// fall through
}
} else if ([json isKindOfClass:[NSString class]]) {
NSData *data = [json dataUsingEncoding:NSUTF8StringEncoding];
return std::string(reinterpret_cast<const char *>(data.bytes), data.length);
} else if ([json isKindOfClass:[NSArray class]]) {
folly::dynamic array = folly::dynamic::array;
for (id element in json) {
array.push_back(RNCPickerRNCPickerConvertIdToFollyDynamic(element));
}
return array;
} else if ([json isKindOfClass:[NSDictionary class]]) {
__block folly::dynamic object = folly::dynamic::object();

[json enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, __unused BOOL *stop) {
object.insert(RNCPickerRNCPickerConvertIdToFollyDynamic(key), RNCPickerRNCPickerConvertIdToFollyDynamic(value));
}];

return object;
}

return nil;
}

8 changes: 4 additions & 4 deletions js/PickerIOS.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,12 @@ const PickerIOSWithForwardedRef: React.AbstractComponent<
if (child === null) {
return null;
}
if (String(child.props.value) === String(selectedValue)) {
if (child.props.value === selectedValue) {
selectedIndex = index;
}
return {
value: String(child.props.value),
label: String(child.props.label),
value: child.props.value,
label: child.props.label,
textColor: processColor(child.props.color),
testID: child.props.testID,
};
Expand All @@ -162,7 +162,7 @@ const PickerIOSWithForwardedRef: React.AbstractComponent<
child: $FlowFixMe,
index: number,
) {
if (String(child.props.value) === String(selectedValue)) {
if (child.props.value === selectedValue) {
jsValue = index;
}
});
Expand Down
6 changes: 3 additions & 3 deletions js/RNCPickerNativeComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNati
import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands';

type PickerIOSChangeEvent = $ReadOnly<{|
newValue: string,
newValue: UnsafeMixed,
newIndex: Int32,
|}>;

type RNCPickerIOSTypeItemType = $ReadOnly<{|
label: ?string,
value: ?string,
label: ?UnsafeMixed,
value: ?UnsafeMixed,
textColor: ?ColorValue,
testID: ?string,
|}>;
Expand Down

0 comments on commit 5d2b382

Please sign in to comment.