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
用来判断是否是在模拟器上运行,所以我们可以很简单的写出下面的代码:
#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,如下:
#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
:
但是这样会获取到一堆信息,我们需要的只是一个 IP。通过下面的命令,我们可以获取到我们要的那一行信息:ifconfig | grep inet\\ | tail -1
通过grep
命令可以进行文本的检索,再通过 tail -【number】
命令来从文本末尾开始选择要显示的行数(默认是10行),我们只要一行,然后从这一行中再取出 IP。 最后通过命令:ifconfig | grep inet\\ | tail -1 | cut -d " " -f 2
即可获取到最终想要的 IP 了。
其中, cut -d " "
表示将文本按空格分割, -f 2
表示显示分割后的第二个元素,也就是 IP 地址了。
好了,现在获取到 IP 地址了。我们知道,XCode 提供了一个在编译时运行脚本的功能,如图:
然后粘贴如下代码将获取到的 IP 写入到 plist 文件中。
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 写入到 plist 文件了。
这一步完成后,就可以将前面的 【你的IP】直接从 plist 文件那取了。如下:
NSString *serverIP = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"SERVER_IP"];
到这,基本就完成了。但是还有个问题:如果我们还想在真机上运行并且在 Chrome 上进行 Debug,那我们还需修改一下 RCTWebSocketExecutor.m 这个文件。将这个文件的 init 方法替换成一下代码即可:
- (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 了。实在想不到好的解决方案,所以根据现在的规律,暴力的将命令改成了:
ifconfig | grep 'inet 192' | head -1 | cut -d " " -f 2
,现在是直接检索 inet 192 然后获取第一个 IP。由于能力有限,只能想到这个方法了,如果大家有更好的方法来获取,可以评论指点下我哈。
参考资料:
http://c.biancheng.net/cpp/view/7005.html
http://www.jb51.cc/article/p-zyktdmss-bnx.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/