-
Notifications
You must be signed in to change notification settings - Fork 26
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
PoC: use raw jsi::Value
prop w/ fabric views
#386
base: main
Are you sure you want to change the base?
Changes from all commits
598d9d1
22baadf
7a853ab
3572f8b
6e8b73a
59e499d
a687250
9d2a8bb
db2eb59
ef112bf
d5d2956
b40551c
8e00bcc
2a397be
2378fe7
67e25a4
0e079f7
5e4dc34
fb593aa
ef7ef21
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { type HostComponent, type ViewProps } from 'react-native' | ||
import { type TestObjectSwiftKotlin } from 'react-native-nitro-image' | ||
|
||
// This has no type exports :/ | ||
import * as NativeComponentRegistry from 'react-native/Libraries/NativeComponent/NativeComponentRegistry' | ||
|
||
// TODO: types for this are not exported / only exist in flow | ||
const viewConfig = { | ||
uiViewClassName: 'CustomView', | ||
bubblingEventTypes: {}, | ||
directEventTypes: {}, | ||
validAttributes: { | ||
nativeProp: true, | ||
}, | ||
} | ||
|
||
// TODO: try passing a nitro object here and casting it on the native side! | ||
type NativeProps = ViewProps & { | ||
nativeProp: TestObjectSwiftKotlin | ||
} | ||
|
||
export const CustomView: HostComponent<NativeProps> = | ||
NativeComponentRegistry.get<NativeProps>('CustomView', () => viewConfig) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it might, we'd have to test this |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import React from 'react' | ||
import { Button, View } from 'react-native' | ||
import { CustomView } from '../components/CustomView' | ||
import { type TestObjectSwiftKotlin } from 'react-native-nitro-image' | ||
import { NitroModules } from 'react-native-nitro-modules' | ||
|
||
function makeObject() { | ||
const obj = NitroModules.createHybridObject<TestObjectSwiftKotlin>( | ||
'TestObjectSwiftKotlin' | ||
) | ||
obj.bigintValue = BigInt(Math.random() * 1000000000000000000 + '') | ||
console.log('makeObject', obj) | ||
return obj | ||
} | ||
|
||
export function CustomViewScreen() { | ||
const [state, setState] = React.useState(makeObject) | ||
|
||
return ( | ||
<View | ||
style={{ | ||
flex: 1, | ||
justifyContent: 'center', | ||
alignItems: 'center', | ||
backgroundColor: 'lightblue', | ||
}} | ||
> | ||
<CustomView | ||
nativeProp={state} | ||
style={{ | ||
width: 200, | ||
height: 200, | ||
backgroundColor: 'red', | ||
}} | ||
/> | ||
<Button | ||
title="Increment" | ||
onPress={() => { | ||
setState(makeObject()) | ||
}} | ||
/> | ||
</View> | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package com.margelo.nitro.image; | ||
|
||
import android.util.Log; | ||
import android.view.View; | ||
|
||
import androidx.annotation.NonNull; | ||
import androidx.annotation.Nullable; | ||
|
||
import com.facebook.react.fabric.StateWrapperImpl; | ||
import com.facebook.react.uimanager.ReactStylesDiffMap; | ||
import com.facebook.react.uimanager.SimpleViewManager; | ||
import com.facebook.react.uimanager.StateWrapper; | ||
import com.facebook.react.uimanager.ThemedReactContext; | ||
import com.facebook.react.bridge.ReadableNativeMap; | ||
|
||
public class NitroExampleViewManager extends SimpleViewManager<View> { | ||
@NonNull | ||
@Override | ||
public String getName() { | ||
return "CustomView"; | ||
} | ||
|
||
@NonNull | ||
@Override | ||
protected View createViewInstance(@NonNull ThemedReactContext reactContext) { | ||
return new View(reactContext); | ||
} | ||
|
||
@Nullable | ||
@Override | ||
public Object updateState(@NonNull View view, ReactStylesDiffMap props, StateWrapper stateWrapper) { | ||
StateWrapperImpl stateWrapperImpl = (StateWrapperImpl) stateWrapper; | ||
|
||
// TODO: i am sure there is a way to make this casting a bit better | ||
// HybridTestObjectKotlin nativeProp = ValueFromStateWrapper.getNativeProp(stateWrapper, "nativeProp", HybridTestObjectKotlin.class); | ||
HybridTestObjectSwiftKotlinSpec nativeProp = ValueFromStateWrapper.valueFromStateWrapper(stateWrapperImpl); | ||
long value = nativeProp.getBigintValue(); | ||
Log.d("NitroExampleViewManager", "Value from state: " + value); | ||
|
||
// TODO: @Marc - here we could call a method that the user has to implement for the prop update | ||
|
||
return super.updateState(view, props, stateWrapper); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.margelo.nitro.image; | ||
|
||
import com.facebook.jni.annotations.DoNotStrip; | ||
import com.facebook.react.fabric.StateWrapperImpl; | ||
|
||
@DoNotStrip | ||
public class ValueFromStateWrapper { | ||
@DoNotStrip | ||
public static native HybridTestObjectSwiftKotlinSpec valueFromStateWrapper(StateWrapperImpl stateWrapper); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
#import <React/RCTComponentViewFactory.h> | ||
#import <React/UIView+ComponentViewProtocol.h> | ||
#import <UIKit/UIKit.h> | ||
#import <react/renderer/componentregistry/ComponentDescriptorRegistry.h> | ||
|
||
#import <React/RCTViewComponentView.h> | ||
#import <jsi/jsi.h> | ||
#import <react/renderer/components/view/ConcreteViewShadowNode.h> | ||
#import <react/renderer/components/view/ViewEventEmitter.h> | ||
#import <react/renderer/components/view/ViewProps.h> | ||
#import <react/renderer/core/ConcreteComponentDescriptor.h> | ||
#import <react/renderer/core/PropsParserContext.h> | ||
|
||
#import "HybridTestObjectCppSpec.hpp" | ||
//#import "JSIConverter.hpp" | ||
|
||
// for prop conversion | ||
#import <react/renderer/core/propsConversions.h> | ||
|
||
|
||
using namespace facebook; | ||
using namespace facebook::react; | ||
using namespace margelo::nitro; | ||
|
||
// We have to inherit from ViewProps so that we can | ||
class CustomViewProps final : public ViewProps { | ||
public: | ||
CustomViewProps() = default; | ||
CustomViewProps(const PropsParserContext& context, const CustomViewProps& sourceProps, const RawProps& rawProps) | ||
: ViewProps(context, sourceProps, rawProps) { | ||
if (rawProps.isEmpty()) { | ||
// There is some code path in RawPropsParser.cpp where we expect to collect all possible keys (prop names) upfront | ||
// during the init phase. Calling this .at() method while the rawProps is empty, will cause this prop value to be indexed … | ||
// Otherwise we'd get a crash when trying to access it later. | ||
// We should really also try to merge the PR i have for providing alternative RawProps parser !! | ||
rawProps.at("nativeProp", nullptr, nullptr); | ||
Comment on lines
+32
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if I fully understand - is this still needed? Do I need to do this for every prop? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think just calling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. but yeah, it has to be called at this point once, as the |
||
return; | ||
} | ||
|
||
const RawValue* rawValue = rawProps.at("nativeProp", nullptr, nullptr); | ||
// Option 1: Manually cast it | ||
const auto& [runtime, value] = (std::pair<jsi::Runtime*, const jsi::Value&>)*rawValue; | ||
std::shared_ptr<HybridTestObjectCppSpec> prop = margelo::nitro::JSIConverter<std::shared_ptr<HybridTestObjectCppSpec>>::fromJSI(*runtime, value); | ||
auto test = prop; | ||
} | ||
|
||
std::shared_ptr<HybridTestObjectCppSpec> nativeProp; | ||
}; | ||
|
||
class CustomViewState { | ||
public: | ||
CustomViewState() = default; | ||
}; | ||
|
||
extern const char CustomViewComponentName[] = "CustomView"; | ||
using ComponentShadowNode = ConcreteViewShadowNode<CustomViewComponentName, CustomViewProps, | ||
ViewEventEmitter, // default one | ||
CustomViewState>; | ||
|
||
class CustomViewComponentDescriptor : public ConcreteComponentDescriptor<ComponentShadowNode> { | ||
public: | ||
CustomViewComponentDescriptor(const ComponentDescriptorParameters& parameters) | ||
// Construct a new RawPropsParser to which we pass true which enables JSI prop parsing | ||
: ConcreteComponentDescriptor(parameters, std::make_unique<RawPropsParser>(true)) {} | ||
}; | ||
|
||
|
||
@interface ManualFabricComponentView : RCTViewComponentView // UIView <RCTComponentViewProtocol> | ||
|
||
@end | ||
|
||
@implementation ManualFabricComponentView | ||
|
||
// Adds the component to the known components | ||
+ (void)load { | ||
[RCTComponentViewFactory.currentComponentViewFactory registerComponentViewClass:[ManualFabricComponentView class]]; | ||
} | ||
|
||
+ (ComponentDescriptorProvider)componentDescriptorProvider { | ||
return concreteComponentDescriptorProvider<CustomViewComponentDescriptor>(); | ||
} | ||
|
||
- (void)updateProps:(const facebook::react::Props::Shared&)props oldProps:(const facebook::react::Props::Shared&)oldProps { | ||
const auto& newViewProps = *std::static_pointer_cast<CustomViewProps const>(props); | ||
// NSLog(@"NativeProp value: %s", newViewProps.nativeProp.c_str()); | ||
|
||
[super updateProps:props oldProps:oldProps]; | ||
} | ||
|
||
@end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,6 +39,9 @@ target_sources( | |
../nitrogen/generated/android/c++/JHybridTestObjectSwiftKotlinSpec.cpp | ||
../nitrogen/generated/android/c++/JHybridBaseSpec.cpp | ||
../nitrogen/generated/android/c++/JHybridChildSpec.cpp | ||
# Utility | ||
../nitrogen/generated/android/c++/JValueFromStateWrapper.cpp | ||
../nitrogen/generated/android/c++/CustomComponentDescriptor.cpp | ||
) | ||
|
||
# Add all libraries required by the generated specs | ||
|
@@ -66,3 +69,20 @@ else() | |
ReactAndroid::react_nativemodule_core # <-- RN: TurboModules Core | ||
) | ||
endif() | ||
|
||
# From node_modules/react-native/ReactAndroid/cmake-utils/folly-flags.cmake | ||
# Used in node_modules/react-native/ReactAndroid/cmake-utils/ReactNative-application.cmake | ||
target_compile_definitions( | ||
NitroImage | ||
PRIVATE | ||
-DFOLLY_NO_CONFIG=1 | ||
-DFOLLY_HAVE_CLOCK_GETTIME=1 | ||
-DFOLLY_USE_LIBCPP=1 | ||
-DFOLLY_CFG_NO_COROUTINES=1 | ||
-DFOLLY_MOBILE=1 | ||
-DFOLLY_HAVE_RECVMMSG=1 | ||
-DFOLLY_HAVE_PTHREAD=1 | ||
# Once we target android-23 above, we can comment | ||
# the following line. NDK uses GNU style stderror_r() after API 23. | ||
-DFOLLY_HAVE_XSI_STRERROR_R=1 | ||
) | ||
Comment on lines
+73
to
+88
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do I really need those..? @hannojg |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
btw: without this change i can't run the example app. I am pretty sure that deleting the .xcode.env.local is wrong.
Will remove this change in a moment though