React-Native频繁修改IP,烦吗

React-Native 开发中,从模拟器切换到真机你必须要做的是:打开AppDelegate.m,将jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];修改为jsCodeLocation = [NSURL URLWithString:@"http://【你的IP】:8081/index.ios.bundle?platform=ios&dev=true"];, 从真机切换到模拟器,你又得修改一次,如此反复。更烦的是,团队开发的时候,有人不小心把他的IP push上去了,你pull下来的时候一编译运行,怎么自己修改的JS代码都不起作用呢?然后各种log,最后发现,尼玛,连接到别人IP去了。反正就是各种坑。
如果你也遇到以上的问题,那就继续看下去哈。
解决这个问题,其实很简单,只要我们判断一下如果是在模拟器上运行的话就用localhost,如果是真机的话就是用计算机的IP,Objective-C 也为我们提供了这样一个宏:TARGET_OS_SIMULATOR 用来判断是否是在模拟器上运行,所以我们可以很简单的写出下面的代码:

1
2
3
4
5
6
7
8
#if TARGET_OS_SIMULATOR
  jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
#else
#warning "DEBUG DEVICE"
  NSString *serverIP = 【你的IP】;
  NSString *jsCodeUrlString = [NSString stringWithFormat:@"http://%@:8081/index.ios.bundle?platform=ios&dev=true", serverIP];
  jsCodeLocation = [NSURL URLWithString:jsCodeUrlString];
#endif

这样,就完成了。但是如果是 release 的话想使用打包的文件咋办? 我们可以直接判断:如果是在 debug 下的话就是用上面的代码,否则则是用 bundle,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#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 = 【你的IP】;
  NSString *jsCodeUrlString = [NSString stringWithFormat:@"http://%@:8081/index.ios.bundle?platform=ios&dev=true", serverIP];
  jsCodeLocation = [NSURL URLWithString:jsCodeUrlString];
#endif
#else
//release
  jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif

这样,我们就能在 debug 和 release 都不用修改代码了。但是,有个问题,这个【你的IP】很是碍眼啊,公司和家里的 IP 肯定不一样啊,不还是得改。而且如果将这代码 push 到服务器,其他人也还是要改,根本问题还是不能解决。如果我们能动态获取到计算机的 IP 问题就解决了。是的,接下来我们要做的就是这个,在终端上获取 IP 的命令是(Linux和Mac OS):ifconfig
ifconfig
但是这样会获取到一堆信息,我们需要的只是一个 IP。通过下面的命令,我们可以获取到我们要的那一行信息:ifconfig | grep inet\\ | tail -1
ifconfig | grep inet\\ | tail -1
通过grep命令可以进行文本的检索,再通过 tail -【number】命令来从文本末尾开始选择要显示的行数(默认是10行),我们只要一行,然后从这一行中再取出 IP。 最后通过命令:ifconfig | grep inet\\ | tail -1 | cut -d " " -f 2即可获取到最终想要的 IP 了。
获取IP
其中, cut -d " " 表示将文本按空格分割, -f 2 表示显示分割后的第二个元素,也就是 IP 地址了。
好了,现在获取到 IP 地址了。我们知道,XCode 提供了一个在编译时运行脚本的功能,如图:
插入脚本
然后粘贴如下代码将获取到的 IP 写入到 plist 文件中。

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

写入IP
这样,每次编译程序的时候就会将 IP 写入到 plist 文件了。

Paste_Image.png
这一步完成后,就可以将前面的 【你的IP】直接从 plist 文件那取了。如下:

1
  NSString *serverIP =  [[NSBundle mainBundle] objectForInfoDictionaryKey:@"SERVER_IP"];

到这,基本就完成了。但是还有个问题:如果我们还想在真机上运行并且在 Chrome 上进行 Debug,那我们还需修改一下 RCTWebSocketExecutor.m 这个文件。将这个文件的 init 方法替换成一下代码即可:

1
2
3
4
5
6
7
8
9
10
11
12
- (instancetype)init
{
  NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
  NSInteger port = [standardDefaults integerForKey:@"websocket-executor-port"] ?: 8081;
#if TARGET_OS_SIMULATOR
  NSString *URLString = [NSString stringWithFormat:@"http://localhost:%zd/debugger-proxy", port];
#else
  NSString *serverIP = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"SERVER_IP"];
  NSString *URLString = [NSString stringWithFormat:@"http://%@:%zd/debugger-proxy", serverIP, port];
#endif
  return [self initWithURL:[RCTConvert NSURL:URLString]];
}

######总结######
通过判断当前运行设备的宏以及编译时通过脚本将 IP 写入 plist 文件我们就可以实现动态的设置 IP 了,以后我们就不需要反复修改 IP 了。另外,通过jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 的方式运行的时候,我们也可以将打包的命令写到脚本上,这样就不用再通过终端去打包了。

######补充######
通过这些天的实践,出现了一个问题,有时候用 grep 来筛选 IP 的时候,找到的不一定准确,因为我们现在通过 grep找出 IP 之后,取的是最后个 IP 。而如果这时候我们运行了 android的模拟器就会出现下面的情况:
两个 IP
这时我们使用的就是最后一个 IP,也就是模拟器的 IP 了。实在想不到好的解决方案,所以根据现在的规律,暴力的将命令改成了:
ifconfig | grep 'inet 192' | head -1 | cut -d " " -f 2 ,现在是直接检索 inet 192 然后获取第一个 IP。由于能力有限,只能想到这个方法了,如果大家有更好的方法来获取,可以评论指点下我哈。

参考资料:
http://c.biancheng.net/cpp/view/7005.html
http://www.cnblogs.com/peida/archive/2012/11/07/2758084.html
http://www.jb51.net/article/41872.htm
http://www.cnblogs.com/end/archive/2012/02/21/2360965.html
https://github.com/facebook/react-native/issues/4245
http://moduscreate.com/automated-ip-configuration-for-react-native-development/