Skip to content

Commit 33744da

Browse files
authored
fix(auth, expo): plugin: skip expo-router on iOS for firebaseauth URLs (#8203)
- add a check for expo-router before patching AppDelegate - skip patching AppDelegate if no 'openURL' doesn't exist - throw Error if explicitly enabled but failed to find 'openURL' method - check that swizzling is enabled before patching - print a warning when config is default and AppDelegate is modified
1 parent 9d2817b commit 33744da

14 files changed

+1339
-7
lines changed
Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`Config Plugin iOS Tests - openUrlFix munges AppDelegate correctly - AppDelegate_bare_sdk43.m 1`] = `
4+
"// This AppDelegate template is used in Expo SDK 43
5+
// It is (nearly) identical to the pure template used when
6+
// creating a bare React Native app (without Expo)
7+
8+
#import "AppDelegate.h"
9+
10+
#import <React/RCTBridge.h>
11+
#import <React/RCTBundleURLProvider.h>
12+
#import <React/RCTRootView.h>
13+
#import <React/RCTLinkingManager.h>
14+
#import <React/RCTConvert.h>
15+
16+
#if defined(FB_SONARKIT_ENABLED) && __has_include(<FlipperKit/FlipperClient.h>)
17+
#import <FlipperKit/FlipperClient.h>
18+
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
19+
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
20+
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
21+
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
22+
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>
23+
24+
static void InitializeFlipper(UIApplication *application) {
25+
FlipperClient *client = [FlipperClient sharedClient];
26+
SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
27+
[client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
28+
[client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
29+
[client addPlugin:[FlipperKitReactPlugin new]];
30+
[client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
31+
[client start];
32+
}
33+
#endif
34+
35+
@implementation AppDelegate
36+
37+
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
38+
{
39+
#if defined(FB_SONARKIT_ENABLED) && __has_include(<FlipperKit/FlipperClient.h>)
40+
InitializeFlipper(application);
41+
#endif
42+
43+
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
44+
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"main" initialProperties:nil];
45+
id rootViewBackgroundColor = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"RCTRootViewBackgroundColor"];
46+
if (rootViewBackgroundColor != nil) {
47+
rootView.backgroundColor = [RCTConvert UIColor:rootViewBackgroundColor];
48+
} else {
49+
rootView.backgroundColor = [UIColor whiteColor];
50+
}
51+
52+
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
53+
UIViewController *rootViewController = [UIViewController new];
54+
rootViewController.view = rootView;
55+
self.window.rootViewController = rootViewController;
56+
[self.window makeKeyAndVisible];
57+
58+
[super application:application didFinishLaunchingWithOptions:launchOptions];
59+
60+
return YES;
61+
}
62+
63+
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge
64+
{
65+
// If you'd like to export some custom RCTBridgeModules, add them here!
66+
return @[];
67+
}
68+
69+
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
70+
#ifdef DEBUG
71+
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
72+
#else
73+
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
74+
#endif
75+
}
76+
77+
// Linking API
78+
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
79+
// @generated begin @react-native-firebase/auth-openURL - expo prebuild (DO NOT MODIFY) sync-5e029a87ac71df3ca5665387eb712d1b32274c6a
80+
if ([url.host caseInsensitiveCompare:@"firebaseauth"] == NSOrderedSame) {
81+
// invocations for Firebase Auth are handled elsewhere and should not be forwarded to Expo Router
82+
return NO;
83+
}
84+
// @generated end @react-native-firebase/auth-openURL
85+
return [RCTLinkingManager application:application openURL:url options:options];
86+
}
87+
88+
// Universal Links
89+
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
90+
return [RCTLinkingManager application:application
91+
continueUserActivity:userActivity
92+
restorationHandler:restorationHandler];
93+
}
94+
95+
@end
96+
"
97+
`;
98+
99+
exports[`Config Plugin iOS Tests - openUrlFix munges AppDelegate correctly - AppDelegate_sdk44.m 1`] = `
100+
"// This AppDelegate prebuild template is used in Expo SDK 44+
101+
// It has the RCTBridge to be created by Expo ReactDelegate
102+
103+
#import "AppDelegate.h"
104+
105+
#import <React/RCTBridge.h>
106+
#import <React/RCTBundleURLProvider.h>
107+
#import <React/RCTRootView.h>
108+
#import <React/RCTLinkingManager.h>
109+
#import <React/RCTConvert.h>
110+
111+
#if defined(FB_SONARKIT_ENABLED) && __has_include(<FlipperKit/FlipperClient.h>)
112+
#import <FlipperKit/FlipperClient.h>
113+
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
114+
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
115+
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
116+
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
117+
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>
118+
119+
static void InitializeFlipper(UIApplication *application) {
120+
FlipperClient *client = [FlipperClient sharedClient];
121+
SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
122+
[client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
123+
[client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
124+
[client addPlugin:[FlipperKitReactPlugin new]];
125+
[client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
126+
[client start];
127+
}
128+
#endif
129+
130+
@implementation AppDelegate
131+
132+
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
133+
{
134+
#if defined(FB_SONARKIT_ENABLED) && __has_include(<FlipperKit/FlipperClient.h>)
135+
InitializeFlipper(application);
136+
#endif
137+
138+
RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions];
139+
RCTRootView *rootView = [self.reactDelegate createRootViewWithBridge:bridge moduleName:@"main" initialProperties:nil];
140+
rootView.backgroundColor = [UIColor whiteColor];
141+
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
142+
UIViewController *rootViewController = [self.reactDelegate createRootViewController];
143+
rootViewController.view = rootView;
144+
self.window.rootViewController = rootViewController;
145+
[self.window makeKeyAndVisible];
146+
147+
[super application:application didFinishLaunchingWithOptions:launchOptions];
148+
149+
return YES;
150+
}
151+
152+
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge
153+
{
154+
// If you'd like to export some custom RCTBridgeModules, add them here!
155+
return @[];
156+
}
157+
158+
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
159+
#ifdef DEBUG
160+
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
161+
#else
162+
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
163+
#endif
164+
}
165+
166+
// Linking API
167+
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
168+
// @generated begin @react-native-firebase/auth-openURL - expo prebuild (DO NOT MODIFY) sync-5e029a87ac71df3ca5665387eb712d1b32274c6a
169+
if ([url.host caseInsensitiveCompare:@"firebaseauth"] == NSOrderedSame) {
170+
// invocations for Firebase Auth are handled elsewhere and should not be forwarded to Expo Router
171+
return NO;
172+
}
173+
// @generated end @react-native-firebase/auth-openURL
174+
return [RCTLinkingManager application:application openURL:url options:options];
175+
}
176+
177+
// Universal Links
178+
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
179+
return [RCTLinkingManager application:application
180+
continueUserActivity:userActivity
181+
restorationHandler:restorationHandler];
182+
}
183+
184+
@end
185+
"
186+
`;
187+
188+
exports[`Config Plugin iOS Tests - openUrlFix munges AppDelegate correctly - AppDelegate_sdk45.mm 1`] = `
189+
"// RN 0.68.1, Expo SDK 45 template
190+
// The main difference between this and the SDK 44 one is that this is
191+
// using React Native 0.68 and is written in Objective-C++
192+
193+
#import "AppDelegate.h"
194+
195+
#import <React/RCTBridge.h>
196+
#import <React/RCTBundleURLProvider.h>
197+
#import <React/RCTRootView.h>
198+
#import <React/RCTLinkingManager.h>
199+
#import <React/RCTConvert.h>
200+
201+
#import <React/RCTAppSetupUtils.h>
202+
203+
#if RCT_NEW_ARCH_ENABLED
204+
#import <React/CoreModulesPlugins.h>
205+
#import <React/RCTCxxBridgeDelegate.h>
206+
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
207+
#import <React/RCTSurfacePresenter.h>
208+
#import <React/RCTSurfacePresenterBridgeAdapter.h>
209+
#import <ReactCommon/RCTTurboModuleManager.h>
210+
211+
#import <react/config/ReactNativeConfig.h>
212+
213+
@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
214+
RCTTurboModuleManager *_turboModuleManager;
215+
RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
216+
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
217+
facebook::react::ContextContainer::Shared _contextContainer;
218+
}
219+
@end
220+
#endif
221+
222+
@implementation AppDelegate
223+
224+
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
225+
{
226+
RCTAppSetupPrepareApp(application);
227+
228+
RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions];
229+
230+
#if RCT_NEW_ARCH_ENABLED
231+
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
232+
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
233+
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
234+
_bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
235+
bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
236+
#endif
237+
238+
UIView *rootView = [self.reactDelegate createRootViewWithBridge:bridge moduleName:@"main" initialProperties:nil];
239+
240+
rootView.backgroundColor = [UIColor whiteColor];
241+
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
242+
UIViewController *rootViewController = [self.reactDelegate createRootViewController];
243+
rootViewController.view = rootView;
244+
self.window.rootViewController = rootViewController;
245+
[self.window makeKeyAndVisible];
246+
247+
[super application:application didFinishLaunchingWithOptions:launchOptions];
248+
249+
return YES;
250+
}
251+
252+
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge
253+
{
254+
// If you'd like to export some custom RCTBridgeModules, add them here!
255+
return @[];
256+
}
257+
258+
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
259+
{
260+
#if DEBUG
261+
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
262+
#else
263+
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
264+
#endif
265+
}
266+
267+
// Linking API
268+
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
269+
// @generated begin @react-native-firebase/auth-openURL - expo prebuild (DO NOT MODIFY) sync-5e029a87ac71df3ca5665387eb712d1b32274c6a
270+
if ([url.host caseInsensitiveCompare:@"firebaseauth"] == NSOrderedSame) {
271+
// invocations for Firebase Auth are handled elsewhere and should not be forwarded to Expo Router
272+
return NO;
273+
}
274+
// @generated end @react-native-firebase/auth-openURL
275+
return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options];
276+
}
277+
278+
// Universal Links
279+
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
280+
BOOL result = [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
281+
return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || result;
282+
}
283+
284+
#if RCT_NEW_ARCH_ENABLED
285+
286+
#pragma mark - RCTCxxBridgeDelegate
287+
288+
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
289+
{
290+
_turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
291+
delegate:self
292+
jsInvoker:bridge.jsCallInvoker];
293+
return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
294+
}
295+
296+
#pragma mark RCTTurboModuleManagerDelegate
297+
298+
- (Class)getModuleClassFromName:(const char *)name
299+
{
300+
return RCTCoreModulesClassProvider(name);
301+
}
302+
303+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
304+
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
305+
{
306+
return nullptr;
307+
}
308+
309+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
310+
initParams:
311+
(const facebook::react::ObjCTurboModule::InitParams &)params
312+
{
313+
return nullptr;
314+
}
315+
316+
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
317+
{
318+
return RCTAppSetupDefaultModuleFromClass(moduleClass);
319+
}
320+
321+
#endif
322+
323+
@end
324+
"
325+
`;
326+
327+
exports[`Config Plugin iOS Tests - openUrlFix must match positiveTemplateCases[0] 1`] = `
328+
"- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
329+
// @generated begin @react-native-firebase/auth-openURL - expo prebuild (DO NOT MODIFY) sync-5e029a87ac71df3ca5665387eb712d1b32274c6a
330+
if ([url.host caseInsensitiveCompare:@"firebaseauth"] == NSOrderedSame) {
331+
// invocations for Firebase Auth are handled elsewhere and should not be forwarded to Expo Router
332+
return NO;
333+
}
334+
// @generated end @react-native-firebase/auth-openURL
335+
int x=3;"
336+
`;
337+
338+
exports[`Config Plugin iOS Tests - openUrlFix must match positiveTemplateCases[3] 1`] = `
339+
" - ( BOOL ) application : ( UIApplication* ) application openURL : ( NSURL*) url options : ( NSDictionary < UIApplicationOpenURLOptionsKey , id > *) options
340+
341+
{
342+
343+
// @generated begin @react-native-firebase/auth-openURL - expo prebuild (DO NOT MODIFY) sync-5e029a87ac71df3ca5665387eb712d1b32274c6a
344+
if ([url.host caseInsensitiveCompare:@"firebaseauth"] == NSOrderedSame) {
345+
// invocations for Firebase Auth are handled elsewhere and should not be forwarded to Expo Router
346+
return NO;
347+
}
348+
// @generated end @react-native-firebase/auth-openURL
349+
"
350+
`;

packages/auth/plugin/__tests__/__snapshots__/iosPlugin.test.ts.snap renamed to packages/auth/plugin/__tests__/__snapshots__/iosPlugin_urlTypes.test.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`Config Plugin iOS Tests adds url types to the Info.plist 1`] = `
3+
exports[`Config Plugin iOS Tests - urlTypes adds url types to the Info.plist 1`] = `
44
{
55
"CFBundleURLTypes": [
66
{

0 commit comments

Comments
 (0)