Skip to content

Instantly share code, notes, and snippets.

@juanchoperezj
Last active January 10, 2025 13:16
Show Gist options
  • Save juanchoperezj/7a057c6ab1040e41abd0bde2f406e448 to your computer and use it in GitHub Desktop.
Save juanchoperezj/7a057c6ab1040e41abd0bde2f406e448 to your computer and use it in GitHub Desktop.
Use RocketSim Network Monitoring with both React Native CLI generated and Expo projects
// 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
@shettayyy
Copy link

How can I integrate it with Expo?

@juanchoperezj
Copy link
Author

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();

@shettayyy

@rshettyawaken
Copy link

rshettyawaken commented Sep 22, 2024

@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!

@khadorkin
Copy link

khadorkin commented Nov 8, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment