先放两张效果图,是在同一个workspace
中代码修改前和修改后的app
网络请求的结果图。可以很明显看到通过修改自己的代码,改变了友盟分享SDK的网络请求协议。
说明:项目ATS Settings
设置了Allow Arbitrary Loads = YES
,即允许HTTP
请求。
本过程使用XCode 8.2.1
运行app
在iPhone5s
上,iPhone5s
上设置代理到本机8888
端口,Charles
监听本机8888
端口,可以拦截app
的HTTP/HTTPS
网络请求。通过Charles
拦截app
的网络请求,可以观察到app
的所有HTTP/HTTPS
请求,是否符合Apple
的ATS
要求。以下两张图聚焦友盟分享SDK
的网络请求。
最初,可以很轻松地观察拦截到的友盟SDK
请求为HTTP
协议(如图1),但是使用HTTPS
协议也能访问,比如 https://api.share.mob.com 和 http://api.share.mob.com 都可以访问。
由此看来,友盟分享的网站是支持HTTPS
的。这是改变友盟分享SDK
的网络协议的基础。
然后,寻找切入点。查看友盟SDK
的头文件,发现MOBFoundation.framework
的MOBFApplication.h
文件中有检测是否启用ATS
的功能。可以从这点出发对它进行改造。
替换方法很简单,使用runtime
知识即可,这里使用Method Swizzling
对原始方法进行替换。因为不需要原始方法的逻辑,只用自己的方法替换掉原始方法就好,不用管原始方法的逻辑。实现起来很简单:
-(void)exchangeMethodMobEnabledATS{
Method originATS_enable = class_getClassMethod([MOBFApplication class], @selector(enabledATS));
Method changeATS_enable = class_getClassMethod([HomeLifeAppDelegate class], @selector(app_enableATS));
method_exchangeImplementations(originATS_enable, changeATS_enable);
}
+ (BOOL)app_enableATS{
return true;
}
在application:didFinishLaunchingWithOptions:
方法中调用
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self exchangeMethodMobEnabledATS];
}
运行工程,Charles
中拦截的友盟SDK
网络请求如下:
效果非常明显,除了一个HTTP
请求以外,其它请求都改用HTTPS
了。这说明之前的思路是正确的。
剩下的这个HTTP请求
估计是时机的问题,猜测很可能是exchangeMethodMobEnabledATS
方法调用之前就已经确定好enableATS
的值了。我们可以将exchangeMethodMobEnabledATS
方法的执行时机提前,看是否有什么不同。
App能否在main()
函数之前执行代码呢?它的入口在哪里呢?
我们一般考虑App
的生命期是从main()
函数开始的,main()
函数执行以后会进入AppDelegate
中的application:willFinishLaunchingWithOptions:
等方法,这样看来,application:willFinishLaunchingWithOptions:
方法已经是最早可以执行代码的时机了。
事实上,有某种方法可以让函数在main()
函数之前或者之后执行。如下所示:
+ (BOOL)app_enableATS{
return true;
}
static void __attribute__((constructor)) before_main_exchange_method() {
Method originATS_enable = class_getClassMethod([MOBFApplication class], @selector(enabledATS));
Method changeATS_enable = class_getClassMethod([HomeLifeAppDelegate class], @selector(app_enableATS));
method_exchangeImplementations(originATS_enable, changeATS_enable);
}
可以放在工程的任何地方,都会在main()
函数执行前被执行(某些SDK和库不用开发者调用都能自动执行,原来The Point在这里→_→)。
也就是之后执行enabledATS
方法时都会调用自定义的app_enableATS
方法,保证友盟SDK
在判断环境的时候都会认为当前是ATS可用状态
。
这样做了以后,友盟SDK
已经完全支持HTTPS
了,无论工程设置了是否允许HTTP
。其网络请求如下:
PS:
在main()
函数或exit()
函数之后执行代码的能力:
staticvoid__attribute__((destructor)) after_main_exchange_method() {
}
可以用它干些有意思的事了(o)/