最近在项目的开发中遇到了一个问题。用户反映在iPhone Xs Max上一些界面显示异常。但是手头没有这个手机,而且自从集成了萤石云的SDK以后,项目无法在模拟器上调试。现在再申请购置一部XS Max手机也来不及了,而且总不能一遇到手头没有的设备就申请新手机吧。于是就需要考虑如何让这种集成了不支持模拟器的SDK如何也能在模拟器上运行,从而解决这类项目中遇到的个别机型差异造成的BUG。
一、什么情况下出现这种问题
刚才说了,出现在这种问题是集成了第三方的SDK,该SDK不支持模拟器运行。其实,再问题具体一点,是因为这类SDK不支持在x86下编译的话,则不能在模拟器上调试运行。
二、解决这种问题的思路。
其实,解决这类问题的方法就是四个字:条件编译。利用条件编译,在模拟器环境下不对不支持x86的SDK进行编译即可。
对于有些SDK提供了两套版本,一套Release环境,一套Debug环境。Debug环境支持在模拟器进行,对于这类SDK,则可以通过替换SDK的framework的方式来解决模拟器调试问题。比如阿里云短视频SDK,七牛视频播放SDK等等。
三、具体解决问题的方法。
我们要设置一个预编译宏,来处理条件编译,比如设置一个预编译宏,名字叫EZDISABLE。在Target的Build Settings选项卡下面,我们找到Preprocessor Macros项,双击后增加一项:EZDISABLE=1。可以只在Debug或者只在Release加入该宏,具体依据你自己的需求而定。这里我都加入了。
下面,就是将用到不支持x86的SDK的调用,通过EZDISABLE宏“隔离”开。在我这个项目中,需要把萤石云的SDK“隔离”。
// 如果没有定义EZDISABLE宏, 则编译以下内容, 否则不编译
#ifndef EZDISABLE
#import <EZOpenSDKFramework/EZOpenSDKFramework.h>
#endif
......
......
- (void)setupEZ{
// 如果没有定义EZDISABLE宏, 则编译以下内容, 否则不编译
#ifndef EZDISABLE
[EZOpenSDK initLibWithAppKey:@"xxxxxx"];
#endif
}
代码可能分散的比较多,需要耐心的一个个把他给去掉。
上面还说了,有些SDK分Release和Debug两个版本,Debug支持模拟器编译。对于这类,如果你的SDK是通过手工添加的方式来集成的话,那你就手工一个个一个把SDK给替换了,如果你用的是cocoaPods的话,那一般只需要将Podfile的引用稍作修改既可以。我这个用的是cocoaPods来集成的七牛视频播放SDK。我将Podfile文件修改成下面的即可:
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
inhibit_all_warnings!
target 'BBTSer' do
...
...
pod "PLPlayerKit", :podspec => 'https://raw.githubusercontent.com/pili-engineering/PLPlayerKit/master/PLPlayerKit-Universal.podspec' #七牛官方提供的支持模拟器的版本,在官方网站上可以查到
end
有时候,我们为了方便代码的复用,做了很多的私有Pod子库提供给主项目集成。如果在这些私有库中引用了类似七牛这种SDK该怎么办?其实我们根据cocoaPods的规则,完全可以不用考虑子库,直接在主工程的Podfile文件将SDK的引用改了就行。cocoaPods会自动处理这类依赖,非常方便。
四、问题优化。
在上面,已经基本上解决了如何在模拟器上编译的问题。但是不太方便,我们总不能每次切换真机与模拟器的时候,都去修改一下预编译宏和Podfile文件吧?
具体该怎么进行优化可以让我们方便的切换调试环境呢?
对于预编译宏的设置,我们可以通过再设置一个Target的方式来处理,我们将工程的Target复制一个,比如原工程Target名字为:BBTSer。复制的一个改名为:BBTSer Simulator。我们只在BBTSer Simulator的Build Settings选项卡中的Preprocessor Macros增加预编译宏EZDISABLE=1。原来的老Target不变。如果用cocoaPods的话,在增加了一个新的Target后要相应的修改一下Podfile文件来增加对这个Target的支持。这样,就可以通过切换Target的方式来处理真机与模拟器的调试切换。
再说下Podfile文件的优化。上面讲了,对于七牛的SDK官方支持了在模拟器调试,但是是不同的pod库地址。需要进行切换。那么我们如何能够使cocoaPod能够以一种相对自动的方式来处理这种切换呢?这需要我们更加的了解Podfile的语法才行。首先,在上面我们建立了两个Target。所以Podfile文件要同时处理两个Target的集成。然后,对于不同的Target有可能要集成不同的七牛SDK地址,如何在pod install的时候来处理这种不同?
对于第一个问题其实很简单,百度一下很多方式,这里不在叙述。对于第二个问题需要说一下。在Podfile中,如果两个或者多个Target引用了相同的pod库,但是源文件地址不一样的话,是会报错的。那么我们就需要增加一个开关来处理一下,如果开关打开,则引入正常地址,如果开关关闭则引入另外一个地址。我们知道,Podfile文件的语法其实跟Ruby差不多。所以,我们可以增加一个变量release,然后通过判断release变量是false还是true来引入不同的源地址。这样,我们的Podfile文件就变成了如下行使。
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
inhibit_all_warnings!
#开关变量
release=false
# abstract_target:抽象target。内部包含两个Target:BBTSer和BBTSer Simulator。这两个Targe继承这个抽象target。抽象target的名字可以随意定义,这里我定义成BBT
abstract_target 'BBT' do
...
...
if release then
target 'BBTSer' do
pod "PLPlayerKit"
end
else
target 'BBTSer Simulator' do
pod "PLPlayerKit", :podspec => 'https://raw.githubusercontent.com/pili-engineering/PLPlayerKit/master/PLPlayerKit-Universal.podspec'
end
end
end
到了这里,我们在每次切换模拟器和真机的时候,只要把release的状态改一下就可以了,真机改成true,模拟器改成false。然后在pod install或者pod update。你可能会说,这样也不好。每次还要改Podfile文件。那么好,我再加一个shell脚本,来帮着我们处理这个改动,不要每次都改Podfile文件了,省的不小心改错了。shell文件起名为podinstall.sh。内容如下:
#!/bin/bash
file="Podfile"
if [ ! -f "$file" ]; then
echo '没有找到Podfile文件'
exit
fi
if [ ! -n "$1" ] ;then
echo "请输入参数。真机调试以及发布到AppStore请输入release,模拟器调试输入debug"
exit
fi
MODE=$1
if [ "$MODE" != "release" ] && [ "$MODE" != "debug" ];then
echo "请输入正确的参数。真机调试以及发布到AppStore请输入release,模拟器调试输入debug"
exit
fi
echo "当前集成环境:$MODE"
podmodel="release=false"
if [ "$MODE" == "release" ];then
podmodel="release=true"
fi
echo $podmodel
# sed -i "s/release=true/$podmodel/g"
# sed -i '' '/release=true/$podmodel/g' $file
sed -i '' "s#release=true#$podmodel#g" $file
sed -i '' "s#release=false#$podmodel#g" $file
#更新pod配置
echo "********************************************"
echo "* 配置完毕,开始Pod集成 *"
echo "********************************************"
pod install
每次pod install的时候,用这个shell来代替。将它和Podfile文件放到一起。真机pod调用./podinstall.sh release。模拟器pod调用./podinstall.sh debug。怎么样,是不是方便多了?