-
-
Save juanchoperezj/7a057c6ab1040e41abd0bde2f406e448 to your computer and use it in GitHub Desktop.
// imports... | |
@interface RocketSimLoader : NSObject | |
- (void)loadRocketSimConnect; | |
@end | |
@implementation RocketSimLoader | |
- (void)loadRocketSimConnect { | |
#if DEBUG | |
NSString *frameworkPath = @"/Applications/RocketSim.app/Contents/Frameworks/RocketSimConnectLinker.nocache.framework"; | |
NSBundle *frameworkBundle = [NSBundle bundleWithPath:frameworkPath]; | |
NSError *error = nil; | |
if (![frameworkBundle loadAndReturnError:&error]) { | |
NSLog(@"Failed to load linker framework: %@", error); | |
return; | |
} | |
NSLog(@"RocketSim Connect successfully linked"); | |
#endif | |
} | |
@end | |
@implementation AppDelegate | |
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions | |
{ | |
RocketSimLoader *loader = [[RocketSimLoader alloc] init]; | |
[loader loadRocketSimConnect]; | |
// code ... | |
return [super application:application didFinishLaunchingWithOptions:launchOptions]; | |
} | |
@end |
This exact code works with Expo, keep in mind that expo prebuild command (including --clean flag) will delete the iOS-generated code such as AppDelegate.
I generate this block of code with AI to modify the AppDelegate by running a command, it should work for your expo project.
import * as fs from 'fs';
import * as path from 'path';
// Function to insert code at a specific position
function insertCodeAtPosition(
content: string,
codeToInsert: string,
searchString: string,
after: boolean = true
): string {
const index = content.indexOf(searchString);
if (index === -1) {
throw new Error(`Search string "${searchString}" not found in the file.`);
}
const insertionIndex = after ? index + searchString.length : index;
return (
content.slice(0, insertionIndex) +
'\n' +
codeToInsert +
'\n' +
content.slice(insertionIndex)
);
}
// Main function to modify the AppDelegate.mm file
function modifyAppDelegate(filePath: string): void {
try {
// Read the file
let content = fs.readFileSync(filePath, 'utf8');
// First block of code to insert
const firstBlock = `@interface RocketSimLoader : NSObject
- (void)loadRocketSimConnect;
@end
@implementation RocketSimLoader
- (void)loadRocketSimConnect {
#if DEBUG
NSString *frameworkPath = @"/Applications/RocketSim.app/Contents/Frameworks/RocketSimConnectLinker.nocache.framework";
NSBundle *frameworkBundle = [NSBundle bundleWithPath:frameworkPath];
NSError *error = nil;
if (![frameworkBundle loadAndReturnError:&error]) {
NSLog(@"Failed to load linker framework: %@", error);
return;
}
NSLog(@"RocketSim Connect successfully linked");
#endif
}
@end`;
// Second block of code to insert
const secondBlock = ` RocketSimLoader *loader = [[RocketSimLoader alloc] init];
[loader loadRocketSimConnect];`;
// Insert the first block after the last import
content = insertCodeAtPosition(
content,
firstBlock,
'#import <React/RCTLinkingManager.h>'
);
// Insert the second block after self.initialProps = @{};
content = insertCodeAtPosition(
content,
secondBlock,
'self.initialProps = @{};'
);
// Write the modified content back to the file
fs.writeFileSync(filePath, content, 'utf8');
console.log('AppDelegate.mm has been successfully modified.');
} catch (error) {
console.error('Error modifying AppDelegate.mm:', (error as Error).message);
}
}
// Main execution
function main(): void {
const rootDir = process.cwd();
const iosDir = path.join(rootDir, 'ios');
// Find the .xcodeproj directory
const xcodeprojDir = fs
.readdirSync(iosDir)
.find((file) => file.endsWith('.xcodeproj'));
if (!xcodeprojDir) {
console.error('Could not find .xcodeproj directory in the ios folder.');
process.exit(1);
}
// Extract the project name from the .xcodeproj directory name
const projectName = xcodeprojDir.replace('.xcodeproj', '');
// Construct the file path
const filePath = path.join(iosDir, projectName, 'AppDelegate.mm');
// Check if the file exists
if (!fs.existsSync(filePath)) {
console.error(`File not found: ${filePath}`);
process.exit(1);
}
// Modify the AppDelegate.mm file
modifyAppDelegate(filePath);
}
// Run the main function
main();
@juanchoperezj Aah! Thank you! Maybe I can use AI to build an expo plugin so that we can build it on the EAS server. I build my app on EAS server for the very reason of losing my changes on prebuild. Thank you!
Hi guys!
I created an expo config plugin for RocketSim Connect.
Feel free to use it.
1. Download the file
./plugins/withRocketSimConnect.js
const { withAppDelegate, WarningAggregator } = require('@expo/config-plugins');
const {
mergeContents,
} = require('@expo/config-plugins/build/utils/generateCode');
const methodInvocationDefinition = `@interface RocketSimLoader : NSObject
- (void)loadRocketSimConnect;
@end
@implementation RocketSimLoader
- (void)loadRocketSimConnect {
#if DEBUG
NSString *frameworkPath = @"/Applications/RocketSim.app/Contents/Frameworks/RocketSimConnectLinker.nocache.framework";
NSBundle *frameworkBundle = [NSBundle bundleWithPath:frameworkPath];
NSError *error = nil;
if (![frameworkBundle loadAndReturnError:&error]) {
NSLog(@"Failed to load linker framework: %@", error);
return;
}
NSLog(@"RocketSim Connect successfully linked");
#endif
}
@end`;
const methodInvocationBlock = `RocketSimLoader *loader = [[RocketSimLoader alloc] init];
[loader loadRocketSimConnect];`;
const methodInvocationLineMatcher =
/-\s*\(BOOL\)\s*application:\s*\(UIApplication\s*\*\s*\)\s*\w+\s+didFinishLaunchingWithOptions:/g;
/** @param {string} appDelegate */
const modifyAppDelegate = (appDelegate) => {
let contents = appDelegate;
// Check if the method invocation is already there
if (contents.includes(methodInvocationBlock)) {
return contents;
}
// Check if the method invocation present in the file
if (!methodInvocationLineMatcher.test(contents)) {
WarningAggregator.addWarningIOS(
'withRocketSimConnect',
`Unable to determine correct insertion point in AppDelegate.
Skipping RocketSim Connect addition.`,
);
return contents;
}
// Check if the import statement is already there
if (!appDelegate.includes(methodInvocationDefinition)) {
contents = mergeContents({
src: contents,
anchor: methodInvocationLineMatcher,
newSrc: methodInvocationDefinition,
offset: -2,
tag: 'withRocketSimConnect - definition',
comment: '//',
}).contents;
}
contents = mergeContents({
src: contents,
anchor: methodInvocationLineMatcher,
newSrc: methodInvocationBlock,
offset: 2,
tag: 'withRocketSimConnect - didFinishLaunchingWithOptions',
comment: '//',
}).contents;
return contents;
};
/** @param {import('@expo/config-types').ExpoConfig} config */
const withRocketSimConnect = (config) => {
return withAppDelegate(config, (config) => {
if (['objc', 'objcpp'].includes(config.modResults.language)) {
config.modResults.contents = modifyAppDelegate(
config.modResults.contents,
);
} else {
WarningAggregator.addWarningIOS(
'withRocketSimConnect',
'Swift AppDelegate files are not supported yet.',
);
}
return config;
});
};
module.exports = withRocketSimConnect;
2. Update your app.json
or app.config.js
{
"expo": {
...
"plugins": [
...
"./plugins/withRocketSimConnect.js"
]
}
}
3. Use it with expo prebuild
cc @AvdLee
@khadorkin your plugin wasn't working for me, I was getting this warning to skip it:
I've made some tweaks and am posting for anyone else in need, it's the same steps as above (but typescript):
1. Download the file
./plugins/withRocketSimConnect.ts
const { withAppDelegate, WarningAggregator } = require('@expo/config-plugins');
const {
mergeContents,
} = require('@expo/config-plugins/build/utils/generateCode');
// Objective-C implementation
const objcDefinition = `@interface RocketSimLoader : NSObject
- (void)loadRocketSimConnect;
@end
@implementation RocketSimLoader
- (void)loadRocketSimConnect {
#if DEBUG
NSString *frameworkPath = @"/Applications/RocketSim.app/Contents/Frameworks/RocketSimConnectLinker.nocache.framework";
NSBundle *frameworkBundle = [NSBundle bundleWithPath:frameworkPath];
NSError *error = nil;
if (![frameworkBundle loadAndReturnError:&error]) {
NSLog(@"Failed to load linker framework: %@", error);
return;
}
NSLog(@"RocketSim Connect successfully linked");
#endif
}
@end`;
const objcInvocation = `RocketSimLoader *loader = [[RocketSimLoader alloc] init];
[loader loadRocketSimConnect];`;
// Swift implementation
const swiftDefinition = `
class RocketSimLoader {
func loadRocketSimConnect() {
#if DEBUG
let frameworkPath = "/Applications/RocketSim.app/Contents/Frameworks/RocketSimConnectLinker.nocache.framework"
guard let frameworkBundle = Bundle(path: frameworkPath) else {
print("Failed to find RocketSim framework")
return
}
do {
try frameworkBundle.loadAndReturnError()
print("RocketSim Connect successfully linked")
} catch {
print("Failed to load linker framework: \\(error)")
}
#endif
}
}`;
const swiftInvocation = `
let loader = RocketSimLoader()
loader.loadRocketSimConnect()`;
const objcMethodMatcher = /-\s*\(BOOL\)\s*application:\s*\(UIApplication\s*\*\s*\)\s*\w+\s+didFinishLaunchingWithOptions:/g;
const swiftMethodMatcher = /bindReactNativeFactory\(factory\)/g;
const modifyObjCAppDelegate = (appDelegate) => {
let contents = appDelegate;
if (contents.includes(objcInvocation)) {
return contents;
}
// Reset regex state
objcMethodMatcher.lastIndex = 0;
if (!objcMethodMatcher.test(contents)) {
WarningAggregator.addWarningIOS(
'withRocketSimConnect',
'Unable to determine correct insertion point in Objective-C AppDelegate.'
);
return contents;
}
// Reset regex state for replacement
objcMethodMatcher.lastIndex = 0;
if (!contents.includes(objcDefinition)) {
contents = mergeContents({
src: contents,
anchor: objcMethodMatcher,
newSrc: objcDefinition,
offset: -2,
tag: 'withRocketSimConnect - definition',
comment: '//',
}).contents;
}
// Reset regex state again
objcMethodMatcher.lastIndex = 0;
contents = mergeContents({
src: contents,
anchor: objcMethodMatcher,
newSrc: objcInvocation,
offset: 2,
tag: 'withRocketSimConnect - didFinishLaunchingWithOptions',
comment: '//',
}).contents;
return contents;
};
const modifySwiftAppDelegate = (appDelegate) => {
let contents = appDelegate;
if (contents.includes(swiftInvocation)) {
return contents;
}
// Reset regex state
swiftMethodMatcher.lastIndex = 0;
if (!swiftMethodMatcher.test(contents)) {
WarningAggregator.addWarningIOS(
'withRocketSimConnect',
'Unable to determine correct insertion point in Swift AppDelegate.'
);
return contents;
}
// Reset regex state for replacement
swiftMethodMatcher.lastIndex = 0;
if (!contents.includes('class RocketSimLoader')) {
const classAnchor = /class ReactNativeDelegate: ExpoReactNativeFactoryDelegate/g;
contents = mergeContents({
src: contents,
anchor: classAnchor,
newSrc: swiftDefinition,
offset: -1,
tag: 'withRocketSimConnect - swift definition',
comment: '//',
}).contents;
}
// Reset regex state again
swiftMethodMatcher.lastIndex = 0;
contents = mergeContents({
src: contents,
anchor: swiftMethodMatcher,
newSrc: swiftInvocation,
offset: 1,
tag: 'withRocketSimConnect - swift didFinishLaunchingWithOptions',
comment: '//',
}).contents;
return contents;
};
const withRocketSimConnect = (config) => {
return withAppDelegate(config, (config) => {
if (config.modResults.language === 'objc' || config.modResults.language === 'objcpp') {
config.modResults.contents = modifyObjCAppDelegate(config.modResults.contents);
} else if (config.modResults.language === 'swift') {
config.modResults.contents = modifySwiftAppDelegate(config.modResults.contents);
} else {
WarningAggregator.addWarningIOS(
'withRocketSimConnect',
`Unsupported AppDelegate language: ${config.modResults.language}`
);
}
return config;
});
};
module.exports = withRocketSimConnect;
2. Update your app.json or app.config.js
{
"expo": {
...
"plugins": [
...
"./plugins/withRocketSimConnect.ts"
]
}
}
3. Use it with expo prebuild
@builtbyproxy thank you. This worked great for me.
How can I integrate it with Expo?