-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(iOS): migrate to RCT_EXPORT_METHOD.
- Loading branch information
Showing
13 changed files
with
319 additions
and
302 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// | ||
// NativeModule.h (example) | ||
// Created by Asaf Korem (Wix.com) on 2025. | ||
// | ||
|
||
#import <React/RCTBridgeModule.h> | ||
#import <React/RCTEventEmitter.h> | ||
#import <UIKit/UIKit.h> | ||
|
||
@interface NativeModule : NSObject <RCTBridgeModule> | ||
|
||
@property (nonatomic, strong) UIWindow *overlayWindow; | ||
@property (nonatomic, strong) UIView *overlayView; | ||
@property (nonatomic, assign) NSInteger callCounter; | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
// | ||
// NativeModule.m (example) | ||
// Created by Asaf Korem (Wix.com) on 2025. | ||
// | ||
|
||
#import "NativeModule.h" | ||
#import <React/RCTRootView.h> | ||
#import <React/RCTBridge.h> | ||
|
||
@implementation NativeModule | ||
|
||
RCT_EXPORT_MODULE(); | ||
|
||
- (instancetype)init { | ||
if (self = [super init]) { | ||
self.callCounter = 0; | ||
} | ||
return self; | ||
} | ||
|
||
+ (BOOL)requiresMainQueueSetup { | ||
return YES; | ||
} | ||
|
||
// MARK: - Locale Methods | ||
RCT_EXPORT_METHOD(getUserLocale:(RCTPromiseResolveBlock)resolve | ||
rejecter:(RCTPromiseRejectBlock)reject) { | ||
NSLocale *currentLocale = [NSLocale currentLocale]; | ||
NSString *localeIdentifier = [currentLocale localeIdentifier]; | ||
resolve(localeIdentifier); | ||
} | ||
|
||
RCT_EXPORT_METHOD(getUserLanguage:(RCTPromiseResolveBlock)resolve | ||
rejecter:(RCTPromiseRejectBlock)reject) { | ||
NSString *languageCode = [[NSLocale preferredLanguages] firstObject]; | ||
if (languageCode) { | ||
resolve(languageCode); | ||
} else { | ||
NSError *error = [NSError errorWithDomain:@"NativeModule" | ||
code:1 | ||
userInfo:@{NSLocalizedDescriptionKey: @"Could not determine user language"}]; | ||
reject(@"no_language", @"Could not determine user language", error); | ||
} | ||
} | ||
|
||
// MARK: - Echo Methods | ||
RCT_EXPORT_METHOD(echoWithoutResponse:(NSString *)str) { | ||
self.callCounter++; | ||
} | ||
|
||
RCT_EXPORT_METHOD(echoWithResponse:(NSString *)str | ||
resolver:(RCTPromiseResolveBlock)resolve | ||
rejecter:(RCTPromiseRejectBlock)reject) { | ||
self.callCounter++; | ||
resolve(str); | ||
} | ||
|
||
// MARK: - Timing Methods | ||
RCT_EXPORT_METHOD(nativeSetTimeout:(NSTimeInterval)delay | ||
block:(RCTResponseSenderBlock)block) { | ||
dispatch_time_t dispatchTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)); | ||
dispatch_after(dispatchTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ | ||
dispatch_async(dispatch_get_main_queue(), ^{ | ||
block(@[]); | ||
}); | ||
}); | ||
} | ||
|
||
// MARK: - Navigation Methods | ||
RCT_EXPORT_METHOD(switchToNativeRoot) { | ||
dispatch_async(dispatch_get_main_queue(), ^{ | ||
UIViewController *newRoot = [self createNativeRootViewController]; | ||
[self updateRootViewController:newRoot]; | ||
}); | ||
} | ||
|
||
RCT_EXPORT_METHOD(switchToMultipleReactRoots) { | ||
dispatch_async(dispatch_get_main_queue(), ^{ | ||
UITabBarController *tabController = [self createTabBarControllerWithBridge]; | ||
[self updateRootViewController:tabController]; | ||
}); | ||
} | ||
|
||
// MARK: - Notification Methods | ||
RCT_EXPORT_METHOD(sendNotification:(NSString *)notification | ||
name:(NSString *)name) { | ||
dispatch_async(dispatch_get_main_queue(), ^{ | ||
[[NSNotificationCenter defaultCenter] postNotificationName:notification | ||
object:nil | ||
userInfo:@{@"name": name}]; | ||
}); | ||
} | ||
|
||
// MARK: - Overlay Methods | ||
RCT_EXPORT_METHOD(presentOverlayWindow) { | ||
dispatch_async(dispatch_get_main_queue(), ^{ | ||
[self setupAndShowOverlayWindow]; | ||
}); | ||
} | ||
|
||
RCT_EXPORT_METHOD(presentOverlayView) { | ||
dispatch_async(dispatch_get_main_queue(), ^{ | ||
[self setupAndShowOverlayView]; | ||
}); | ||
} | ||
|
||
// MARK: - Private Helper Methods | ||
- (UIViewController *)createNativeRootViewController { | ||
UIViewController *newRoot = [[UIViewController alloc] init]; | ||
newRoot.view.backgroundColor = [UIColor whiteColor]; | ||
|
||
UILabel *label = [[UILabel alloc] init]; | ||
label.text = @"this is a new native root"; | ||
[label sizeToFit]; | ||
label.center = newRoot.view.center; | ||
[newRoot.view addSubview:label]; | ||
|
||
return newRoot; | ||
} | ||
|
||
- (UITabBarController *)createTabBarControllerWithBridge { | ||
RCTBridge *bridge = [self getCurrentBridge]; | ||
if (!bridge) { | ||
return nil; | ||
} | ||
|
||
NSMutableArray *viewControllers = [NSMutableArray array]; | ||
for (NSInteger i = 1; i <= 4; i++) { | ||
UIViewController *vc = [self createReactRootViewControllerWithBridge:bridge | ||
title:[NSString stringWithFormat:@"%ld", (long)i]]; | ||
[viewControllers addObject:vc]; | ||
} | ||
|
||
UITabBarController *tabController = [[UITabBarController alloc] init]; | ||
tabController.viewControllers = viewControllers; | ||
return tabController; | ||
} | ||
|
||
- (UIViewController *)createReactRootViewControllerWithBridge:(RCTBridge *)bridge title:(NSString *)title { | ||
UIViewController *viewController = [[UIViewController alloc] init]; | ||
viewController.view = [[RCTRootView alloc] initWithBridge:bridge | ||
moduleName:@"example" | ||
initialProperties:nil]; | ||
viewController.tabBarItem.title = title; | ||
return viewController; | ||
} | ||
|
||
- (RCTBridge *)getCurrentBridge { | ||
id appDelegate = [[UIApplication sharedApplication] delegate]; | ||
if ([appDelegate respondsToSelector:@selector(window)]) { | ||
UIWindow *window = [appDelegate window]; | ||
RCTRootView *rootView = (RCTRootView *)window.rootViewController.view; | ||
if ([rootView isKindOfClass:[RCTRootView class]]) { | ||
return rootView.bridge; | ||
} | ||
} | ||
return nil; | ||
} | ||
|
||
- (void)updateRootViewController:(UIViewController *)viewController { | ||
id appDelegate = [[UIApplication sharedApplication] delegate]; | ||
if ([appDelegate respondsToSelector:@selector(window)]) { | ||
UIWindow *window = [appDelegate window]; | ||
window.rootViewController = viewController; | ||
[window makeKeyAndVisible]; | ||
} | ||
} | ||
|
||
- (void)setupAndShowOverlayWindow { | ||
CGRect screenBounds = [[UIScreen mainScreen] bounds]; | ||
self.overlayWindow = [[UIWindow alloc] initWithFrame:screenBounds]; | ||
self.overlayWindow.accessibilityIdentifier = @"OverlayWindow"; | ||
self.overlayWindow.windowLevel = UIWindowLevelStatusBar; | ||
self.overlayWindow.hidden = NO; | ||
[self.overlayWindow makeKeyAndVisible]; | ||
} | ||
|
||
- (void)setupAndShowOverlayView { | ||
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow]; | ||
if (!keyWindow) return; | ||
|
||
CGRect screenBounds = [[UIScreen mainScreen] bounds]; | ||
self.overlayView = [[UIView alloc] initWithFrame:screenBounds]; | ||
self.overlayView.userInteractionEnabled = YES; | ||
self.overlayView.accessibilityIdentifier = @"OverlayView"; | ||
[keyWindow addSubview:self.overlayView]; | ||
} | ||
|
||
@end |
Oops, something went wrong.