我们自己的app中集成的官方SDK需要和官方客户端通信,在iOS中,调起其他app,基本上都是用:
这就类似于http中的GET方法,我们可以在AppDelegate.m
的-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
中接收这个url然后做相应的处理。比如url如果是weixin://app/wx0fff8fc7685bb2c6/auth/?scope=snsapi_userinfo
,那么微信就知道其他app请求的是auth,scope是snsapi_userinfo,urlschema是wx0fff8fc7685bb2c6,这样微信处理以后,就可以通过打开wx0fff8fc7685bb2c6://处理结果
来调起我们自己的app,我们自己的app同样处理url,就能得到返回结果了。
这里最重要的是URLScheme
,可以在Info.plist
中设置,比如:
这样如果其他的应用程序(包括Safari中的html页面中的href),都可以通过打开wx0fff8fc7685bb2c6
或wxd930ea5d5a258f4f
掉起我们的app,比如Safari中给的链接地址是:
这样我们在
在js中。比如下面的代码AppDelegate.m
的-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
中解析一下,就知道用户想查看pid123456的商品,展现这个就对了。
类似于HTTP中的GET,通过url传递的信息毕竟是有长度上限的,虽然Apple并没有文档指定最大长度,但是对于需要传递的内容比较长的情况(比如分享文件到微信),最好用类似于HTTP中的POST方法,iOS中常用的是系统粘贴板。
粘贴板好像是一个全局(操作系统全局)的map,每个app都可以去set和get。而且目前还没有发现最大限制(除了系统内存限制),所以是一个很好的传递数据的方法。这样我想起很久以前浏览器中两个Tab中的页面通信的黑科技,有人就用粘贴板实现了,一个页面set,另一个页面循环get。
知道了通信原理。下一步要做的就是监控SDK调用这些方法的时候,传递的参数的格式。如果我们也能生成同样的参数,然后发起请求,官方的客户端也不能区分到底用的是OpenShare呢还是官方SDK。狸猫换太子,达到同样的效果。
监控的思路就是hook关键方法。比如在js中,我们想要在alert的时候输出到console再alert,可以:
这样,如果调用alert("Hello World");
,就能输出到console再弹窗了。
同样在objc中,我们可以用runtime提供的API进行Method Swizzling。
我们首先把各种官方提供的Demo运行一下,然后在appdelegate中对我们确定的几个方法进行Swizzling:
这样可以在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
中调用一下[self swizzle];
就可以监控了。
其中还遇到一些坑,比如研究Sina微博的时候,监控不到粘贴板数据,百思不得其解。于是用lldb添加断点:
这样就能把所有调用UIPasteboard
的方法都打印出来了。原来Sina微博用的是[UIPasteboard generalPasteboard].items
方法设置粘贴板。这个方法没有hook当然监控不到啦。
update 20150717
今天又学到一种监控mac/iOS模拟器中runtime message的方法:
这样在/tmp/下目录就能生成/tmp/msgSends-1234
类似的文件了。可以用tail -f
查看。非常方便。加上从源代码编译objc runtime中提到的方法,现在都有两种监控消息发送的方法了。另外还听说Dtrace
的方法。
我们知道粘贴板传递的数据了,得到的是一个NSData类型,还需要猜测这个二进制NSData是如何生成和解码的。通过把NSData写入到文件,隐约看到bplist的身影,于是用node.js和plutil
试一下:
果然可以解析出来。最后经过尝试,目前只发现两种序列化方式:
这样就能解决app和客户端之间通信的问题了