Skip to content

Commit

Permalink
feat: migrate view to fabric
Browse files Browse the repository at this point in the history
  • Loading branch information
WoLewicki committed Apr 10, 2024
1 parent a6aebe0 commit c68899e
Show file tree
Hide file tree
Showing 17 changed files with 261 additions and 42 deletions.
2 changes: 1 addition & 1 deletion FabricExample/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1537,7 +1537,7 @@ SPEC CHECKSUMS:
React-jsitracing: 39abf32718d8ea6e634f01c7604be086185b78bd
React-logger: a790d16be67220e42ba1de47fd5f3bf694961307
React-Mapbuffer: 30f85b69520bd0d33e0da391b756cc213a554352
react-native-plaid-link-sdk: 41c57993bc36296d0dc2e29a6524bd48d1a6c1c3
react-native-plaid-link-sdk: 703985c98dc91afaf638f32f0fe4f2904defc94e
react-native-safe-area-context: fc0424ee0e1ae0c321c7aeb19c215e75da465676
React-nativeconfig: c9e21ec2c4fdd9965ed627c896f8b62ce75a482a
React-NativeModulesApple: f92d16846e045bc4cec6843994e64e497f49173d
Expand Down
20 changes: 18 additions & 2 deletions android/src/main/java/com/plaid/PLKEmbeddedViewManager.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
package com.plaid

import com.facebook.react.module.annotations.ReactModule
import com.facebook.react.uimanager.SimpleViewManager
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.ViewManagerDelegate
import com.facebook.react.uimanager.annotations.ReactProp
import com.facebook.react.viewmanagers.PLKEmbeddedViewManagerDelegate
import com.facebook.react.viewmanagers.PLKEmbeddedViewManagerInterface

