开发iOS的过程中,有一件非常令人头疼的事,那就是网络请求的调试,无论是后端接口的问题,或是参数结构问题,你总需要一个网络调试的工具来简化调试步骤。

现状

App外调试

早先很多的网络调试都是通过App外的调试来进行的,这种的好处是可以完全不影响App内的任何逻辑,并且也不用去考虑对网络层可能造成的影响。

App内调试

目前GitHub上已经有非常多的网络调试框架,提供了简单的应用内收集网络请求的功能。

网络调试的原理

为了解决上面的问题,我们决定优化现有的App内调试的方案,下面先介绍一下目前主流的几个网络调试方案的原理。

URL Loading System中的URL Protocol

很多人在入门iOS的时候,都会通过Alamofire等第三方网络请求库来发送网络请求,但大部分的网络请求库都是基于标准库中URLConnection或者URLSession的封装,这两个类一个是旧的封装,而URLSession则是较新的也是被推荐使用的封装,它们本身对URL的加载、响应等一系列的事件进行了处理,其中就包含了所谓的传输协议的修改,标准库中提供了基础的URL传输协议,包括http、https、ftp等,同时也提供了自定义传输协议的方式。

标准库对于协议的处理流程也很简单,它提供了一个URLProtocol的类,并且有一个URLProtocol子类的数组(这个数组在URLConnection中是URLProtocol的类变量,可以通过registerClass这个类方法来插入,而在新的URLSession中,则是有对应的configuration来处理),在每次请求发出的时候,系统都会从这个数组中,依次询问是否能处理当前的请求,如果能处理,那么剩下的发送、接收事件都会交由这一个protocol来完成。

因此我们可以通过继承URLProtocol,并实现相关的方法,作为中间层来处理网络的发送、接收后的处理等,URLProtocol有能力改变URL加载过程中的每一个环节,但是又要去调用原始的响应方法,这样的设计让协议的处理不会影响网络调用以及网络响应的调用方式,让网络请求发送方无感知的情况下来做中间的处理。

正是这个类似“隐身”的特点,让URLProtocol成为了很多网络调试框架使用的首选,他们在URLSession中的configuration中插入网络调试的Protocol,那么所有对应的网络请求都会通过这个Protocol来发送, 在这其中只需要将请求再次发送一遍,就可以在不影响原有请求的情况下,拿到请求的所有回调,并在这其中进行记录。

以上面提到的GodEye 为首的就是这种方法,只不过它内部发送请求用的是老的URLConnection而不是URLSession,然而这倒是没有什么影响,这类的实现起来也是基本差不多

  1. 利用Objc的运行时来hook掉URLSession.init(configuration:delegate:delegateQueue:)方法,然后在调用原初始化方法之前,在URLSessionConfiguration中插入我们自定义的URLProtocol,同时调用URLProtocol下的类方法registerClass来注册自定义的类。
  2. 在自定义的URLProtocol子类中实现