使用NSURLProtocol实现离线缓存
一、說明:
NSURLProtocol可以攔截任何網絡請求,包含UIWebView中發出的所有請求。但是在WKWebView中,只能攔截到最初始的請求,內嵌的資源下載攔截不到。比如通過WKWebView加載"http://www.baidu.com",則只能攔截到"http://www.baidu.com",網頁內部的資源加載攔截不到。頁面跳轉屬于最初始請求之內,可以攔截到。
二、創建NSURLProtocol的子類,通過下面的代碼注冊此協議類:
[NSURLProtocol registerClass:[MyURLProtocol class]];
三、下面是此子類的代碼:
#import "MyURLProtocol.h"
#define MyURLProtocolHandled @"MyURLProtocolHandled"
//創建archive數據模型,重寫編碼解碼協議
@interface MyCacheData : NSObject
@property(nonatomic,strong) NSURLRequest *request;
@property(nonatomic,strong) NSURLResponse *response;
@property(nonatomic,strong) NSData *data;
@end
@interface NSURLRequest (MutableCopyWorkaround)
- (id)mutableCopyWorkaround;
@end
@interface MyURLProtocol ()
@property(nonatomic,strong) NSURLConnection *connection;
@property(nonatomic,strong) NSMutableData *httpData;
@property(nonatomic,strong) NSURLResponse *response;
@end
@implementation MyURLProtocol
#pragma mark - 重寫NSURLProtocol子類方法
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
? ? //如果此請求是攔截到請求之后,接管請求而發起的新請求,則不處理。
? ? if ([request.URL.scheme isEqualToString:@"http"] &&
? ? ? ? [request valueForHTTPHeaderField:MyURLProtocolHandled] == nil)
? ? {
? ? ? ? return YES;
? ? }
? ? return NO;
}
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
? ? return request;
}
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a
?? ? ? ? ? ? ? ? ? ? ? toRequest:(NSURLRequest *)b
{
? ? return [super requestIsCacheEquivalent:a toRequest:b];
}
- (void)startLoading
{
? ? //如果發現已經存在此請求的緩存數據,則返回緩存數據,否則發起新的請求從服務求加載數據
? ? MyCacheData *cacheData = [NSKeyedUnarchiver unarchiveObjectWithFile:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [self cachePathForRequest:self.request]];
? ? if(cacheData != nil)
? ? {
? ? ? ? NSData *data = cacheData.data;
? ? ? ? NSURLResponse *response = cacheData.response;
? ? ? ? NSURLRequest *redirectRequest = cacheData.request;
?? ? ? ?
? ? ? ? //使用NSURLProtocolClient做請求轉向,直接將請求和數據轉發到之前的請求
? ? ? ? if(redirectRequest != nil)
? ? ? ? {
? ? ? ? ? ? [[self client] URLProtocol:self wasRedirectedToRequest:redirectRequest
? ? ? ? ? ? ? ? ? ? ? redirectResponse:response];
? ? ? ? }
? ? ? ? else
? ? ? ? {
? ? ? ? ? ? [[self client] URLProtocol:self didReceiveResponse:response
? ? ? ? ? ? ? ? ? ? cacheStoragePolicy:NSURLCacheStorageNotAllowed];
? ? ? ? ? ? [[self client] URLProtocol:self didLoadData:data];
? ? ? ? ? ? [[self client] URLProtocolDidFinishLoading:self];
? ? ? ? }
? ? }
? ? else
? ? {
//接管此網絡請求,發起一個新的請求,后續會將新請求拿到的數據交給之前的舊請求
? ? ? ? NSMutableURLRequest *connectionRequest = [[self request] mutableCopyWorkaround];
? ? ? ? //增加標記,標示是由我們接管而發出的請求
? ? ? ? [connectionRequest setValue:@"Tag" forHTTPHeaderField:MyURLProtocolHandled];
? ? ? ? self.connection = [NSURLConnection connectionWithRequest:connectionRequest
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? delegate:self];
? ? }
}
- (void)stopLoading
{
? ? [self.connection cancel];
? ? self.connection = nil;
}
#pragma mark - 網絡請求代理
- (NSURLRequest *)connection:(NSURLConnection *)connection
?? ? ? ? ? ? willSendRequest:(NSURLRequest *)request
? ? ? ? ? ? redirectResponse:(NSURLResponse *)response
{
? ? if(response != nil)
? ? {
? ? ? ? NSMutableURLRequest *redirectableRequest = [request mutableCopyWorkaround];
?? ? ? ?
? ? ? ? //緩存數據
? ? ? ? MyCacheData *cacheData = [MyCacheData new];
? ? ? ? [cacheData setData:self.httpData];
? ? ? ? [cacheData setResponse:response];
? ? ? ? [cacheData setRequest:redirectableRequest];
?? ? ? ?
? ? ? ? [NSKeyedArchiver archiveRootObject:cacheData
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? toFile:[self cachePathForRequest:[self request]]];
?? ? ? ?
? ? ? ? //將請求和緩存的響應數據轉向到之前的請求
? ? ? ? [[self client] URLProtocol:self wasRedirectedToRequest:redirectableRequest
? ? ? ? ? ? ? ? ? redirectResponse:response];
? ? ? ? return redirectableRequest ;
? ? }
? ? return request;
}
- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
{
? ? return YES;
}
- (void)connection:(NSURLConnection *)connection
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
? ? [self.client URLProtocol:self didReceiveAuthenticationChallenge:challenge];
}
- (void)connection:(NSURLConnection *)connection
didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
? ? [self.client URLProtocol:self didCancelAuthenticationChallenge:challenge];
}
- (void)connection:(NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response
{
? ? //保存響應對象
? ? self.response = response;
? ? [self.client URLProtocol:self didReceiveResponse:response
? ? ? ? ? cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}
- (void)connection:(NSURLConnection *)connection
? ? didReceiveData:(NSData *)data
{
? ? [self.client URLProtocol:self didLoadData:data];
?? ?
? ? //保存服務器返回的數據
? ? if(self.httpData == nil) {
? ? ? ? self.httpData = [NSMutableData dataWithData: data];
? ? }
? ? else
? ? {
? ? ? ? [self.httpData appendData:data];
? ? }
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
? ? ? ? ? ? ? ? ? willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
? ? return cachedResponse;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
? ? [self.client URLProtocolDidFinishLoading:self];
?? ?
? ? //請求加載完畢之后,將數據緩存
? ? MyCacheData *cacheData = [MyCacheData new];
? ? [cacheData setData:self.httpData];
? ? [cacheData setResponse:self.response];
? ? [NSKeyedArchiver archiveRootObject:cacheData
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? toFile:[self cachePathForRequest:self.request]];
?? ?
? ? self.connection = nil;
? ? self.httpData = nil;
? ? self.response = nil;
}
- (void)connection:(NSURLConnection *)connection
? didFailWithError:(NSError *)error
{
? ? [self.client URLProtocol:self didFailWithError:error];
?? ?
? ? self.connection = nil;
? ? self.httpData = nil;
? ? self.response = nil;
}
#pragma mark - 為請求創建緩存路徑
- (NSString *)cachePathForRequest:(NSURLRequest *)aRequest
{
? ? NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? NSUserDomainMask, YES) lastObject];
? ? return [cachesPath stringByAppendingPathComponent:
? ? ? ? ? ? [NSString stringWithFormat:@"%ld", [[[aRequest URL] absoluteString] hash]]];
}
@end
@implementation NSURLRequest (MutableCopyWorkaround)
- (id) mutableCopyWorkaround {
? ? NSMutableURLRequest *mutableURLRequest = [[NSMutableURLRequest alloc]
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? initWithURL:[self URL]
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cachePolicy:[self cachePolicy]
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? timeoutInterval:[self timeoutInterval]];
? ? [mutableURLRequest setAllHTTPHeaderFields:[self allHTTPHeaderFields]];
? ? return mutableURLRequest;
}
@end
@implementation MyCacheData
-(id) initWithCoder:(NSCoder *) aDecoder
{
? ? self = [super init];
? ? if(!self) {
? ? ? ? return nil;
? ? }
? ? [self setData:[aDecoder decodeObjectForKey:@"data"]];
? ? [self setRequest:[aDecoder decodeObjectForKey:@"request"]];
? ? [self setResponse:[aDecoder decodeObjectForKey:@"response"]];
?? ?
? ? return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
? ? [aCoder encodeObject:[self data] forKey:@"data"];
? ? [aCoder encodeObject:[self request] forKey:@"request"];
? ? [aCoder encodeObject:[self response] forKey:@"response"];
}
@end
部分代碼轉自:http://tanlimin201.blog.163.com/blog/static/38171407201383032914736/
總結
以上是生活随笔為你收集整理的使用NSURLProtocol实现离线缓存的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PBOC3.0中使用的国密SM2算法
- 下一篇: spring boot controll