【iOS】通过NSURLProtocol实现网页加载本地缓存数据

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dolacmeng/article/details/80681771

一.项目需求

项目中有个海报生成功能,使用UIWebView加载一些网页,因为海报使用率比较高,有时加载网页比较慢会影响用户体验,因此我们在APP启动后,将一些固定资源,如css、图片等,先缓存到本地。加载网页时,通过NSURLProtocol,优先使用本地的文件,以加快网页加载速度。

二.Demo:百度首页logo改为本地图片

下面demo实现,加载baidu首页,百度logo图片改用本地缓存好的图片代替。

(一)获取到所有请求

(1)新建JXURLProtocol类,继承自NSURLProtocol

@interface JXURLProtocol : NSURLProtocol

@end

(2)在JXURLProtocol.m中实现+ (BOOL)canInitWithRequest:方法,打印出所有请求:

+ (BOOL)canInitWithRequest:(NSURLRequest *)request{

    NSString *urlString = request.URL.absoluteString;
    NSLog(@"%@",urlString);

    return NO;
}

(3)在加载网页前,注册JXURLProtocol类:

[NSURLProtocol registerClass:[JXURLProtocol class]];

(二)拦截请求

(1)我们需要拦截百度图片url的请求,即在canInitWithRequest:方法中,根据url判断,如果是百度logo图的url(https://m.baidu.com/static/index/plus/plus_logo.png),返回YES,表示不发出请求而是自己处理:

+ (BOOL)canInitWithRequest:(NSURLRequest *)request{

    NSString *urlString = request.URL.absoluteString;
    NSLog(@"%@",urlString);

    //此处防止重复拦截(与后面的三(3)呼应)
    if ([NSURLProtocol propertyForKey:@"JXProtocol" inRequest:request]) {
        return NO;
    }

    if ([urlString isEqualToString:@"https://m.baidu.com/static/index/plus/plus_logo.png"]) {
        return YES;
    }
    return NO;
}

(2)返回YES后,会调用方法:

- (void)startLoading{}

(三)返回本地图片

(1)根据url返回本地图片:

- (NSData*)imageDataWithUrl:(NSURL*)url{
    if ([url.absoluteString isEqualToString:@"https://m.baidu.com/static/index/plus/plus_logo.png"]) {
        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"long" ofType:@"png"];
        NSData *data = [NSData dataWithContentsOfFile:filePath];
        return data;
    }
    return nil;
}

(2)尝试获取本地图片,获取成功后,构建响应头,回调请求成功。

- (void)startLoading{

    NSData *imageData = [self imageDataWithUrl:self.request.URL];

    if (imageData) {
        //获取本地图片成功

        //构建请求头
        NSString *mimeType = @"image/jpeg";

        NSMutableDictionary *header = [NSMutableDictionary dictionary];

        NSString *contentType = [mimeType stringByAppendingString:@";chartset=UTF-8"];
        header[@"Content-type"] = contentType;
        header[@"Content-Length"] = [NSString stringWithFormat:@"%ld",imageData.length];

        NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:self.request.URL
                                                                  statusCode:200 HTTPVersion:@"1.1" headerFields:header];
        //回调
        [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];
        [self.client URLProtocol:self didLoadData:imageData];
        [self.client URLProtocolDidFinishLoading:self];
    }else{
        //获取本地图片失败
    }
}

(3)如果获取本地图片失败,设置标志防止重复拦截,并重新构建请求:

- (void)startLoading{
    NSData *imageData = [self imageDataWithUrl:self.request.URL];
    if (imageData) {
        //获取本地图片成功
        //...
    }else{
        [NSURLProtocol setProperty:@(YES) forKey:@"JXProtocol" inRequest:self.request];
        NSMutableURLRequest *newRequset = [self.request mutableCopy];
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
        NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
        NSURLSessionTask *task = [session dataTaskWithRequest:newRequset];
        [task resume];
    }
}

(四)实现必须的方法:

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
    return request;
}

- (void)stopLoading{
}


#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.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.client URLProtocol:self didLoadData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    [self.client URLProtocolDidFinishLoading:self];
}

(五)运行APP,百度的logo已经换成本地的long.png,小恐龙了:

这里写图片描述

完整代码:https://github.com/dolacmeng/NSURLProtocolDemo

更多资料:https://developer.apple.com/documentation/foundation/url_loading_system

猜你喜欢

转载自blog.csdn.net/dolacmeng/article/details/80681771