Skip to content
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

[dev-launcher][dev-menu][ios] Add support for Fabric #22184

Merged
merged 26 commits into from
Jun 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e1dc6be
[dev-launcher][ios] Add support for Fabric
gabrieldonadel Apr 18, 2023
9574d4c
Partially fix app launch
gabrieldonadel May 19, 2023
951446d
Simplify EXDevLauncherBridgeDelegate
gabrieldonadel May 19, 2023
e8533bd
Simplify podspec
gabrieldonadel May 22, 2023
3902f05
Fix lauching DevMenu
gabrieldonadel May 22, 2023
1dee398
Prevent DevLauncherController from removing fabric root view
gabrieldonadel May 26, 2023
1f4559c
Fix imports
gabrieldonadel May 31, 2023
c55e066
Enable DevMenu to run on Fabric
gabrieldonadel Jun 9, 2023
fcb57e4
Ensure we use DevMenuRCTCxxBridge
gabrieldonadel Jun 9, 2023
34ba5b3
Fix jsExecutorFactoryForBridge
gabrieldonadel Jun 9, 2023
a2d9519
Update RootView class name
gabrieldonadel Jun 9, 2023
8eaf425
Add changelog entry
gabrieldonadel Jun 13, 2023
058bbcb
Update packages/expo-dev-launcher/expo-dev-launcher.podspec
gabrieldonadel Jun 14, 2023
2fa425a
Disable swiftlint implicitly_unwrapped_optional warning on DevMenuApp…
gabrieldonadel Jun 14, 2023
19ca50c
[dev-launcher][dev-menu][ios] Add support for Fabric - Part 2 (#22821)
gabrieldonadel Jun 15, 2023
fc47c7d
Fix SwiftLint
gabrieldonadel Jun 15, 2023
609d4ea
Address PR comments
gabrieldonadel Jun 15, 2023
51dba0c
Rename createBridgeWithAdapter to createBridgeAndSetAdapterWithLaunch…
gabrieldonadel Jun 15, 2023
8b05716
Add DevClientAppDelegate and update code to latest 0.72
gabrieldonadel Jun 16, 2023
b503917
Revert unnecessary xcode project changes
gabrieldonadel Jun 16, 2023
d618b14
Add EXRCTAppDelegateInterceptor
gabrieldonadel Jun 19, 2023
6df071f
Update RCTAppDelegate imports
gabrieldonadel Jun 19, 2023
44258ef
Move RCTAppDelegate interface to DevClientAppDelegate.mm
gabrieldonadel Jun 19, 2023
50b24a2
Update RCTAppDelegate imports
gabrieldonadel Jun 19, 2023
b660581
Fix imports when fabric is disabled
gabrieldonadel Jun 19, 2023
1c82d92
Fix JSCExecutorFactory header import
gabrieldonadel Jun 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
917 changes: 460 additions & 457 deletions apps/fabric-tester/ios/Podfile.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/expo-dev-launcher/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@

### 🎉 New features

- [Android] Added support for the new architecture. ([#22607](https://github.com/expo/expo/pull/22607) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Allow users to manually load apps without specifying a URL scheme. ([#22637](https://github.com/expo/expo/pull/22637) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Added support for React Native 0.72. ([#22588](https://github.com/expo/expo/pull/22588) by [@kudo](https://github.com/kudo))
- Run tsc and lint and tests on bundle. ([#22866](https://github.com/expo/expo/pull/22866) by [@wschurman](https://github.com/wschurman))
- Added support for the new architecture. ([#22607](https://github.com/expo/expo/pull/22607), [#22184](https://github.com/expo/expo/pull/22184) by [@gabrieldonadel](https://github.com/gabrieldonadel))

### 🐛 Bug fixes

Expand Down
22 changes: 16 additions & 6 deletions packages/expo-dev-launcher/expo-dev-launcher.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ Pod::Spec.new do |s|
s.swift_version = '5.2'
s.source = { :git => 'https://github.com/github_account/expo-development-client.git', :tag => "#{s.version}" }
s.static_framework = true
s.source_files = 'ios/**/*.{h,m,swift,cpp}'
s.preserve_paths = 'ios/**/*.{h,m,swift}'
s.source_files = 'ios/**/*.{h,m,mm,swift,cpp}'
s.preserve_paths = 'ios/**/*.{h,m,mm,swift}'
s.exclude_files = 'ios/Unsafe/**/*.{h,m,mm,swift,cpp}', 'ios/Tests/**/*.{h,m,swift}'
s.requires_arc = true
s.header_dir = 'EXDevLauncher'
Expand All @@ -28,9 +28,7 @@ Pod::Spec.new do |s|
]
}

s.xcconfig = {
'GCC_PREPROCESSOR_DEFINITIONS' => "EX_DEV_LAUNCHER_VERSION=#{s.version}"
}
new_arch_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1'

other_c_flags = '$(inherited)'
dev_launcher_url = ENV['EX_DEV_LAUNCHER_URL'] || ""
Expand All @@ -43,18 +41,30 @@ Pod::Spec.new do |s|
other_swift_flags += ' -DEX_DEV_CLIENT_NETWORK_INSPECTOR'
end

if new_arch_enabled
other_c_flags += ' -DRN_FABRIC_ENABLED -DRCT_NEW_ARCH_ENABLED'
end

s.xcconfig = {
'GCC_PREPROCESSOR_DEFINITIONS' => "EX_DEV_LAUNCHER_VERSION=#{s.version}",
'OTHER_CFLAGS' => other_c_flags,
}

# Swift/Objective-C compatibility
s.pod_target_xcconfig = {
'DEFINES_MODULE' => 'YES',
'OTHER_CFLAGS[config=Debug]' => other_c_flags,
'OTHER_SWIFT_FLAGS[config=Debug]' => other_swift_flags,
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/Headers/Private/React-Core\"",
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17",
}

s.user_target_xcconfig = {
"HEADER_SEARCH_PATHS" => "\"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-launcher/Swift Compatibility Header\"",
}

s.dependency "React-Core"
s.dependency 'React-RCTAppDelegate'
s.dependency "expo-dev-menu-interface"
s.dependency "EXManifests"
s.dependency "EXUpdatesInterface"
Expand All @@ -78,7 +88,7 @@ Pod::Spec.new do |s|
end

s.test_spec 'Tests' do |test_spec|
test_spec.source_files = 'ios/Tests/**/*.{h,m,swift}'
test_spec.source_files = 'ios/Tests/**/*.{h,m,mm,swift}'
test_spec.dependency 'Quick'
test_spec.dependency 'Nimble'
test_spec.dependency "React-CoreModules"
Expand Down
10 changes: 10 additions & 0 deletions packages/expo-dev-launcher/ios/EXDevLauncherBridgeDelegate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#import <EXDevMenu/DevClientAppDelegate.h>
#import <React/RCTRootView.h>

@interface EXDevLauncherBridgeDelegate : DevClientAppDelegate

- (RCTRootView *)createRootViewWithModuleName:(NSString *)moduleName
launchOptions:(NSDictionary *_Nullable)launchOptions
application:(UIApplication *)application;

@end
43 changes: 43 additions & 0 deletions packages/expo-dev-launcher/ios/EXDevLauncherBridgeDelegate.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#import "EXDevLauncherBridgeDelegate.h"
#import "EXDevLauncherController.h"

#import <React/RCTBundleURLProvider.h>
#if __has_include(<React_RCTAppDelegate/RCTAppSetupUtils.h>)
// for importing the header from framework, the dash will be transformed to underscore
#import <React_RCTAppDelegate/RCTAppSetupUtils.h>
#else
#import <React-RCTAppDelegate/RCTAppSetupUtils.h>
#endif

#ifdef RCT_NEW_ARCH_ENABLED

static NSString *const kRNConcurrentRoot = @"concurrentRoot";

#endif

@implementation EXDevLauncherBridgeDelegate

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
return [[EXDevLauncherController sharedInstance] sourceURLForBridge:bridge];
}

- (RCTRootView *)createRootViewWithModuleName:(NSString *)moduleName launchOptions:(NSDictionary * _Nullable)launchOptions application:(UIApplication *)application{
BOOL enableTM = NO;
#if RCT_NEW_ARCH_ENABLED
enableTM = YES;
#endif

RCTAppSetupPrepareApp(application, enableTM);

self.bridge = [super createBridgeAndSetAdapterWithLaunchOptions:launchOptions];

NSMutableDictionary *initProps = [NSMutableDictionary new];
#ifdef RCT_NEW_ARCH_ENABLED
initProps[kRNConcurrentRoot] = @YES;
#endif


return [super createRootViewWithBridge:self.bridge moduleName:moduleName initProps:initProps];
}

@end
24 changes: 17 additions & 7 deletions packages/expo-dev-launcher/ios/EXDevLauncherController.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,19 @@
#import "EXDevLauncherUpdatesHelper.h"
#import "RCTPackagerConnection+EXDevLauncherPackagerConnectionInterceptor.h"

#import "EXDevLauncherBridgeDelegate.h"

#if __has_include(<EXDevLauncher/EXDevLauncher-Swift.h>)
// For cocoapods framework, the generated swift header will be inside EXDevLauncher module
#import <EXDevLauncher/EXDevLauncher-Swift.h>
#else
#import <EXDevLauncher-Swift.h>
#endif

#ifdef RCT_NEW_ARCH_ENABLED
#import <React/RCTSurfaceView.h>
#endif

@import EXManifests;
@import EXDevMenu;

Expand Down Expand Up @@ -50,6 +56,7 @@ @interface EXDevLauncherController ()
@property (nonatomic, strong) EXDevLauncherInstallationIDHelper *installationIDHelper;
@property (nonatomic, strong) EXDevLauncherNetworkInterceptor *networkInterceptor;
@property (nonatomic, assign) BOOL isStarted;
@property (nonatomic, strong) EXDevLauncherBridgeDelegate *bridgeDelegate;

@end

Expand All @@ -76,6 +83,7 @@ - (instancetype)init {
self.installationIDHelper = [EXDevLauncherInstallationIDHelper new];
self.networkInterceptor = [EXDevLauncherNetworkInterceptor new];
self.shouldPreferUpdatesInterfaceSourceUrl = NO;
self.bridgeDelegate = [EXDevLauncherBridgeDelegate new];
}
return self;
}
Expand Down Expand Up @@ -280,12 +288,8 @@ - (void)navigateToLauncher
}

[self _removeInitModuleObserver];

_launcherBridge = [[EXDevLauncherRCTBridge alloc] initWithDelegate:self launchOptions:_launchOptions];

RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:_launcherBridge
moduleName:@"main"
initialProperties:@{}];
UIView *rootView = [_bridgeDelegate createRootViewWithModuleName:@"main" launchOptions:_launchOptions application:UIApplication.sharedApplication];
_launcherBridge = _bridgeDelegate.bridge;

[self _ensureUserInterfaceStyleIsInSyncWithTraitEnv:rootView];

Expand Down Expand Up @@ -578,12 +582,18 @@ - (void)onAppContentDidAppear
[[NSNotificationCenter defaultCenter] removeObserver:self name:RCTContentDidAppearNotification object:nil];

dispatch_async(dispatch_get_main_queue(), ^{
#ifdef RCT_NEW_ARCH_ENABLED
#define EXPECTED_ROOT_VIEW RCTSurfaceView
#else
#define EXPECTED_ROOT_VIEW RCTRootContentView
#endif
NSArray<UIView *> *views = [[[self->_window rootViewController] view] subviews];
for (UIView *view in views) {
if (![view isKindOfClass:[RCTRootContentView class]]) {
if (![view isKindOfClass:[EXPECTED_ROOT_VIEW class]]) {
[view removeFromSuperview];
}
}
#undef EXPECTED_ROOT_VIEW
});
}

Expand Down
15 changes: 15 additions & 0 deletions packages/expo-dev-launcher/ios/EXRCTAppDelegateInterceptor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2015-present 650 Industries. All rights reserved.
#import <EXDevMenu/DevClientAppDelegate.h>

NS_ASSUME_NONNULL_BEGIN

@interface EXRCTAppDelegateInterceptor : DevClientAppDelegate

@property(nonatomic, weak) id<RCTBridgeDelegate> bridgeDelegate;
@property(nonatomic, weak) id<RCTBridgeDelegate> interceptor;

- (instancetype)initWithBridgeDelegate:(id<RCTBridgeDelegate>)bridgeDelegate interceptor:(id<RCTBridgeDelegate>)interceptor;

@end

NS_ASSUME_NONNULL_END
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the difference of this and the packages/expo-modules-core/ios/ReactDelegates/EXRCTBridgeDelegateInterceptor.h? could we just keep one?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only difference is that this inherits from DevClientAppDelegate, that way we can call createBridgeAndSetAdapterWithLaunchOptions inside ExpoDevLauncherReactDelegateHandler

49 changes: 49 additions & 0 deletions packages/expo-dev-launcher/ios/EXRCTAppDelegateInterceptor.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright 2018-present 650 Industries. All rights reserved.

#import "EXRCTAppDelegateInterceptor.h"

@implementation EXRCTAppDelegateInterceptor

- (instancetype)initWithBridgeDelegate:(id<RCTBridgeDelegate>)bridgeDelegate interceptor:(id<RCTBridgeDelegate>)interceptor
{
if (self = [super init]) {
self.bridgeDelegate = bridgeDelegate;
self.interceptor = interceptor;
}
return self;
}

- (BOOL)conformsToProtocol:(Protocol *)protocol
{
return [self.bridgeDelegate conformsToProtocol:protocol];
}

- (id)forwardingTargetForSelector:(SEL)selector
{
if ([self isInterceptedSelector:selector]) {
return self;
}
return self.bridgeDelegate;
}

- (BOOL)respondsToSelector:(SEL)selector
{
if ([self isInterceptedSelector:selector]) {
return YES;
}
return [self.bridgeDelegate respondsToSelector:selector];
}

- (BOOL)isInterceptedSelector:(SEL)selector
{
if ([self.interceptor respondsToSelector:selector]) {
return YES;
}
return NO;
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
return [self.interceptor sourceURLForBridge:bridge];
}

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#import <EXDevMenu/DevClientAppDelegate.h>

@interface ExpoDevLauncherBridgeDelegateHandler : DevClientAppDelegate

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#import "ExpoDevLauncherBridgeDelegateHandler.h"
#import "EXDevLauncherController.h"


@implementation ExpoDevLauncherBridgeDelegateHandler

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
return [[EXDevLauncherController sharedInstance] sourceUrl];
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class ExpoDevLauncherReactDelegateHandler: ExpoReactDelegateHandler, RCTB
public static var enableAutoSetup: Bool = true

private weak var reactDelegate: ExpoReactDelegate?
private var bridgeDelegate: RCTBridgeDelegate?
private var bridgeDelegateHandler: DevClientAppDelegate = ExpoDevLauncherBridgeDelegateHandler()
private var deferredRootView: EXDevLauncherDeferredRCTRootView?
private var rootViewModuleName: String?
private var rootViewInitialProperties: [AnyHashable : Any]?
Expand Down Expand Up @@ -42,16 +42,14 @@ public class ExpoDevLauncherReactDelegateHandler: ExpoReactDelegateHandler, RCTB
ExpoDevMenuReactDelegateHandler.enableAutoSetup = false

self.reactDelegate = reactDelegate
self.bridgeDelegate = EXRCTBridgeDelegateInterceptor(bridgeDelegate: bridgeDelegate, interceptor: self)
self.bridgeDelegateHandler = EXRCTAppDelegateInterceptor(bridgeDelegate: bridgeDelegate, interceptor: self)

EXDevLauncherController.sharedInstance().autoSetupPrepare(self, launchOptions: launchOptions)
if let sharedController = UpdatesControllerRegistry.sharedInstance.controller {
// for some reason the swift compiler and bridge are having issues here
EXDevLauncherController.sharedInstance().updatesInterface = sharedController
}
// swiftlint:disable force_unwrapping
return EXDevLauncherDeferredRCTBridge(delegate: self.bridgeDelegate!, launchOptions: launchOptions)
// swiftlint:enable force_unwrapping
return EXDevLauncherDeferredRCTBridge(delegate: self.bridgeDelegateHandler, launchOptions: launchOptions)
}

