詳解如何攔截iOS所有網(wǎng)絡(luò)請求
背景
最近在研究iOS無埋點統(tǒng)計技術(shù),我們的統(tǒng)計SDK主要分兩部分:點擊事件和網(wǎng)絡(luò)請求。統(tǒng)計所有的點擊事件是采用Method Swizzling實現(xiàn)的,可以做到使用中不需要一行代碼實現(xiàn)統(tǒng)計所有事件,具體細節(jié)將來我會專門抽幾篇文章介紹。
今天主要說說如何統(tǒng)計APP中的所有網(wǎng)絡(luò)請求。公司網(wǎng)絡(luò)請求如果不是靜態(tài)庫或者框架,很容易想到在網(wǎng)絡(luò)請求發(fā)送和返回時添加統(tǒng)計的代碼。如何在不修改原來代碼(或者修改最少)的基礎(chǔ)上攔截所有的請求呢,能不能從系統(tǒng)層面上攔截回調(diào)呢?答案是肯定的,蘋果有一個黑魔法NSURLProtocol。
介紹
NSURLProtocol是iOS URL Loading System中的一部分,看起來像是一個協(xié)議,但其實這是一個類,而且必須使用該類的子類,并且需要被注冊。先看看他在URL Loading System中的位置:

使用場景
不管是UIWebView還是URLSession還是第三方的AFNetWorkong、Alamofire或者SDWebImage他們都是基于URLSession或者NSURLConnection來實現(xiàn)的,因此可以通過NSURLProtocol做自定義操作。
- 重定向網(wǎng)絡(luò)請求
- 攔截網(wǎng)絡(luò)加載,采用本地緩存
- 修改Request信息
- 自定義返回結(jié)果
- 對請求進行HTTPDNS解析,動態(tài)設(shè)置Host,解決不同網(wǎng)絡(luò)下客戶端不能訪問的情況
實現(xiàn)
首先要繼承NSURLProtocol創(chuàng)建自定義的類,然后重寫startLoading、stopLoading添加我們的統(tǒng)計代碼就可以了:
static NSString * const hasInitKey = @"LLMarkerProtocolKey"; @interface LLMarkerURLProtocol : NSURLProtocol @end
子類實現(xiàn)的NSURLProtocol方法:
1.0 +(BOOL)canInitWithRequest:(NSURLRequest *)request;子類是否能響應(yīng)該請求。
+(BOOL)canInitWithRequest:(NSURLRequest *)request{
if ([NSURLProtocol propertyForKey:hasInitKey inRequest:request]) {
return NO;
}
return YES;
}
2.0 +(NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request;自定義網(wǎng)絡(luò)請求,如果不需要處理直接返回request。
+(NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request{
return request;
}
3.0 -(void)startLoading 開始網(wǎng)絡(luò)請求,需要在該方法中發(fā)起一個請求,對于NSURLConnection來說,就是創(chuàng)建一個NSURLConnection,對于NSURLSession,就是發(fā)起一個NSURLSessionTask 。一般下載前需要設(shè)置該請求正在進行下載,防止多次下載的情況發(fā)生。
-(void)startLoading{
NSMutableURLRequest *mutableReqeust = [[self request] mutableCopy];
//做下標記,防止遞歸調(diào)用
[NSURLProtocol setProperty:@YES forKey:hasInitKey inRequest:mutableReqeust];
self.connection = [NSURLConnection connectionWithRequest:mutableReqeust delegate:self];
}
4.0 -(void)stopLoading 停止相應(yīng)請求,清空請求Connection 或Task。
-(void)stopLoading{
[self.connection cancel];
}
5.0 實現(xiàn)NSURLConnectionDelegate、NSURLConnectionDataDelegate或者NSURLSessionTaskDelegate。
#pragma mark - NSURLConnectionDelegate
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
[self.client URLProtocol:self didFailWithError:error];
}
#pragma mark - NSURLConnectionDataDelegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
self.responseData = [[NSMutableData alloc] init];
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.responseData appendData:data];
[self.client URLProtocol:self didLoadData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.client URLProtocolDidFinishLoading:self];
}
使用
一、在AppDelegate中注冊:
[NSURLProtocol registerClass:[LLMarkerURLProtocol class]];
這樣能攔截UIWebView和自定義的請求了,如果要攔截AFNetWorking、Alamofire等第三方請求還需要做一些修改。
二、LLMarkerURLProtocol中添加自定義NSURLSessionConfiguration方法:
+ (NSURLSessionConfiguration *) defaultSessionConfiguration{
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSMutableArray *array = [[config protocolClasses] mutableCopy];
[array insertObject:[self class] atIndex:0];
config.protocolClasses = array;
return config;
}
攔截第三方網(wǎng)絡(luò)庫方法就是讓第三方使用我們這個NSURLSessionConfiguration。因為我們在自己的NSURLSessionConfiguration 中的protocolClasses中注冊了自己類。
三、 下面以Alamofire為例
1.0 繼承Alamofire.SessionManager 自定義SessionManager
class LLSessionManger: Alamofire.SessionManager{
public static let sharedManager: SessionManager = {
let configuration = LLMarkerURLProtocol.defaultSessionConfiguration()
configuration?.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
let manager = Alamofire.SessionManager(configuration: configuration!)
return manager
}()
}
2.0 使用 LLSessionManger進行網(wǎng)絡(luò)請求
let manager = LLSessionManger.sharedManager
manager.request("https://httpbin.org/get").responseJSON { (response) in
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
}
注意:AFNetWorking、SDWebimage等第三方庫的修改和Alamofire類似,找到使用NSURLSessionConfiguration的地方,換成LLMarkerURLProtocol的defaultSessionConfiguration就可以了。
看到這你可能發(fā)現(xiàn),如果使用Alamofire進行網(wǎng)絡(luò)請求,我們還是修改了原來的代碼,下篇文章單獨介紹如何不修改原來代碼,通過注冊Alamofire通知方式,攔截Alamofire的網(wǎng)絡(luò)請求。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
- iOS在頁面銷毀時如何優(yōu)雅的cancel網(wǎng)絡(luò)請求詳解
- node.js通過axios實現(xiàn)網(wǎng)絡(luò)請求的方法
- iOS當多個網(wǎng)絡(luò)請求完成后執(zhí)行下一步的方法詳解
- iOS中多網(wǎng)絡(luò)請求的線程安全詳解
- IOS 網(wǎng)絡(luò)請求中設(shè)置cookie
- 詳解iOS中多個網(wǎng)絡(luò)請求的同步問題總結(jié)
- IOS開發(fā)中異步網(wǎng)絡(luò)請求上實現(xiàn)同步邏輯
- IOS網(wǎng)絡(luò)請求之NSURLSession使用詳解
- iOS判斷網(wǎng)絡(luò)請求超時的方法
- iOS開發(fā)中不合法的網(wǎng)絡(luò)請求地址如何解決
相關(guān)文章
IOS 播放系統(tǒng)提示音使用總結(jié)(AudioToolbox)
這篇文章主要介紹了IOS 播放系統(tǒng)提示音使用總結(jié)(AudioToolbox)的相關(guān)資料,需要的朋友可以參考下2017-05-05
Swift 2.1 為 UIView 添加點擊事件和點擊效果
本文主要介紹 Swift UIView,這里給大家提供代碼示例作為參考為UIView 添加點擊事件和點擊效果,希望能幫助IOS開發(fā)的同學2016-07-07

