UIWebView 有网直接请求服务器,无网使用缓存

本文介绍了一种在iOS应用中实现WebView缓存功能的方法。通过使用自定义的TURLSessionProtocol组件,在有网络的情况下直接请求服务器数据,在无网络时则从本地缓存中读取,确保了应用的流畅体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

项目需求: 页面需要有缓存功能,有网络的时候直接请求服务器,没有网络的时候从缓存获取。


   1.应用webviewcache组件

TURLSessionProtocol.h

TURLSessionProtocol.m

   2. 注册缓存组件

在Appdelegate中的application:didFinishLaunchingWithOptions:方法中加上:

[NSURLProtocol registerClass:[TURLSessionProtocol class]];

  3.webView加载url即可

NSString * webUrl=@"http://localhost:8080/html/";

        NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL URLWithString:webUrl]];

        [self.webView loadRequest:request];


以下是TURLSessionProtocol源码

 ==============TURLSessionProtocol.h===============

#import <Foundation/Foundation.h>


extern NSString *const KProtocolHttpHeadKey;


@interface TURLSessionProtocol :NSURLProtocol

//添加需要过滤的请求前缀

+ (NSSet *)filterUrlPres;

+ (void)setFilterUrlPres:(NSSet *)filterUrlPre;

@end


=============TURLSessionProtocol.m==============

#import "TURLSessionProtocol.h"

#import <objc/runtime.h>

#import <CommonCrypto/CommonDigest.h>

#import "AppDelegate.h"

#import "BWAFNetWorkingTools.h"

NSString *const KProtocolHttpHeadKey =@"KProtocolHttpHeadKey";


static NSUIntegerconst KCacheTime = 113529600000;//365*24*3600;//缓存的时间 默认设置为360可以任意的更改


static NSObject *TURLSessionFilterUrlPreObject;

static NSSet *TURLSessionFilterUrlPre;


@interface NSURLRequest(MutableCopyWorkaround)

- (id)mutableCopyWorkaround;

@end


@interface NSString (MD5)

- (NSString *)md5String;

@end


@interface TURLProtocolCacheData :NSObject<NSCoding>

@property (nonatomic,strong) NSDate *addDate;

@property (nonatomic,strong) NSData *data;

@property (nonatomic,strong) NSURLResponse *response;

@property (nonatomic,strong) NSURLRequest *redirectRequest;

@end



@interface TURLSessionProtocol ()<NSURLSessionDataDelegate>

@property (nonatomic,strong) NSURLSession *session;

@property (nonatomic,strong) NSURLSessionDataTask *downloadTask;

@property (nonatomic,strong) NSURLResponse *response;

@property (nonatomic,strong) NSMutableData *cacheData;

@end


@implementation NSURLRequest (MutableCopyWorkaround)


-(id)mutableCopyWorkaround {

    

    NSMutableURLRequest *mutableURLRequest = [[NSMutableURLRequestalloc] initWithURL:[selfURL]

                                                                         cachePolicy:[selfcachePolicy]

                                                                     timeoutInterval:[selftimeoutInterval]];

    [mutableURLRequest setAllHTTPHeaderFields:[selfallHTTPHeaderFields]];

    if ([selfHTTPBodyStream]) {

        [mutableURLRequest setHTTPBodyStream:[selfHTTPBodyStream]];

    } else {

        [mutableURLRequest setHTTPBody:[selfHTTPBody]];

    }

    [mutableURLRequest setHTTPMethod:[selfHTTPMethod]];

    

    return mutableURLRequest;

}


@end


@implementation NSString(MD5)


- (NSString *)md5String {

    

    NSData *data = [selfdataUsingEncoding:NSUTF8StringEncoding];

    uint8_t digest[CC_SHA1_DIGEST_LENGTH];

    

    CC_SHA1(data.bytes, data.length, digest);

    

    NSMutableString *output = [NSMutableStringstringWithCapacity:CC_SHA1_DIGEST_LENGTH *2];

    

    for (int i =0; i < CC_SHA1_DIGEST_LENGTH; i++) {

        [output appendFormat:@"%02x", digest[i]];

    }

    

    return output;

}


