├── .DS_Store
├── NSURLSession-NSURLProtocol-WebviewCache.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── project.pbxproj
├── NSURLSession-NSURLProtocol-WebviewCache
├── ViewController.h
├── AppDelegate.h
├── TURLSessionProtocol.h
├── main.m
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── ViewController.m
├── Info.plist
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── AppDelegate.m
└── TURLSessionProtocol.m
├── .gitignore
└── README.md
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhangli4659507/NSURLSession-NSURLProtocol-webviewCache/HEAD/.DS_Store
--------------------------------------------------------------------------------
/NSURLSession-NSURLProtocol-WebviewCache.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/NSURLSession-NSURLProtocol-WebviewCache/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // NSURLSession-NSURLProtocol-WebviewCache
4 | //
5 | // Created by Mark on 16/5/20.
6 | // Copyright © 2016年 Mark. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/NSURLSession-NSURLProtocol-WebviewCache/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // NSURLSession-NSURLProtocol-WebviewCache
4 | //
5 | // Created by Mark on 16/5/20.
6 | // Copyright © 2016年 Mark. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/NSURLSession-NSURLProtocol-WebviewCache/TURLSessionProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // TURLSessionProtocol.h
3 | // CachedWebView
4 | //
5 | // Created by Mark on 16/5/19.
6 | //
7 | //
8 |
9 | #import
10 |
11 | extern NSString *const KProtocolHttpHeadKey;
12 |
13 | @interface TURLSessionProtocol : NSURLProtocol
14 | //添加需要过滤的请求前缀
15 | + (NSSet *)filterUrlPres;
16 | + (void)setFilterUrlPres:(NSSet *)filterUrlPre;
17 | @end
18 |
--------------------------------------------------------------------------------
/NSURLSession-NSURLProtocol-WebviewCache/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // NSURLSession-NSURLProtocol-WebviewCache
4 | //
5 | // Created by Mark on 16/5/20.
6 | // Copyright © 2016年 Mark. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/NSURLSession-NSURLProtocol-WebviewCache/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/NSURLSession-NSURLProtocol-WebviewCache/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // NSURLSession-NSURLProtocol-WebviewCache
4 | //
5 | // Created by Mark on 16/5/20.
6 | // Copyright © 2016年 Mark. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import "TURLSessionProtocol.h"
11 | @interface ViewController ()
12 | @property (nonatomic, strong) NSMutableData *cacheData;
13 | @property (weak, nonatomic) IBOutlet UIWebView *webView;
14 | @end
15 |
16 | @implementation ViewController
17 |
18 | - (void)viewDidLoad {
19 | [super viewDidLoad];
20 |
21 | // Do any additional setup after loading the view, typically from a nib.
22 | [self actionRefrash:nil];
23 |
24 |
25 | }
26 |
27 | - (IBAction)actionRefrash:(id)sender {
28 |
29 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.233.com"]];
30 |
31 | [self.webView loadRequest:request];
32 |
33 | self.webView.scalesPageToFit = YES;
34 | }
35 |
36 | - (void)didReceiveMemoryWarning {
37 | [super didReceiveMemoryWarning];
38 | // Dispose of any resources that can be recreated.
39 | }
40 |
41 | @end
42 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata
19 |
20 | ## Other
21 | *.xccheckout
22 | *.moved-aside
23 | *.xcuserstate
24 | *.xcscmblueprint
25 |
26 | ## Obj-C/Swift specific
27 | *.hmap
28 | *.ipa
29 |
30 | # CocoaPods
31 | #
32 | # We recommend against adding the Pods directory to your .gitignore. However
33 | # you should judge for yourself, the pros and cons are mentioned at:
34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
35 | #
36 | # Pods/
37 |
38 | # Carthage
39 | #
40 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
41 | # Carthage/Checkouts
42 |
43 | Carthage/Build
44 |
45 | # fastlane
46 | #
47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
48 | # screenshots whenever they are needed.
49 | # For more information about the recommended setup visit:
50 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md
51 |
52 | fastlane/report.xml
53 | fastlane/screenshots
54 |
--------------------------------------------------------------------------------
/NSURLSession-NSURLProtocol-WebviewCache/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | NSAppTransportSecurity
26 |
27 | NSAllowsArbitraryLoads
28 |
29 |
30 | UILaunchStoryboardName
31 | LaunchScreen
32 | UIMainStoryboardFile
33 | Main
34 | UIRequiredDeviceCapabilities
35 |
36 | armv7
37 |
38 | UISupportedInterfaceOrientations
39 |
40 | UIInterfaceOrientationPortrait
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NSURLSession-NSURLProtocol-webviewCache
2 |
3 | ##产生缘由
4 | 其实项目已经上线了一年多了,一直没时间来将一些有用的东西摘出来。最近还算是比较闲,就将一些自认为值得拿出来分享的东西摘了出来。
5 |
6 | ##与其他公共类比较
7 | 之前在GitHub上star最多的莫过于[RNCachingURLProtocol](https://github.com/rnapier/RNCachingURLProtocol),我也认真的看了他的源码,写的非常不错,不的不说有点过时了。而且有一点没处理的好,如果放入直接的将其使用,他会将所有的http请求都缓存下来,不管你是不是来自UIWebView的请求,所以有很大的必要去改进。
8 |
9 | ##与NSURLCache比较
10 | 相信对使用过UIWebView的同学都会知道,其实UIWebView本身是有缓存的,那就是基于NSURLCache来实现的。之前到网上看到过基于他来对NSURLCache实现离线缓存,我开始也封装了个,但是发现不是特别的好用,显得特别的笨重。后面看了一篇关于介绍[NSURLProtocol](https://www.raywenderlich.com/59982/nsurlprotocol-tutorial)的文章,英文还行的同学可以去看看。
11 |
12 | 另外,之前的请求是基于NSURLConnection来封装的,但是现在看到苹果最近发的通告,所以就改进成了NSURLSession。
13 |
14 | ##使用方法
15 | 1.在Appdelegate中的`application:didFinishLaunchingWithOptions:`方法中加上:
16 | `[NSURLProtocol registerClass:[TURLSessionProtocol class]];`
17 |
18 | `注:在使用NSURLSession做请求,必须这样写才能截获该请求:`
19 |
20 | ` NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];`
21 |
22 | ` config.protocolClasses = @[[TURLSessionProtocol class]];`
23 |
24 | 对于拦截NSURLSession请求,可以参考这篇博客[objc](http://objccn.io/issue-5-4/)
25 |
26 |
27 | 2.在你创建请求时,给请求添加一个请求头:
28 | `[request setValue:@(YES) forHTTPHeaderField:KProtocolHttpHeadKey];`需要注意key必须是`KProtocolHttpHeadKey`,不然会拦截不到你的请求。
29 |
30 | 这样就大功告成。
31 |
32 | ###备注
33 | 如果需要修改缓存时间,请在`TURLSessionProtocol.m`文件中去将`static NSUInteger const KCacheTime = 30;`修改为你需要缓存时长的时间,单位是秒。
34 |
35 | 另外就是,最终的缓存内容是以归档的方式存入到沙盒的`cache`目录下,要是清空缓存也会将缓存的内容删除,如果不想一起删除,可以自定义目录。
36 |
37 | ##联系方式
38 | 如果你在使用过程中遇到任何的问题或者说有Bug和好的建议,请将你的需求发到我的Email:
39 |
40 | 欢迎交流。
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/NSURLSession-NSURLProtocol-WebviewCache/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/NSURLSession-NSURLProtocol-WebviewCache/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // NSURLSession-NSURLProtocol-WebviewCache
4 | //
5 | // Created by Mark on 16/5/20.
6 | // Copyright © 2016年 Mark. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import "TURLSessionProtocol.h"
11 | @interface AppDelegate ()
12 |
13 | @end
14 |
15 | @implementation AppDelegate
16 |
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
19 | [NSURLProtocol registerClass:[TURLSessionProtocol class]];
20 | // Override point for customization after application launch.
21 | return YES;
22 | }
23 |
24 | - (void)applicationWillResignActive:(UIApplication *)application {
25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
26 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
27 | }
28 |
29 | - (void)applicationDidEnterBackground:(UIApplication *)application {
30 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
31 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
32 | }
33 |
34 | - (void)applicationWillEnterForeground:(UIApplication *)application {
35 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
36 | }
37 |
38 | - (void)applicationDidBecomeActive:(UIApplication *)application {
39 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
40 | }
41 |
42 | - (void)applicationWillTerminate:(UIApplication *)application {
43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
44 | }
45 |
46 | @end
47 |
--------------------------------------------------------------------------------
/NSURLSession-NSURLProtocol-WebviewCache/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/NSURLSession-NSURLProtocol-WebviewCache/TURLSessionProtocol.m:
--------------------------------------------------------------------------------
1 | //
2 | // TURLSessionProtocol.m
3 | // CachedWebView
4 | //
5 | // Created by Mark on 16/5/19.
6 | //
7 | //
8 |
9 | #import "TURLSessionProtocol.h"
10 | #import
11 | #import
12 | NSString *const KProtocolHttpHeadKey = @"KProtocolHttpHeadKey";
13 |
14 | static NSUInteger const KCacheTime = 360;//缓存的时间 默认设置为360秒 可以任意的更改
15 |
16 | static NSObject *TURLSessionFilterUrlPreObject;
17 | static NSSet *TURLSessionFilterUrlPre;
18 |
19 | @interface NSURLRequest(MutableCopyWorkaround)
20 | - (id)mutableCopyWorkaround;
21 | @end
22 |
23 | @interface NSString (MD5)
24 | - (NSString *)md5String;
25 | @end
26 |
27 | @interface TURLProtocolCacheData : NSObject
28 | @property (nonatomic, strong) NSDate *addDate;
29 | @property (nonatomic, strong) NSData *data;
30 | @property (nonatomic, strong) NSURLResponse *response;
31 | @property (nonatomic, strong) NSURLRequest *redirectRequest;
32 | @end
33 |
34 |
35 | @interface TURLSessionProtocol ()
36 | @property (nonatomic, strong) NSURLSession *session;
37 | @property (nonatomic, strong) NSURLSessionDataTask *downloadTask;
38 | @property (nonatomic, strong) NSURLResponse *response;
39 | @property (nonatomic, strong) NSMutableData *cacheData;
40 | @end
41 |
42 | @implementation NSURLRequest (MutableCopyWorkaround)
43 |
44 | -(id)mutableCopyWorkaround {
45 |
46 | NSMutableURLRequest *mutableURLRequest = [[NSMutableURLRequest alloc] initWithURL:[self URL]
47 | cachePolicy:[self cachePolicy]
48 | timeoutInterval:[self timeoutInterval]];
49 | [mutableURLRequest setAllHTTPHeaderFields:[self allHTTPHeaderFields]];
50 | if ([self HTTPBodyStream]) {
51 | [mutableURLRequest setHTTPBodyStream:[self HTTPBodyStream]];
52 | } else {
53 | [mutableURLRequest setHTTPBody:[self HTTPBody]];
54 | }
55 | [mutableURLRequest setHTTPMethod:[self HTTPMethod]];
56 |
57 | return mutableURLRequest;
58 | }
59 |
60 | @end
61 |
62 | @implementation NSString(MD5)
63 |
64 | - (NSString *)md5String {
65 |
66 | NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
67 | uint8_t digest[CC_SHA1_DIGEST_LENGTH];
68 |
69 | CC_SHA1(data.bytes, data.length, digest);
70 |
71 | NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
72 |
73 | for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) {
74 | [output appendFormat:@"%02x", digest[i]];
75 | }
76 |
77 | return output;
78 | }
79 |
80 | @end
81 |
82 | @implementation TURLProtocolCacheData
83 | - (void)encodeWithCoder:(NSCoder *)aCoder {
84 |
85 | unsigned int count;
86 | Ivar *ivar = class_copyIvarList([self class], &count);
87 | for (int i = 0 ; i < count ; i++) {
88 | Ivar iv = ivar[i];
89 | const char *name = ivar_getName(iv);
90 | NSString *strName = [NSString stringWithUTF8String:name];
91 | //利用KVC取值
92 | id value = [self valueForKey:strName];
93 | [aCoder encodeObject:value forKey:strName];
94 | }
95 | free(ivar);
96 | }
97 |
98 | - (instancetype)initWithCoder:(NSCoder *)aDecoder {
99 | self = [super init];
100 | if (self != nil) {
101 | unsigned int count = 0;
102 | Ivar *ivar = class_copyIvarList([self class], &count);
103 | for (int i= 0 ;i < count ; i++) {
104 | Ivar var = ivar[i];
105 | const char *keyName = ivar_getName(var);
106 | NSString *key = [NSString stringWithUTF8String:keyName];
107 | id value = [aDecoder decodeObjectForKey:key];
108 | [self setValue:value forKey:key];
109 | }
110 | free(ivar);
111 | }
112 |
113 | return self;
114 | }
115 |
116 | @end
117 |
118 | @implementation TURLSessionProtocol
119 |
120 | + (void)initialize
121 | {
122 | if (self == [TURLSessionProtocol class])
123 | {
124 | static dispatch_once_t onceToken;
125 | dispatch_once(&onceToken, ^{
126 | TURLSessionFilterUrlPreObject = [[NSObject alloc] init];
127 | });
128 |
129 | [self setFilterUrlPres:[NSSet setWithObject:@"http://api.233.com"]];
130 | }
131 | }
132 | - (NSURLSession *)session {
133 |
134 | if (!_session) {
135 | _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
136 | }
137 | return _session;
138 | }
139 |
140 | #pragma mark - publicFunc
141 | + (NSSet *)filterUrlPres {
142 |
143 | NSSet *set;
144 | @synchronized(TURLSessionFilterUrlPreObject)
145 | {
146 | set = TURLSessionFilterUrlPre;
147 | }
148 | return set;
149 | }
150 |
151 | + (void)setFilterUrlPres:(NSSet *)filterUrlPre {
152 | @synchronized(TURLSessionFilterUrlPreObject)
153 | {
154 | TURLSessionFilterUrlPre = filterUrlPre;
155 | }
156 | }
157 |
158 | #pragma mark - privateFunc
159 |
160 | - (NSString *)p_filePathWithUrlString:(NSString *)urlString {
161 |
162 | NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
163 | NSString *fileName = [urlString md5String];
164 | return [cachesPath stringByAppendingPathComponent:fileName];
165 | }
166 |
167 | - (BOOL)p_isUseCahceWithCacheData:(TURLProtocolCacheData *)cacheData {
168 |
169 | if (cacheData == nil) {
170 | return NO;
171 | }
172 |
173 | NSTimeInterval timeInterval = [[NSDate date] timeIntervalSinceDate:cacheData.addDate];
174 | return timeInterval < KCacheTime;
175 | }
176 |
177 | + (BOOL)p_isFilterWithUrlString:(NSString *)urlString {
178 |
179 | BOOL state = NO;
180 | for (NSString *str in TURLSessionFilterUrlPre) {
181 |
182 | if ([urlString hasPrefix:str]) {
183 | state = YES;
184 | break;
185 | }
186 | }
187 | return state;
188 | }
189 |
190 | #pragma mark - override
191 |
192 | +(BOOL)canInitWithRequest:(NSURLRequest *)request {
193 |
194 | if ([request valueForHTTPHeaderField:KProtocolHttpHeadKey] == nil && ![self p_isFilterWithUrlString:request.URL.absoluteString]) {
195 | //拦截请求头中包含KProtocolHttpHeadKey的请求
196 | // NSLog(@"request url:%@",request.URL.absoluteString);
197 | return YES;
198 | }
199 | return NO;
200 | }
201 |
202 | + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
203 |
204 | return request;
205 | }
206 |
207 | + (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b {
208 |
209 | return [super requestIsCacheEquivalent:a toRequest:b];
210 | }
211 |
212 | - (void)startLoading {
213 |
214 | NSString *url = self.request.URL.absoluteString;//请求的链接
215 | TURLProtocolCacheData *cacheData = [NSKeyedUnarchiver unarchiveObjectWithFile:[self p_filePathWithUrlString:url]];
216 |
217 | if ([self p_isUseCahceWithCacheData:cacheData]) {
218 | //有缓存并且缓存没过期
219 |
220 | if (cacheData.redirectRequest) {
221 | [self.client URLProtocol:self wasRedirectedToRequest:cacheData.redirectRequest redirectResponse:cacheData.response];
222 | } else if (cacheData.response){
223 | [self.client URLProtocol:self didReceiveResponse:cacheData.response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
224 | [self.client URLProtocol:self didLoadData:cacheData.data];
225 | [self.client URLProtocolDidFinishLoading:self];
226 | }
227 |
228 |
229 | } else {
230 | NSMutableURLRequest *request = [self.request mutableCopyWorkaround];
231 | request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
232 | // NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.request.URL.absoluteString]];
233 | [request setValue:@"test" forHTTPHeaderField:KProtocolHttpHeadKey];
234 | self.downloadTask = [self.session dataTaskWithRequest:request];
235 | [self.downloadTask resume];
236 |
237 | }
238 | }
239 |
240 | - (void)stopLoading {
241 | [self.downloadTask cancel];
242 | self.cacheData = nil;
243 | self.downloadTask = nil;
244 | self.response = nil;
245 |
246 |
247 | }
248 |
249 | #pragma mark - session delegate
250 |
251 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler {
252 |
253 | //处理重定向问题
254 | if (response != nil) {
255 | NSMutableURLRequest *redirectableRequest = [request mutableCopyWorkaround];
256 | TURLProtocolCacheData *cacheData = [[TURLProtocolCacheData alloc] init];
257 | cacheData.data = self.cacheData;
258 | cacheData.response = response;
259 | cacheData.redirectRequest = redirectableRequest;
260 | [NSKeyedArchiver archiveRootObject:cacheData toFile:[self p_filePathWithUrlString:request.URL.absoluteString]];
261 |
262 | [self.client URLProtocol:self wasRedirectedToRequest:redirectableRequest redirectResponse:response];
263 | completionHandler(request);
264 |
265 | } else {
266 |
267 | completionHandler(request);
268 | }
269 | }
270 |
271 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
272 |
273 | [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
274 | // 允许处理服务器的响应,才会继续接收服务器返回的数据
275 | completionHandler(NSURLSessionResponseAllow);
276 | self.cacheData = [NSMutableData data];
277 | self.response = response;
278 | }
279 |
280 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
281 | //下载过程中
282 | [self.client URLProtocol:self didLoadData:data];
283 | [self.cacheData appendData:data];
284 |
285 | }
286 |
287 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
288 | // 下载完成之后的处理
289 |
290 | if (error) {
291 | NSLog(@"error url = %@",task.currentRequest.URL.absoluteString);
292 | [self.client URLProtocol:self didFailWithError:error];
293 | } else {
294 | //将数据的缓存归档存入到本地文件中
295 | NSLog(@"ok url = %@",task.currentRequest.URL.absoluteString);
296 | TURLProtocolCacheData *cacheData = [[TURLProtocolCacheData alloc] init];
297 | cacheData.data = [self.cacheData copy];
298 | cacheData.addDate = [NSDate date];
299 | cacheData.response = self.response;
300 | [NSKeyedArchiver archiveRootObject:cacheData toFile:[self p_filePathWithUrlString:self.request.URL.absoluteString]];
301 | [self.client URLProtocolDidFinishLoading:self];
302 | }
303 | }
304 |
305 | @end
306 |
--------------------------------------------------------------------------------
/NSURLSession-NSURLProtocol-WebviewCache.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 4DD4CB231CEEADCE00C6364F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DD4CB221CEEADCE00C6364F /* main.m */; };
11 | 4DD4CB261CEEADCE00C6364F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DD4CB251CEEADCE00C6364F /* AppDelegate.m */; };
12 | 4DD4CB291CEEADCE00C6364F /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DD4CB281CEEADCE00C6364F /* ViewController.m */; };
13 | 4DD4CB2C1CEEADCE00C6364F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4DD4CB2A1CEEADCE00C6364F /* Main.storyboard */; };
14 | 4DD4CB2E1CEEADCE00C6364F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4DD4CB2D1CEEADCE00C6364F /* Assets.xcassets */; };
15 | 4DD4CB311CEEADCE00C6364F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4DD4CB2F1CEEADCE00C6364F /* LaunchScreen.storyboard */; };
16 | 4DD4CB3A1CEEAE6A00C6364F /* TURLSessionProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DD4CB391CEEAE6A00C6364F /* TURLSessionProtocol.m */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXFileReference section */
20 | 4DD4CB1E1CEEADCE00C6364F /* NSURLSession-NSURLProtocol-WebviewCache.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "NSURLSession-NSURLProtocol-WebviewCache.app"; sourceTree = BUILT_PRODUCTS_DIR; };
21 | 4DD4CB221CEEADCE00C6364F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
22 | 4DD4CB241CEEADCE00C6364F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
23 | 4DD4CB251CEEADCE00C6364F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
24 | 4DD4CB271CEEADCE00C6364F /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
25 | 4DD4CB281CEEADCE00C6364F /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
26 | 4DD4CB2B1CEEADCE00C6364F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
27 | 4DD4CB2D1CEEADCE00C6364F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
28 | 4DD4CB301CEEADCE00C6364F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
29 | 4DD4CB321CEEADCE00C6364F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
30 | 4DD4CB381CEEAE6A00C6364F /* TURLSessionProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TURLSessionProtocol.h; sourceTree = ""; };
31 | 4DD4CB391CEEAE6A00C6364F /* TURLSessionProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TURLSessionProtocol.m; sourceTree = ""; };
32 | /* End PBXFileReference section */
33 |
34 | /* Begin PBXFrameworksBuildPhase section */
35 | 4DD4CB1B1CEEADCE00C6364F /* Frameworks */ = {
36 | isa = PBXFrameworksBuildPhase;
37 | buildActionMask = 2147483647;
38 | files = (
39 | );
40 | runOnlyForDeploymentPostprocessing = 0;
41 | };
42 | /* End PBXFrameworksBuildPhase section */
43 |
44 | /* Begin PBXGroup section */
45 | 4DD4CB151CEEADCE00C6364F = {
46 | isa = PBXGroup;
47 | children = (
48 | 4DD4CB201CEEADCE00C6364F /* NSURLSession-NSURLProtocol-WebviewCache */,
49 | 4DD4CB1F1CEEADCE00C6364F /* Products */,
50 | );
51 | sourceTree = "";
52 | };
53 | 4DD4CB1F1CEEADCE00C6364F /* Products */ = {
54 | isa = PBXGroup;
55 | children = (
56 | 4DD4CB1E1CEEADCE00C6364F /* NSURLSession-NSURLProtocol-WebviewCache.app */,
57 | );
58 | name = Products;
59 | sourceTree = "";
60 | };
61 | 4DD4CB201CEEADCE00C6364F /* NSURLSession-NSURLProtocol-WebviewCache */ = {
62 | isa = PBXGroup;
63 | children = (
64 | 4DD4CB241CEEADCE00C6364F /* AppDelegate.h */,
65 | 4DD4CB251CEEADCE00C6364F /* AppDelegate.m */,
66 | 4DD4CB271CEEADCE00C6364F /* ViewController.h */,
67 | 4DD4CB281CEEADCE00C6364F /* ViewController.m */,
68 | 4DD4CB2A1CEEADCE00C6364F /* Main.storyboard */,
69 | 4DD4CB381CEEAE6A00C6364F /* TURLSessionProtocol.h */,
70 | 4DD4CB391CEEAE6A00C6364F /* TURLSessionProtocol.m */,
71 | 4DD4CB2D1CEEADCE00C6364F /* Assets.xcassets */,
72 | 4DD4CB2F1CEEADCE00C6364F /* LaunchScreen.storyboard */,
73 | 4DD4CB321CEEADCE00C6364F /* Info.plist */,
74 | 4DD4CB211CEEADCE00C6364F /* Supporting Files */,
75 | );
76 | path = "NSURLSession-NSURLProtocol-WebviewCache";
77 | sourceTree = "";
78 | };
79 | 4DD4CB211CEEADCE00C6364F /* Supporting Files */ = {
80 | isa = PBXGroup;
81 | children = (
82 | 4DD4CB221CEEADCE00C6364F /* main.m */,
83 | );
84 | name = "Supporting Files";
85 | sourceTree = "";
86 | };
87 | /* End PBXGroup section */
88 |
89 | /* Begin PBXNativeTarget section */
90 | 4DD4CB1D1CEEADCE00C6364F /* NSURLSession-NSURLProtocol-WebviewCache */ = {
91 | isa = PBXNativeTarget;
92 | buildConfigurationList = 4DD4CB351CEEADCE00C6364F /* Build configuration list for PBXNativeTarget "NSURLSession-NSURLProtocol-WebviewCache" */;
93 | buildPhases = (
94 | 4DD4CB1A1CEEADCE00C6364F /* Sources */,
95 | 4DD4CB1B1CEEADCE00C6364F /* Frameworks */,
96 | 4DD4CB1C1CEEADCE00C6364F /* Resources */,
97 | );
98 | buildRules = (
99 | );
100 | dependencies = (
101 | );
102 | name = "NSURLSession-NSURLProtocol-WebviewCache";
103 | productName = "NSURLSession-NSURLProtocol-WebviewCache";
104 | productReference = 4DD4CB1E1CEEADCE00C6364F /* NSURLSession-NSURLProtocol-WebviewCache.app */;
105 | productType = "com.apple.product-type.application";
106 | };
107 | /* End PBXNativeTarget section */
108 |
109 | /* Begin PBXProject section */
110 | 4DD4CB161CEEADCE00C6364F /* Project object */ = {
111 | isa = PBXProject;
112 | attributes = {
113 | LastUpgradeCheck = 0730;
114 | ORGANIZATIONNAME = Mark;
115 | TargetAttributes = {
116 | 4DD4CB1D1CEEADCE00C6364F = {
117 | CreatedOnToolsVersion = 7.3.1;
118 | };
119 | };
120 | };
121 | buildConfigurationList = 4DD4CB191CEEADCE00C6364F /* Build configuration list for PBXProject "NSURLSession-NSURLProtocol-WebviewCache" */;
122 | compatibilityVersion = "Xcode 3.2";
123 | developmentRegion = English;
124 | hasScannedForEncodings = 0;
125 | knownRegions = (
126 | en,
127 | Base,
128 | );
129 | mainGroup = 4DD4CB151CEEADCE00C6364F;
130 | productRefGroup = 4DD4CB1F1CEEADCE00C6364F /* Products */;
131 | projectDirPath = "";
132 | projectRoot = "";
133 | targets = (
134 | 4DD4CB1D1CEEADCE00C6364F /* NSURLSession-NSURLProtocol-WebviewCache */,
135 | );
136 | };
137 | /* End PBXProject section */
138 |
139 | /* Begin PBXResourcesBuildPhase section */
140 | 4DD4CB1C1CEEADCE00C6364F /* Resources */ = {
141 | isa = PBXResourcesBuildPhase;
142 | buildActionMask = 2147483647;
143 | files = (
144 | 4DD4CB311CEEADCE00C6364F /* LaunchScreen.storyboard in Resources */,
145 | 4DD4CB2E1CEEADCE00C6364F /* Assets.xcassets in Resources */,
146 | 4DD4CB2C1CEEADCE00C6364F /* Main.storyboard in Resources */,
147 | );
148 | runOnlyForDeploymentPostprocessing = 0;
149 | };
150 | /* End PBXResourcesBuildPhase section */
151 |
152 | /* Begin PBXSourcesBuildPhase section */
153 | 4DD4CB1A1CEEADCE00C6364F /* Sources */ = {
154 | isa = PBXSourcesBuildPhase;
155 | buildActionMask = 2147483647;
156 | files = (
157 | 4DD4CB291CEEADCE00C6364F /* ViewController.m in Sources */,
158 | 4DD4CB3A1CEEAE6A00C6364F /* TURLSessionProtocol.m in Sources */,
159 | 4DD4CB261CEEADCE00C6364F /* AppDelegate.m in Sources */,
160 | 4DD4CB231CEEADCE00C6364F /* main.m in Sources */,
161 | );
162 | runOnlyForDeploymentPostprocessing = 0;
163 | };
164 | /* End PBXSourcesBuildPhase section */
165 |
166 | /* Begin PBXVariantGroup section */
167 | 4DD4CB2A1CEEADCE00C6364F /* Main.storyboard */ = {
168 | isa = PBXVariantGroup;
169 | children = (
170 | 4DD4CB2B1CEEADCE00C6364F /* Base */,
171 | );
172 | name = Main.storyboard;
173 | sourceTree = "";
174 | };
175 | 4DD4CB2F1CEEADCE00C6364F /* LaunchScreen.storyboard */ = {
176 | isa = PBXVariantGroup;
177 | children = (
178 | 4DD4CB301CEEADCE00C6364F /* Base */,
179 | );
180 | name = LaunchScreen.storyboard;
181 | sourceTree = "";
182 | };
183 | /* End PBXVariantGroup section */
184 |
185 | /* Begin XCBuildConfiguration section */
186 | 4DD4CB331CEEADCE00C6364F /* Debug */ = {
187 | isa = XCBuildConfiguration;
188 | buildSettings = {
189 | ALWAYS_SEARCH_USER_PATHS = NO;
190 | CLANG_ANALYZER_NONNULL = YES;
191 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
192 | CLANG_CXX_LIBRARY = "libc++";
193 | CLANG_ENABLE_MODULES = YES;
194 | CLANG_ENABLE_OBJC_ARC = YES;
195 | CLANG_WARN_BOOL_CONVERSION = YES;
196 | CLANG_WARN_CONSTANT_CONVERSION = YES;
197 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
198 | CLANG_WARN_EMPTY_BODY = YES;
199 | CLANG_WARN_ENUM_CONVERSION = YES;
200 | CLANG_WARN_INT_CONVERSION = YES;
201 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
202 | CLANG_WARN_UNREACHABLE_CODE = YES;
203 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
204 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
205 | COPY_PHASE_STRIP = NO;
206 | DEBUG_INFORMATION_FORMAT = dwarf;
207 | ENABLE_STRICT_OBJC_MSGSEND = YES;
208 | ENABLE_TESTABILITY = YES;
209 | GCC_C_LANGUAGE_STANDARD = gnu99;
210 | GCC_DYNAMIC_NO_PIC = NO;
211 | GCC_NO_COMMON_BLOCKS = YES;
212 | GCC_OPTIMIZATION_LEVEL = 0;
213 | GCC_PREPROCESSOR_DEFINITIONS = (
214 | "DEBUG=1",
215 | "$(inherited)",
216 | );
217 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
218 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
219 | GCC_WARN_UNDECLARED_SELECTOR = YES;
220 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
221 | GCC_WARN_UNUSED_FUNCTION = YES;
222 | GCC_WARN_UNUSED_VARIABLE = YES;
223 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
224 | MTL_ENABLE_DEBUG_INFO = YES;
225 | ONLY_ACTIVE_ARCH = YES;
226 | SDKROOT = iphoneos;
227 | };
228 | name = Debug;
229 | };
230 | 4DD4CB341CEEADCE00C6364F /* Release */ = {
231 | isa = XCBuildConfiguration;
232 | buildSettings = {
233 | ALWAYS_SEARCH_USER_PATHS = NO;
234 | CLANG_ANALYZER_NONNULL = YES;
235 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
236 | CLANG_CXX_LIBRARY = "libc++";
237 | CLANG_ENABLE_MODULES = YES;
238 | CLANG_ENABLE_OBJC_ARC = YES;
239 | CLANG_WARN_BOOL_CONVERSION = YES;
240 | CLANG_WARN_CONSTANT_CONVERSION = YES;
241 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
242 | CLANG_WARN_EMPTY_BODY = YES;
243 | CLANG_WARN_ENUM_CONVERSION = YES;
244 | CLANG_WARN_INT_CONVERSION = YES;
245 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
246 | CLANG_WARN_UNREACHABLE_CODE = YES;
247 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
248 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
249 | COPY_PHASE_STRIP = NO;
250 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
251 | ENABLE_NS_ASSERTIONS = NO;
252 | ENABLE_STRICT_OBJC_MSGSEND = YES;
253 | GCC_C_LANGUAGE_STANDARD = gnu99;
254 | GCC_NO_COMMON_BLOCKS = YES;
255 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
256 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
257 | GCC_WARN_UNDECLARED_SELECTOR = YES;
258 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
259 | GCC_WARN_UNUSED_FUNCTION = YES;
260 | GCC_WARN_UNUSED_VARIABLE = YES;
261 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
262 | MTL_ENABLE_DEBUG_INFO = NO;
263 | SDKROOT = iphoneos;
264 | VALIDATE_PRODUCT = YES;
265 | };
266 | name = Release;
267 | };
268 | 4DD4CB361CEEADCE00C6364F /* Debug */ = {
269 | isa = XCBuildConfiguration;
270 | buildSettings = {
271 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
272 | CODE_SIGN_IDENTITY = "iPhone Developer: Wen Luo (6P2MSBB3R6)";
273 | INFOPLIST_FILE = "NSURLSession-NSURLProtocol-WebviewCache/Info.plist";
274 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
275 | PRODUCT_BUNDLE_IDENTIFIER = com.233.test.ppppp;
276 | PRODUCT_NAME = "$(TARGET_NAME)";
277 | PROVISIONING_PROFILE = "5eb87a29-826a-4da2-809c-3ed7299dd292";
278 | };
279 | name = Debug;
280 | };
281 | 4DD4CB371CEEADCE00C6364F /* Release */ = {
282 | isa = XCBuildConfiguration;
283 | buildSettings = {
284 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
285 | CODE_SIGN_IDENTITY = "iPhone Developer: Wen Luo (6P2MSBB3R6)";
286 | INFOPLIST_FILE = "NSURLSession-NSURLProtocol-WebviewCache/Info.plist";
287 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
288 | PRODUCT_BUNDLE_IDENTIFIER = com.233.test.ppppp;
289 | PRODUCT_NAME = "$(TARGET_NAME)";
290 | PROVISIONING_PROFILE = "5eb87a29-826a-4da2-809c-3ed7299dd292";
291 | };
292 | name = Release;
293 | };
294 | /* End XCBuildConfiguration section */
295 |
296 | /* Begin XCConfigurationList section */
297 | 4DD4CB191CEEADCE00C6364F /* Build configuration list for PBXProject "NSURLSession-NSURLProtocol-WebviewCache" */ = {
298 | isa = XCConfigurationList;
299 | buildConfigurations = (
300 | 4DD4CB331CEEADCE00C6364F /* Debug */,
301 | 4DD4CB341CEEADCE00C6364F /* Release */,
302 | );
303 | defaultConfigurationIsVisible = 0;
304 | defaultConfigurationName = Release;
305 | };
306 | 4DD4CB351CEEADCE00C6364F /* Build configuration list for PBXNativeTarget "NSURLSession-NSURLProtocol-WebviewCache" */ = {
307 | isa = XCConfigurationList;
308 | buildConfigurations = (
309 | 4DD4CB361CEEADCE00C6364F /* Debug */,
310 | 4DD4CB371CEEADCE00C6364F /* Release */,
311 | );
312 | defaultConfigurationIsVisible = 0;
313 | defaultConfigurationName = Release;
314 | };
315 | /* End XCConfigurationList section */
316 | };
317 | rootObject = 4DD4CB161CEEADCE00C6364F /* Project object */;
318 | }
319 |
--------------------------------------------------------------------------------