public override func createRootView(reactDelegate: ExpoReactDelegate, bridge: RCTBridge, moduleName: String, initialProperties: [AnyHashable : Any]?) -> RCTRootView? {
Expand All @@ -76,12 +74,17 @@ public class ExpoDevLauncherReactDelegateHandler: ExpoReactDelegateHandler, RCTB
// MARK: EXDevelopmentClientControllerDelegate implementations

public func devLauncherController(_ developmentClientController: EXDevLauncherController, didStartWithSuccess success: Bool) {
let bridge = RCTBridge(delegate: self.bridgeDelegate, launchOptions: developmentClientController.getLaunchOptions())
let bridge = self.bridgeDelegateHandler.createBridgeAndSetAdapter(launchOptions: developmentClientController.getLaunchOptions())
developmentClientController.appBridge = bridge

// swiftlint:disable force_unwrapping
let rootView = RCTRootView(bridge: bridge!, moduleName: self.rootViewModuleName!, initialProperties: self.rootViewInitialProperties)
// swiftlint:enable force_unwrapping
guard let rootView = self.bridgeDelegateHandler.createRootView(
with: bridge,
// swiftlint:disable:next force_unwrapping
moduleName: self.rootViewModuleName!,
initProps: self.rootViewInitialProperties
) else {
return
}
rootView.backgroundColor = self.deferredRootView?.backgroundColor ?? UIColor.white
let window = getWindow()

Expand Down
2 changes: 1 addition & 1 deletion packages/expo-dev-menu/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@

### 🎉 New features

- [Android] Added support for the new architecture. ([#22607](https://github.com/expo/expo/pull/22607) by [@gabrieldonadel](https://github.com/gabrieldonadel))
- Added support for React Native 0.72. ([#22588](https://github.com/expo/expo/pull/22588) by [@kudo](https://github.com/kudo))
- Added support for the new architecture. ([#22607](https://github.com/expo/expo/pull/22607), [#22184](https://github.com/expo/expo/pull/22184) by [@gabrieldonadel](https://github.com/gabrieldonadel))

### 🐛 Bug fixes

Expand Down
3 changes: 1 addition & 2 deletions packages/expo-dev-menu/app/hooks/useDevSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,7 @@ export function useDevSettings() {
}, []);

const openRNDevMenu = React.useCallback(async () => {
await DevMenu.openDevMenuFromReactNative();
DevMenu.closeMenu();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does the closeMenu not necessary anymore?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessary anymore, we have to close this from the native side to ensure we first remove the expo-dev-menu overlay and then show the DevMenu from React Native otherwise both menus get closed.

E.g.

Screen.Recording.2023-06-19.at.11.14.07.mov

That's the reason for the changes on packages/expo-dev-menu/ios/Modules/DevMenuInternalModule.swift

DevMenu.openDevMenuFromReactNative();
}, []);

const openJSInspector = React.useCallback(async () => {
Expand Down
15 changes: 15 additions & 0 deletions packages/expo-dev-menu/ios/DevClientAppDelegate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2015-present 650 Industries. All rights reserved.

#import <React/RCTBridgeDelegate.h>
#if __has_include(<React-RCTAppDelegate/RCTAppDelegate.h>)
#import <React-RCTAppDelegate/RCTAppDelegate.h>
#elif __has_include(<React_RCTAppDelegate/RCTAppDelegate.h>)
// for importing the header from framework, the dash will be transformed to underscore
#import <React_RCTAppDelegate/RCTAppDelegate.h>
#endif

@interface DevClientAppDelegate : RCTAppDelegate

- (RCTBridge *)createBridgeAndSetAdapterWithLaunchOptions:(NSDictionary *_Nullable)launchOptions;

@end
Loading
Loading