ReactNative自动设置开发服务器IP

在开发 ReactNative 应用时,jsbundle 有两种加载方式。第一种是指定 url 通过网络进行加载;第二种是 pre-bundled 将 jsbundle 文件打包进 app 安装包中。

以下就是创建项目之后 ios 的默认配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* Loading JavaScript code - uncomment the one you want.
*
* OPTION 1
* Load from development server. Start the server from the repository root:
*
* $ npm start
*
* To run on device, change `localhost` to the IP address of your computer
* (you can get this by typing `ifconfig` into the terminal and selecting the
* `inet` value under `en0:`) and make sure your computer and iOS device are
* on the same Wi-Fi network.
*/
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
/**
* OPTION 2
* Load from pre-bundled file on disk. The static bundle is automatically
* generated by the "Bundle React Native code and images" build step when
* running the project on an actual device or running the project on the
* simulator in the "Release" build configuration.
*/
// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];

这里有个麻烦的地方就是,当我在真机设备上调试时。每次都需要执行 ifconfig 命令,然后将 localhost 修改为我的 ip 地址。并且在使用 git 进行代码管理时,一不小心将修改后的文件提交上去了,其他同事在 pull 时又会与自己的冲突。
最终实在忍受不了了,在想能不能编译时自动获取到本机的 ip 呢,这样就不用每次都手动修改了。于是找到了这篇文章: http://moduscreate.com/automated-ip-configuration-for-react-native-development/
我这里参考了他的方案并做了一点小调整。

按照他的步骤,首先添加 Run Script。
在 Xcode 中选择“Build Phases”,然后点击左上角的”+”选择“New Run Script Phase”。
在列表最后出现了“Run Script”,将其展开,然后编辑代码块的内容:

1
2
3
4
5
6
INFOPLIST="${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
echo "writing to $INFOPLIST"
PLISTCMD="Add :SERVER_IP string $(ifconfig | grep inet\ | tail -1 | cut -d " " -f 2)"
echo -n "$INFOPLIST" | xargs -0 /usr/libexec/PlistBuddy -c "$PLISTCMD" || true
PLISTCMD="Set :SERVER_IP $(ifconfig | grep inet\ | tail -1 | cut -d " " -f 2)"
echo -n "$INFOPLIST" | xargs -0 /usr/libexec/PlistBuddy -c "$PLISTCMD" || true

第二步编辑 AppDelegate.m 文件。
将项目默认生成的 jsCodeLocation 配置删除掉,并添加代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#if DEBUG
#if TARGET_OS_SIMULATOR
#warning "DEBUG SIMULATOR"
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
#else
#warning "DEBUG DEVICE"
NSString *serverIP = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"SERVER_IP"];
NSString *jsCodeUrlString = [NSString stringWithFormat:@"http://%@:8081/index.ios.bundle?platform=ios&dev=true", serverIP];
NSString *jsBundleUrlString = [jsCodeUrlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
jsCodeLocation = [NSURL URLWithString:jsBundleUrlString];
#endif
#else
#warning "PRODUCTION DEVICE"
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif

这里如果在模拟器中进行调试,那么 development server 则为 localhost;如果在真机设备中调试,那么 development server 则为电脑的 ip 地址。
到此已经可以实现自动设置 ip 地址了,如果还想要在 Chrome 中对设备进行调试,那么还需要修改一下 WebSocket 的配置。

第三步编辑 RCTWebSocketExecutor.m 文件。
在 Xcode 中打开 -> Libraries -> RCTWebSocket.xcodeproj -> RCTWebSocketExecutor.m 文件,大概在文件 53 行左右的位置,将 NSString *URLString = [NSString stringWithFormat:@"http://localhost:%zd/debugger-proxy?role=client", port]; 修改为:

1
2
3
4
5
6
#if TARGET_OS_SIMULATOR
NSString *serverIP = @"localhost";
#else
NSString *serverIP = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"SERVER_IP"];
#endif
NSString *URLString = [NSString stringWithFormat:@"http://%@:%zd/debugger-proxy?role=client", serverIP, port];

现在配置已经完成了,接下来就试试看是否有效吧。

经过修改之后相对于之前已经方便了不少,只是我还遇到一个问题。那就是我的 MacBook 在办公室时的 ip 跟在家里的 ip 是不同的。
这样的话每次切换环境都需要重新编译一下应用,还是有点麻烦。于是乎我自己将第一步的脚本作了下修改,新的内容如下:

1
2
3
4
5
6
INFOPLIST="${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
echo "writing to $INFOPLIST"
PLISTCMD="Add :SERVER_IP string $(hostname)"
echo -n "$INFOPLIST" | xargs -0 /usr/libexec/PlistBuddy -c "$PLISTCMD" || true
PLISTCMD="Set :SERVER_IP $(hostname)"
echo -n "$INFOPLIST" | xargs -0 /usr/libexec/PlistBuddy -c "$PLISTCMD" || true

这里我使用 hostname 来作为 development server 的地址,而不是 ip。这样的话即便是网络环境发生了变化,只要手机设备跟电脑处于同一个局域网内就不需要再重新编译应用了。