From c6c7c6ed0e5dc9b14770bc171dfa837e35c82e31 Mon Sep 17 00:00:00 2001 From: Tanishq Gupta Date: Mon, 26 Aug 2024 17:33:56 +0530 Subject: [PATCH] feat/ios-fragmentview-support --- hyper-sdk-react.podspec | 2 +- ios/HyperSdkReact.h | 4 ++ ios/HyperSdkReact.mm | 106 ++++++++++++++++++++++++++++++-------- src/HyperFragmentView.tsx | 36 +++++++++---- 4 files changed, 114 insertions(+), 34 deletions(-) diff --git a/hyper-sdk-react.podspec b/hyper-sdk-react.podspec index b2ddea1..59bb55f 100644 --- a/hyper-sdk-react.podspec +++ b/hyper-sdk-react.podspec @@ -3,7 +3,7 @@ require "json" package = JSON.parse(File.read(File.join(__dir__, "package.json"))) folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -hyper_sdk_version = "2.1.39" +hyper_sdk_version = "2.2.1.4" begin package_json_path = File.expand_path(File.join(__dir__, "../../package.json")) diff --git a/ios/HyperSdkReact.h b/ios/HyperSdkReact.h index f04c4b9..0354470 100644 --- a/ios/HyperSdkReact.h +++ b/ios/HyperSdkReact.h @@ -11,6 +11,7 @@ #import #import #import +#import @interface HyperSdkReact : RCTEventEmitter @@ -34,3 +35,6 @@ @property (nonatomic, strong) NSLayoutConstraint *trailing; @end + +@interface HyperFragmentViewManagerIOS : RCTViewManager +@end diff --git a/ios/HyperSdkReact.mm b/ios/HyperSdkReact.mm index e38953b..1c60657 100644 --- a/ios/HyperSdkReact.mm +++ b/ios/HyperSdkReact.mm @@ -19,6 +19,8 @@ #import +__weak static HyperServices *_hyperServicesReference; + // Overriding the RCTRootView to add contraints to align with the views superview @implementation SDKRootView @@ -31,7 +33,7 @@ -(void)didMoveToSuperview { if (self.trailing.isActive) { self.trailing.active = @NO; } - + //Checking superview just to be sure that it is not nil if(self.superview) { // Create contraints to replicate wrapcontent @@ -70,12 +72,12 @@ - (void) setHeight: (NSNumber*)height forTag: (NSString * _Nonnull)tag { // Update the latest value of the height holder for the given tag // This will be used to set the height of view if view is created at a later point [self.heightHolder setObject: height forKey:tag]; - + // Fetch previous height constraint so that it can be set to inactive NSLayoutConstraint *heightConstraint = [self.heightConstraintHolder objectForKey:tag]; // Fetch rootview to update set constraints if view is already created UIView *rootView = [self.rootHolder objectForKey:tag]; - + // Check if view is already present if (rootView && [rootView isKindOfClass: [UIView class]]) { // If present set earlier constraint to inactive @@ -96,7 +98,7 @@ - (void) setHeight: (NSNumber*)height forTag: (NSString * _Nonnull)tag { Use bridge to share the same JS VM */ - (UIView * _Nullable)merchantViewForViewType:(NSString * _Nonnull)viewType { - + // Create a SDKRootView so that we can attach width constraints once it is attached to it's parent RCTRootView *rrv = [SDKRootView alloc]; NSString *moduleName = @"JP_003"; @@ -109,23 +111,23 @@ - (UIView * _Nullable)merchantViewForViewType:(NSString * _Nonnull)viewType { } else if ([viewType isEqual:@"FOOTER_ATTACHED"] && [registeredComponents containsObject:@"JuspayFooterAttached"]) { moduleName = @"JuspayFooterAttached"; } - + // Save a reference of the react root view // This will be used to update height constraint if a newer value is sent by the merchant [self.rootHolder setObject:rrv forKey:moduleName]; - - + + rrv = [rrv initWithBridge: self.bridge - moduleName:moduleName - initialProperties:nil - ]; - + moduleName:moduleName + initialProperties:nil + ]; + // Remove background colour. Default colour white is getting applied to the merchant view rrv.backgroundColor = UIColor.clearColor ; - + // Remove height 0, width 0 constraints added by default. rrv.translatesAutoresizingMaskIntoConstraints = false; - + // If height is available set the height NSNumber *height = [self.heightHolder objectForKey:moduleName]; if (height && [height isKindOfClass:[NSNumber class]]) { @@ -166,12 +168,12 @@ + (BOOL)requiresMainQueueSetup{ - (NSDictionary *)constantsToExport { - return @{ HYPER_EVENT: HYPER_EVENT - , JUSPAY_HEADER : JUSPAY_HEADER - , JUSPAY_HEADER_ATTACHED : JUSPAY_HEADER_ATTACHED - , JUSPAY_FOOTER : JUSPAY_FOOTER - , JUSPAY_FOOTER_ATTACHED : JUSPAY_FOOTER_ATTACHED - }; + return @{ HYPER_EVENT: HYPER_EVENT + , JUSPAY_HEADER : JUSPAY_HEADER + , JUSPAY_HEADER_ATTACHED : JUSPAY_HEADER_ATTACHED + , JUSPAY_FOOTER : JUSPAY_FOOTER + , JUSPAY_FOOTER_ATTACHED : JUSPAY_FOOTER_ATTACHED + }; } // Will be called when this module's first listener is added. @@ -191,7 +193,7 @@ -(void)stopObserving { if (jsonData && [jsonData isKindOfClass:[NSDictionary class]] && jsonData.allKeys.count>0) { [HyperServices preFetch:jsonData]; } else { - + } } @catch (NSException *exception) { //Parsing failure. @@ -202,6 +204,7 @@ -(void)stopObserving { RCT_EXPORT_METHOD(createHyperServices) { if (self.hyperInstance == NULL) { self.hyperInstance = [HyperServices new]; + _hyperServicesReference = self.hyperInstance; } } @@ -210,7 +213,7 @@ -(void)stopObserving { @try { NSDictionary *jsonData = [HyperSdkReact stringToDictionary:data]; if (jsonData && [jsonData isKindOfClass:[NSDictionary class]] && jsonData.allKeys.count>0) { - + UIViewController *baseViewController = RCTPresentedViewController(); __weak HyperSdkReact *weakSelf = self; self.delegate = [[SdkDelegate alloc] initWithBridge:self.bridge]; @@ -240,7 +243,7 @@ -(void)stopObserving { if (self.hyperInstance.baseViewController == nil || self.hyperInstance.baseViewController.view.window == nil) { // Getting topViewController id baseViewController = RCTPresentedViewController(); - + // Set the presenting ViewController as baseViewController if the topViewController is RCTModalHostViewController. if ([baseViewController isMemberOfClass:RCTModalHostViewController.class] && [baseViewController presentingViewController]) { [self.hyperInstance setBaseViewController:[baseViewController presentingViewController]]; @@ -318,3 +321,62 @@ + (NSString*)dictionaryToString:(id)dict{ } @end + +@implementation HyperFragmentViewManagerIOS +RCT_EXPORT_MODULE() + +- (dispatch_queue_t)methodQueue{ + return dispatch_get_main_queue(); +} + ++ (BOOL)requiresMainQueueSetup{ + return YES; +} + +- (UIView *)view +{ + return [[UIView alloc] init]; +} + +RCT_EXPORT_METHOD(process:(nonnull NSNumber *)viewTag nameSpace:(NSString *)nameSpace payload:(NSString *)payload) +{ + HyperServices *hyperServicesInstance = _hyperServicesReference; + if (payload && payload.length>0) { + @try { + NSDictionary *jsonData = [HyperSdkReact stringToDictionary:payload]; + if (jsonData && [jsonData isKindOfClass:[NSDictionary class]] && jsonData.allKeys.count>0) { + [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { + if (hyperServicesInstance.baseViewController == nil || hyperServicesInstance.baseViewController.view.window == nil) { + id baseViewController = RCTPresentedViewController(); + if ([baseViewController isMemberOfClass:RCTModalHostViewController.class] && [baseViewController presentingViewController]) { + [hyperServicesInstance setBaseViewController:[baseViewController presentingViewController]]; + } else { + [hyperServicesInstance setBaseViewController:baseViewController]; + } + } + UIView *view = viewRegistry[viewTag]; + [self manuallyLayoutChildren:view]; + if (!view || ![view isKindOfClass:[UIView class]]) { + RCTLogError(@"Cannot find NativeViewManager with tag #%@", viewTag); + return; + } + NSMutableDictionary *nestedPayload = [jsonData[@"payload"] mutableCopy]; + NSDictionary *fragmentViewGroup = @{nameSpace: view}; + nestedPayload[@"fragmentViewGroups"] = fragmentViewGroup; + NSMutableDictionary *updatedJsonData = [jsonData mutableCopy]; + updatedJsonData[@"payload"] = nestedPayload; + [hyperServicesInstance process:[updatedJsonData copy]]; + }]; + } else {} + } @catch (NSException *exception) {} + } else {} +} + +- (void)manuallyLayoutChildren:(UIView *)view { + UIView *parent = view.superview; + if (!parent) return; + + view.frame = parent.bounds; +} + +@end diff --git a/src/HyperFragmentView.tsx b/src/HyperFragmentView.tsx index 8b07b10..7861b57 100644 --- a/src/HyperFragmentView.tsx +++ b/src/HyperFragmentView.tsx @@ -24,15 +24,31 @@ var HyperFragmentViewManager: any; if (Platform.OS === 'android') { HyperFragmentViewManager = requireNativeComponent('HyperFragmentViewManager'); +} else { + HyperFragmentViewManager = requireNativeComponent( + 'HyperFragmentViewManagerIOS' + ); } const createFragment = (viewId: number, namespace: string, payload: string) => { - UIManager.dispatchViewManagerCommand( - viewId, - //@ts-ignore - UIManager.HyperFragmentViewManager.Commands.process.toString(), - [viewId, namespace, payload] - ); + if (Platform.OS === 'android') { + UIManager.dispatchViewManagerCommand( + viewId, + //@ts-ignore + UIManager.HyperFragmentViewManager.Commands.process.toString(), + [viewId, namespace, payload] + ); + } else { + const commandId = UIManager.getViewManagerConfig( + 'HyperFragmentViewManagerIOS' + ).Commands.process; + if (typeof commandId !== 'undefined') { + UIManager.dispatchViewManagerCommand(viewId, commandId, [ + namespace, + payload, + ]); + } + } }; const HyperFragmentView: React.FC = ({ @@ -43,11 +59,9 @@ const HyperFragmentView: React.FC = ({ }) => { const ref = React.useRef(null); React.useEffect(() => { - if (Platform.OS === 'android') { - const viewId = findNodeHandle(ref.current); - if (viewId) { - createFragment(viewId, namespace, payload); - } + const viewId = findNodeHandle(ref.current); + if (viewId) { + createFragment(viewId, namespace, payload); } }, [namespace, payload]);