@ReactModule(name = PLKEmbeddedViewManager.REACT_CLASS)
class PLKEmbeddedViewManager : SimpleViewManager<PLKEmbeddedView>(),
PLKEmbeddedViewManagerInterface<PLKEmbeddedView> {
private val delegate: ViewManagerDelegate<PLKEmbeddedView>

init {
delegate = PLKEmbeddedViewManagerDelegate(this)
}

class PLKEmbeddedViewManager : SimpleViewManager<PLKEmbeddedView>() {
override fun getName(): String {
return REACT_CLASS
}
Expand All @@ -14,10 +26,14 @@ class PLKEmbeddedViewManager : SimpleViewManager<PLKEmbeddedView>() {
}

@ReactProp(name = "token")
fun setToken(view: PLKEmbeddedView, token: String?) {
override fun setToken(view: PLKEmbeddedView, token: String?) {
view.setToken(token ?: "")
}

override fun setIOSPresentationStyle(view: PLKEmbeddedView, value: String?) {
// Unsupported on Android
}

override fun getExportedCustomBubblingEventTypeConstants(): Map<String, Any> {
return mutableMapOf(
EVENT_NAME to mutableMapOf(
Expand Down
5 changes: 1 addition & 4 deletions android/src/main/java/com/plaid/PlaidModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import android.util.Log
import com.facebook.react.bridge.ActivityEventListener
import com.facebook.react.bridge.Callback
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.module.annotations.ReactModule
import com.facebook.react.modules.core.DeviceEventManagerModule
Expand All @@ -19,11 +18,9 @@ import com.plaid.link.configuration.LinkLogLevel
import com.plaid.link.configuration.LinkTokenConfiguration
import com.plaid.link.event.LinkEvent
import com.plaid.link.exception.LinkException
import com.plaid.link.result.LinkAccountSubtype
import com.plaid.link.result.LinkResultHandler
import org.json.JSONException
import org.json.JSONObject
import java.util.ArrayList

@ReactModule(name = PlaidModule.NAME)
class PlaidModule internal constructor(reactContext: ReactApplicationContext) :
Expand Down Expand Up @@ -164,7 +161,7 @@ class PlaidModule internal constructor(reactContext: ReactApplicationContext) :

override fun addListener(eventName: String?) = Unit

override fun removeListeners(count: Int) = Unit
override fun removeListeners(count: Double) = Unit

private fun maybeGetStringField(obj: JSONObject, fieldName: String): String? {
if (obj.has(fieldName) && !TextUtils.isEmpty(obj.getString(fieldName))) {
Expand Down
46 changes: 29 additions & 17 deletions android/src/main/java/com/plaid/PlaidPackage.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.plaid;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -10,6 +10,7 @@
import com.facebook.react.bridge.ModuleSpec;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.module.annotations.ReactModuleList;
import com.facebook.react.module.model.ReactModuleInfo;
Expand All @@ -18,31 +19,47 @@

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Provider;

@ReactModuleList(nativeModules = {PlaidModule.class})
public class PlaidPackage extends TurboReactPackage implements ViewManagerOnDemandReactPackage {

/**
* {@inheritDoc}
*/
private @Nullable Map<String, ModuleSpec> mViewManagers;

private Map<String, ModuleSpec> getViewManagersMap(final ReactApplicationContext reactContext) {
if (mViewManagers == null) {
Map<String, ModuleSpec> specs = MapBuilder.newHashMap();
specs.put(
PLKEmbeddedViewManager.REACT_CLASS,
ModuleSpec.viewManagerSpec(
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new PLKEmbeddedViewManager();
}
}));
mViewManagers = specs;
}
return mViewManagers;
}

/** {@inheritDoc} */
@Override
public List<String> getViewManagerNames(ReactApplicationContext reactContext) {
return null;
return new ArrayList<>(getViewManagersMap(reactContext).keySet());
}

@Override
protected List<ModuleSpec> getViewManagers(ReactApplicationContext reactContext) {
return null;
return new ArrayList<>(getViewManagersMap(reactContext).values());
}

/**
* {@inheritDoc}
*/
/** {@inheritDoc} */
@Override
public @Nullable
ViewManager createViewManager(
public @Nullable ViewManager createViewManager(
ReactApplicationContext reactContext, String viewManagerName) {
return null;
ModuleSpec spec = getViewManagersMap(reactContext).get(viewManagerName);
return spec != null ? (ViewManager) spec.getProvider().get() : null;
}

@Override
Expand All @@ -55,11 +72,6 @@ public NativeModule getModule(String name, @Nonnull ReactApplicationContext reac
}
}

@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList( new PLKEmbeddedViewManager() );
}

@Override
public ReactModuleInfoProvider getReactModuleInfoProvider() {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,5 @@ public NativePlaidLinkModuleAndroidSpec(ReactApplicationContext reactContext) {

@ReactMethod
@DoNotStrip
public abstract void removeListeners(int count);
public abstract void removeListeners(double count);
}
2 changes: 1 addition & 1 deletion ios/PLKEmbeddedView.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ @interface RCT_EXTERN_MODULE(PLKEmbeddedViewManager, RCTViewManager)
RCT_EXPORT_VIEW_PROPERTY(token, NSString)
RCT_EXPORT_VIEW_PROPERTY(iOSPresentationStyle, NSString)
RCT_EXPORT_VIEW_PROPERTY(onEmbeddedEvent, RCTDirectEventBlock)
@end
@end
8 changes: 4 additions & 4 deletions ios/PLKEmbeddedView.swift
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import LinkKit
import UIKit

internal final class PLKEmbeddedView: UIView {
@objc public final class PLKEmbeddedView: UIView {

// Properties exposed to React Native.

@objc var iOSPresentationStyle: String = "" {
@objc public var iOSPresentationStyle: String = "" {
didSet {
createNativeEmbeddedView()
}
}

@objc var token: String = "" {
@objc public var token: String = "" {
didSet {
createNativeEmbeddedView()
}
}

@objc var onEmbeddedEvent: RCTDirectEventBlock?
@objc public var onEmbeddedEvent: RCTDirectEventBlock?

// MARK: Private

Expand Down
15 changes: 15 additions & 0 deletions ios/PLKEmbeddedViewComponentView.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifdef RCT_NEW_ARCH_ENABLED

#import <UIKit/UIKit.h>

#import <React/RCTUIManager.h>
#import <React/RCTViewComponentView.h>

NS_ASSUME_NONNULL_BEGIN

@interface PLKEmbeddedViewComponentView : RCTViewComponentView
@end

NS_ASSUME_NONNULL_END

#endif // RCT_NEW_ARCH_ENABLED
85 changes: 85 additions & 0 deletions ios/PLKEmbeddedViewComponentView.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#ifdef RCT_NEW_ARCH_ENABLED

#import "PLKEmbeddedViewComponentView.h"
#import "PLKFabricHelpers.h"

#import <React/RCTBridge+Private.h>
#import <React/RCTConversions.h>
#import <React/RCTFabricComponentsPlugins.h>

#import <react/renderer/components/rnplaidlink/ComponentDescriptors.h>
#import <react/renderer/components/rnplaidlink/EventEmitters.h>
#import <react/renderer/components/rnplaidlink/Props.h>
#import <react/renderer/components/rnplaidlink/RCTComponentViewHelpers.h>

using namespace facebook::react;

@implementation PLKEmbeddedViewComponentView {
PLKEmbeddedView *_view;
}

- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
static const auto defaultProps = std::make_shared<const PLKEmbeddedViewProps>();
_props = defaultProps;
[self prepareView];
}

return self;
}

- (void)prepareView
{
_view = [[PLKEmbeddedView alloc] init];

__weak __typeof__(self) weakSelf = self;

[_view setOnEmbeddedEvent:^(NSDictionary* event) {
__typeof__(self) strongSelf = weakSelf;

if (strongSelf != nullptr && strongSelf->_eventEmitter != nullptr) {
std::dynamic_pointer_cast<const facebook::react::PLKEmbeddedViewEventEmitter>(strongSelf->_eventEmitter)->onEmbeddedEvent({
.embeddedEventName = RCTStringFromNSString(event[@"embeddedEventName"]),
.eventName = RCTStringFromNSString(event[@"eventName"]),
.error = PLKConvertIdToFollyDynamic(event[@"error"]),
.publicToken = RCTStringFromNSString(event[@"publicToken"]),
.metadata = PLKConvertIdToFollyDynamic(event[@"metadata"]),
});
}
}];
self.contentView = _view;
}

#pragma mark - RCTComponentViewProtocol

+ (ComponentDescriptorProvider)componentDescriptorProvider
{
return concreteComponentDescriptorProvider<PLKEmbeddedViewComponentDescriptor>();
}


- (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps
{
const auto &newProps = static_cast<const PLKEmbeddedViewProps &>(*props);
_view.token = RCTNSStringFromStringNilIfEmpty(newProps.token);
_view.iOSPresentationStyle = RCTNSStringFromStringNilIfEmpty(newProps.token);

[super updateProps:props oldProps:oldProps];
}

- (void)prepareForRecycle
{
[super prepareForRecycle];
[self prepareView];
}


@end

Class<RCTComponentViewProtocol> PLKEmbeddedViewCls(void)
{
return PLKEmbeddedViewComponentView.class;
}

#endif // RCT_NEW_ARCH_ENABLED
70 changes: 70 additions & 0 deletions ios/PLKFabricHelpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#import <React/RCTConversions.h>
#import <folly/dynamic.h>
#import <objc/runtime.h>

#if __has_include(<rnplaidlink/react_native_plaid_link_sdk-Swift.h>)
#import <rnplaidlink/react_native_plaid_link_sdk-Swift.h>
#else
#import <react_native_plaid_link_sdk-Swift.h>
#endif

// copied from RCTFollyConvert
folly::dynamic PLKConvertIdToFollyDynamic(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(PLKConvertIdToFollyDynamic(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(PLKConvertIdToFollyDynamic(key), PLKConvertIdToFollyDynamic(value));
}];

return object;
}

return nil;
}
2 changes: 2 additions & 0 deletions ios/react_native_plaid_link_sdk.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// xcode tries to import this header in the auto-generated swift header
// same as here: https://github.com/rnmapbox/maps/blob/b76c000a237b9757a616982d6c07f6ecfd7d60a9/ios/RNMBX/rnmapbox_maps.h
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
},
"codegenConfig": {
"name": "rnplaidlink",
"type": "modules",
"type": "all",
"jsSrcsDir": "./src/fabric",
"android": {
"javaPackageName": "com.plaid"
Expand Down
Loading

0 comments on commit c68899e

Please sign in to comment.