Skip to content

Commit f5465f5

Browse files
authored
fix(iOS): expo plugin override keyword on ios (#86)
1 parent 9bc1d9c commit f5465f5

File tree

7 files changed

+1382
-101
lines changed

7 files changed

+1382
-101
lines changed

plugin/__tests__/__snapshots__/withRNOrientationAppDelegate.spec.ts.snap

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ exports[`withRNOrientationAppDelegate updates the AppDelegate.mm with both heade
7777
"
7878
`;
7979
80-
exports[`withRNOrientationAppDelegate updates the AppDelegate.swift with the method implementation 1`] = `
80+
exports[`withRNOrientationAppDelegate updates the AppDelegate52.swift with the method implementation having override when sdk is < 53 1`] = `
8181
"import UIKit
8282
import React
8383
import React_RCTAppDelegate
@@ -117,3 +117,84 @@ class AppDelegate: RCTAppDelegate {
117117
}
118118
"
119119
`;
120+
121+
exports[`withRNOrientationAppDelegate updates the AppDelegate53.swift with the method implementation without override when sdk is >= 53 1`] = `
122+
"import Expo
123+
import React
124+
import ReactAppDependencyProvider
125+
126+
@UIApplicationMain
127+
public class AppDelegate: ExpoAppDelegate {
128+
var window: UIWindow?
129+
130+
var reactNativeDelegate: ExpoReactNativeFactoryDelegate?
131+
var reactNativeFactory: RCTReactNativeFactory?
132+
133+
public override func application(
134+
_ application: UIApplication,
135+
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
136+
) -> Bool {
137+
let delegate = ReactNativeDelegate()
138+
let factory = ExpoReactNativeFactory(delegate: delegate)
139+
delegate.dependencyProvider = RCTAppDependencyProvider()
140+
141+
reactNativeDelegate = delegate
142+
reactNativeFactory = factory
143+
bindReactNativeFactory(factory)
144+
145+
#if os(iOS) || os(tvOS)
146+
window = UIWindow(frame: UIScreen.main.bounds)
147+
factory.startReactNative(
148+
withModuleName: "main",
149+
in: window,
150+
launchOptions: launchOptions)
151+
#endif
152+
153+
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
154+
}
155+
// React Native Orientation Director @generated begin @react-native-orientation-director/supportedInterfaceOrientationsFor-implementation - expo prebuild (DO NOT MODIFY) sync-e6869f486930a8ef16098c6ceec0011ee7ff621f
156+
157+
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
158+
return OrientationDirector.getSupportedInterfaceOrientationsForWindow()
159+
}
160+
161+
// React Native Orientation Director @generated end @react-native-orientation-director/supportedInterfaceOrientationsFor-implementation
162+
163+
// Linking API
164+
public override func application(
165+
_ app: UIApplication,
166+
open url: URL,
167+
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
168+
) -> Bool {
169+
return super.application(app, open: url, options: options) || RCTLinkingManager.application(app, open: url, options: options)
170+
}
171+
172+
// Universal Links
173+
public override func application(
174+
_ application: UIApplication,
175+
continue userActivity: NSUserActivity,
176+
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
177+
) -> Bool {
178+
let result = RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
179+
return super.application(application, continue: userActivity, restorationHandler: restorationHandler) || result
180+
}
181+
}
182+
183+
class ReactNativeDelegate: ExpoReactNativeFactoryDelegate {
184+
// Extension point for config-plugins
185+
186+
override func sourceURL(for bridge: RCTBridge) -> URL? {
187+
// needed to return the correct URL for expo-dev-client.
188+
bridge.bundleURL ?? bundleURL()
189+
}
190+
191+
override func bundleURL() -> URL? {
192+
#if DEBUG
193+
return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: ".expo/.virtual-metro-entry")
194+
#else
195+
return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
196+
#endif
197+
}
198+
}
199+
"
200+
`;
File renamed without changes.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import Expo
2+
import React
3+
import ReactAppDependencyProvider
4+
5+
@UIApplicationMain
6+
public class AppDelegate: ExpoAppDelegate {
7+
var window: UIWindow?
8+
9+
var reactNativeDelegate: ExpoReactNativeFactoryDelegate?
10+
var reactNativeFactory: RCTReactNativeFactory?
11+
12+
public override func application(
13+
_ application: UIApplication,
14+
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
15+
) -> Bool {
16+
let delegate = ReactNativeDelegate()
17+
let factory = ExpoReactNativeFactory(delegate: delegate)
18+
delegate.dependencyProvider = RCTAppDependencyProvider()
19+
20+
reactNativeDelegate = delegate
21+
reactNativeFactory = factory
22+
bindReactNativeFactory(factory)
23+
24+
#if os(iOS) || os(tvOS)
25+
window = UIWindow(frame: UIScreen.main.bounds)
26+
factory.startReactNative(
27+
withModuleName: "main",
28+
in: window,
29+
launchOptions: launchOptions)
30+
#endif
31+
32+
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
33+
}
34+
35+
// Linking API
36+
public override func application(
37+
_ app: UIApplication,
38+
open url: URL,
39+
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
40+
) -> Bool {
41+
return super.application(app, open: url, options: options) || RCTLinkingManager.application(app, open: url, options: options)
42+
}
43+
44+
// Universal Links
45+
public override func application(
46+
_ application: UIApplication,
47+
continue userActivity: NSUserActivity,
48+
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
49+
) -> Bool {
50+
let result = RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
51+
return super.application(application, continue: userActivity, restorationHandler: restorationHandler) || result
52+
}
53+
}
54+
55+
class ReactNativeDelegate: ExpoReactNativeFactoryDelegate {
56+
// Extension point for config-plugins
57+
58+
override func sourceURL(for bridge: RCTBridge) -> URL? {
59+
// needed to return the correct URL for expo-dev-client.
60+
bridge.bundleURL ?? bundleURL()
61+
}
62+
63+
override func bundleURL() -> URL? {
64+
#if DEBUG
65+
return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: ".expo/.virtual-metro-entry")
66+
#else
67+
return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
68+
#endif
69+
}
70+
}

plugin/__tests__/withRNOrientationAppDelegate.spec.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,25 @@ describe('withRNOrientationAppDelegate', function () {
1919
expect(result).toMatchSnapshot();
2020
});
2121

22-
it('updates the AppDelegate.swift with the method implementation', async function () {
22+
it('updates the AppDelegate52.swift with the method implementation having override when sdk is < 53', async function () {
2323
const appDelegatePath = path.join(
2424
__dirname,
25-
'./fixtures/AppDelegate.swift'
25+
'./fixtures/AppDelegate52.swift'
2626
);
2727
const appDelegate = await fs.promises.readFile(appDelegatePath, 'utf-8');
2828

29-
const result = swiftFileUpdater(appDelegate);
29+
const result = swiftFileUpdater(appDelegate, '52');
30+
expect(result).toMatchSnapshot();
31+
});
32+
33+
it('updates the AppDelegate53.swift with the method implementation without override when sdk is >= 53', async function () {
34+
const appDelegatePath = path.join(
35+
__dirname,
36+
'./fixtures/AppDelegate53.swift'
37+
);
38+
const appDelegate = await fs.promises.readFile(appDelegatePath, 'utf-8');
39+
40+
const result = swiftFileUpdater(appDelegate, '53');
3041
expect(result).toMatchSnapshot();
3142
});
3243
});

plugin/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212
"expo-module": "expo-module"
1313
},
1414
"devDependencies": {
15-
"expo-module-scripts": "^4.1.7",
16-
"glob": "^11.0.2"
15+
"expo": "53.0.13",
16+
"expo-module-scripts": "4.1.8",
17+
"glob": "11.0.3"
1718
},
1819
"engines": {
1920
"node": ">=18"

plugin/src/withRNOrientationAppDelegate.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,52 @@ async function readAppDelegateFileAndUpdateContents(
1515
): Promise<ExportedConfigWithProps<AppDelegateProjectFile>> {
1616
const { modResults: appDelegateFile } = config;
1717

18-
const worker = getCompatibleFileUpdater(appDelegateFile.language);
19-
appDelegateFile.contents = worker(appDelegateFile.contents);
18+
const fileUpdater = getCompatibleFileUpdater(appDelegateFile.language);
19+
if (fileUpdater.language === 'swift') {
20+
const { worker } = fileUpdater;
21+
appDelegateFile.contents = worker(
22+
appDelegateFile.contents,
23+
config.sdkVersion
24+
);
25+
} else {
26+
const { worker } = fileUpdater;
27+
appDelegateFile.contents = worker(appDelegateFile.contents);
28+
}
2029

2130
return config;
2231
}
2332

2433
function getCompatibleFileUpdater(
2534
language: AppDelegateProjectFile['language']
26-
): (originalContents: string) => string {
35+
):
36+
| { language: 'swift'; worker: typeof swiftFileUpdater }
37+
| { language: 'objc' | 'objcpp'; worker: typeof objCFileUpdater } {
2738
switch (language) {
2839
case 'objc':
2940
case 'objcpp': {
30-
return objCFileUpdater;
41+
return {
42+
language,
43+
worker: objCFileUpdater,
44+
};
3145
}
3246
case 'swift':
33-
return swiftFileUpdater;
47+
return {
48+
language,
49+
worker: swiftFileUpdater,
50+
};
3451
default:
3552
throw new Error(
3653
`Cannot add React Native Orientation Director code to AppDelegate of language "${language}"`
3754
);
3855
}
3956
}
4057

41-
export function swiftFileUpdater(originalContents: string): string {
42-
const supportedInterfaceOrientationsForCodeBlock = `\n override func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
58+
export function swiftFileUpdater(
59+
originalContents: string,
60+
sdkVersion?: string
61+
): string {
62+
const methodPrefix = !sdkVersion?.includes('53') ? 'override' : '';
63+
const supportedInterfaceOrientationsForCodeBlock = `\n ${methodPrefix} func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
4364
return OrientationDirector.getSupportedInterfaceOrientationsForWindow()
4465
}\n`;
4566
const rightBeforeLastClosingBrace =

0 commit comments

Comments
 (0)