@end


@implementation TURLProtocolCacheData

- (void)encodeWithCoder:(NSCoder *)aCoder {

    

    unsignedint count;

    Ivar *ivar =class_copyIvarList([selfclass], &count);

    for (int i =0 ; i < count ; i++) {

        Ivar iv = ivar[i];

        constchar *name = ivar_getName(iv);

        NSString *strName = [NSStringstringWithUTF8String:name];

        //利用KVC取值

        id value = [selfvalueForKey:strName];

        [aCoder encodeObject:valueforKey:strName];

    }

    free(ivar);

}


- (instancetype)initWithCoder:(NSCoder *)aDecoder {

    self = [superinit];

    if (self !=nil) {

        unsignedint count = 0;

        Ivar *ivar =class_copyIvarList([selfclass], &count);

        for (int i=0 ;i < count ; i++) {

            Ivar var = ivar[i];

            constchar *keyName = ivar_getName(var);

            NSString *key = [NSStringstringWithUTF8String:keyName];

            id value = [aDecoderdecodeObjectForKey:key];

            [selfsetValue:value forKey:key];

        }

        free(ivar);

    }

    

    returnself;

}


@end


@implementation TURLSessionProtocol


+ (void)initialize

{

    if (self == [TURLSessionProtocolclass])

    {

        staticdispatch_once_t onceToken;

        dispatch_once(&onceToken, ^{

            TURLSessionFilterUrlPreObject = [[NSObjectalloc] init];

        });

        

        [selfsetFilterUrlPres:[NSSetsetWithObject:@"http://localhost:8081"]];

    }

}

- (NSURLSession *)session {

    

    if (!_session) {

        _session = [NSURLSessionsessionWithConfiguration:[NSURLSessionConfigurationdefaultSessionConfiguration] delegate:selfdelegateQueue:nil];

    }

    return_session;

}


#pragma mark - publicFunc

+ (NSSet *)filterUrlPres {

    

    NSSet *set;

    @synchronized(TURLSessionFilterUrlPreObject)

    {

        set = TURLSessionFilterUrlPre;

    }

    return set;

}


+ (void)setFilterUrlPres:(NSSet *)filterUrlPre {

    @synchronized(TURLSessionFilterUrlPreObject)

    {

        TURLSessionFilterUrlPre = filterUrlPre;

    }

}


#pragma mark - privateFunc


- (NSString *)p_filePathWithUrlString:(NSString *)urlString {

    

    NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask,YES) lastObject];

    NSString *fileName = [urlString md5String];

    return [cachesPath stringByAppendingPathComponent:fileName];

}


- (BOOL)p_isUseCahceWithCacheData:(TURLProtocolCacheData *)cacheData {

    

    if (cacheData ==nil) {

        returnNO;

    }

    

    NSTimeInterval timeInterval = [[NSDate date] timeIntervalSinceDate:cacheData.addDate];

    return timeInterval < KCacheTime;

}


+ (BOOL)p_isFilterWithUrlString:(NSString *)urlString {

    

    BOOL state =NO;

    for (NSString *strin TURLSessionFilterUrlPre) {

        

        if ([urlString hasPrefix:str]) {

            state = YES;

            break;

        }

    }

    return state;

}


#pragma mark - override


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

    

    if ([request valueForHTTPHeaderField:KProtocolHttpHeadKey] ==nil && ![self p_isFilterWithUrlString:request.URL.absoluteString]) {

        //拦截请求头中包含KProtocolHttpHeadKey的请求

        //        NSLog(@"request url:%@",request.URL.absoluteString);

        returnYES;

    }

    returnNO;

}


+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {

    

    return request;

}


+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b {

    

    return [super requestIsCacheEquivalent:a toRequest:b];

}


- (void)startLoading {

    NSString *url = self.request.URL.absoluteString;//请求的链接

    TURLProtocolCacheData *cacheData = [NSKeyedUnarchiver unarchiveObjectWithFile:[self p_filePathWithUrlString:url]];

    AppDelegate* app=(AppDelegate*)[[UIApplication sharedApplication] delegate];

//判断当前网络环境,有网的时候直接走服务器请求不用本地缓存

    if (app.currentNetWorkState==AFNetworkReachabilityStatusReachableViaWWAN||app.currentNetWorkState==AFNetworkReachabilityStatusReachableViaWiFi) {

        NSMutableURLRequest *request = [self.request mutableCopyWorkaround];

        request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;

        //        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.request.URL.absoluteString]];

        [request setValue:@"test" forHTTPHeaderField:KProtocolHttpHeadKey];

        self.downloadTask = [self.session dataTaskWithRequest:request];

        [self.downloadTask resume];

        return;

    }

    if ([self p_isUseCahceWithCacheData:cacheData]) {

        //有缓存并且缓存没过期

        

        if (cacheData.redirectRequest) {

            [self.client URLProtocol:self wasRedirectedToRequest:cacheData.redirectRequest redirectResponse:cacheData.response];

        } else if (cacheData.response){

            [self.client URLProtocol:self didReceiveResponse:cacheData.response cacheStoragePolicy:NSURLCacheStorageNotAllowed];

            [self.client URLProtocol:self didLoadData:cacheData.data];

            [self.client URLProtocolDidFinishLoading:self];

        }

        

        

    } else {

        NSMutableURLRequest *request = [self.request mutableCopyWorkaround];

        request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;

        //        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.request.URL.absoluteString]];

        [request setValue:@"test" forHTTPHeaderField:KProtocolHttpHeadKey];

        self.downloadTask = [self.session dataTaskWithRequest:request];

        [self.downloadTask resume];

        

    }

}


- (void)stopLoading {

    [self.downloadTask cancel];

    self.cacheData =nil;

    self.downloadTask =nil;

    self.response =nil;

    

    

}


#pragma mark - session delegate


- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler {

    

    //处理重定向问题

    if (response !=nil) {

        NSMutableURLRequest *redirectableRequest = [request mutableCopyWorkaround];

        TURLProtocolCacheData *cacheData = [[TURLProtocolCacheData alloc] init];

        cacheData.data = self.cacheData;

        cacheData.response = response;

        cacheData.redirectRequest = redirectableRequest;

        [NSKeyedArchiver archiveRootObject:cacheData toFile:[self p_filePathWithUrlString:request.URL.absoluteString]];

        

        [self.client URLProtocol:self wasRedirectedToRequest:redirectableRequest redirectResponse:response];

        completionHandler(request);

        

    } else {

        

        completionHandler(request);

    }

}


- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {

    

    [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];

    // 允许处理服务器的响应,才会继续接收服务器返回的数据

    completionHandler(NSURLSessionResponseAllow);

    self.cacheData = [NSMutableData data];

    self.response = response;

}


-  (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {

    //下载过程中

    [self.client URLProtocol:self didLoadData:data];

    [self.cacheData appendData:data];

    

}


- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {

    //    下载完成之后的处理

   

    if (error) {

        NSLog(@"error url = %@",task.currentRequest.URL.absoluteString);

        [self.client URLProtocol:self didFailWithError:error];

    } else {

        //将数据的缓存归档存入到本地文件中

        NSLog(@"ok url = %@",task.currentRequest.URL.absoluteString);

        TURLProtocolCacheData *cacheData = [[TURLProtocolCacheData alloc] init];

        cacheData.data = [self.cacheData copy];

        cacheData.addDate = [NSDate date];

        cacheData.response = self.response;

        [NSKeyedArchiver archiveRootObject:cacheData toFile:[self p_filePathWithUrlString:self.request.URL.absoluteString]];

        [self.clientURLProtocolDidFinishLoading:self];

    }

}


@end



            




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值