├── .gitignore ├── LICENSE ├── Podfile ├── Podfile.lock ├── README.md ├── WebCacheFile.podspec ├── WebCacheFile ├── NSString+MD5.h ├── NSString+MD5.m ├── WebCacheFile.h └── WebCacheFile.m ├── assets ├── urldownload.png └── urllocal.png ├── demo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── demo.xcscheme ├── demo.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ ├── IDEWorkspaceChecks.plist │ └── WorkspaceSettings.xcsettings └── demo ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets ├── AppIcon.appiconset │ └── Contents.json └── Contents.json ├── Base.lproj └── LaunchScreen.storyboard ├── Info.plist ├── Prefix.pch ├── ViewController.h ├── ViewController.m ├── WkViewController.h ├── WkViewController.m └── main.m /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | # 6 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | 12 | ## Various settings 13 | xcuserdata/ 14 | 15 | ## Other 16 | *.moved-aside 17 | *.xccheckout 18 | *.xcuserstate 19 | *.xcscmblueprint 20 | 21 | ## Obj-C/Swift specific 22 | *.hmap 23 | *.ipa 24 | *.dSYM.zip 25 | *.dSYM 26 | 27 | # CocoaPods 28 | # 29 | # We recommend against adding the Pods directory to your .gitignore. However 30 | # you should judge for yourself, the pros and cons are mentioned at: 31 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 32 | # 33 | Pods/ 34 | 35 | # Carthage 36 | # 37 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 38 | # Carthage/Checkouts 39 | 40 | Carthage/Build 41 | 42 | # fastlane 43 | # 44 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 45 | # screenshots whenever they are needed. 46 | # For more information about the recommended setup visit: 47 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 48 | 49 | fastlane/report.xml 50 | fastlane/Preview.html 51 | fastlane/screenshots 52 | fastlane/test_output 53 | 54 | # Code Injection 55 | # 56 | # After new code Injection tools there's a generated folder /iOSInjectionProject 57 | # https://github.com/johnno1962/injectionforxcode 58 | 59 | iOSInjectionProject/ 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 ibireme 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | platform :ios, '9.0' 3 | 4 | target 'demo' do 5 | # Comment the next line if you don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for demo 9 | pod 'JKSandBoxManager' 10 | pod 'AFNetworking' 11 | 12 | end 13 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - AFNetworking (4.0.1): 3 | - AFNetworking/NSURLSession (= 4.0.1) 4 | - AFNetworking/Reachability (= 4.0.1) 5 | - AFNetworking/Security (= 4.0.1) 6 | - AFNetworking/Serialization (= 4.0.1) 7 | - AFNetworking/UIKit (= 4.0.1) 8 | - AFNetworking/NSURLSession (4.0.1): 9 | - AFNetworking/Reachability 10 | - AFNetworking/Security 11 | - AFNetworking/Serialization 12 | - AFNetworking/Reachability (4.0.1) 13 | - AFNetworking/Security (4.0.1) 14 | - AFNetworking/Serialization (4.0.1) 15 | - AFNetworking/UIKit (4.0.1): 16 | - AFNetworking/NSURLSession 17 | - JKSandBoxManager (0.1.7.1) 18 | 19 | DEPENDENCIES: 20 | - AFNetworking 21 | - JKSandBoxManager 22 | 23 | SPEC REPOS: 24 | trunk: 25 | - AFNetworking 26 | - JKSandBoxManager 27 | 28 | SPEC CHECKSUMS: 29 | AFNetworking: 7864c38297c79aaca1500c33288e429c3451fdce 30 | JKSandBoxManager: 1de779140b7c768a2362bfbd35b26e66dcb77392 31 | 32 | PODFILE CHECKSUM: 6f9478533cb9ca304ef431b295aa332dec0eae57 33 | 34 | COCOAPODS: 1.9.0 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebCacheFile 2 | 3 | Cache the web file locally for faster access next time. 4 | 5 | 缓存web文件到本地以提高下次访问速度。 6 | 7 | [WKWebView 的优化思考](https://github.com/stelalae/blog/blob/master/iOS/WKWebView.md) 8 | 9 | ## 安装 10 | 11 | 该库最低支持 iOS 9.0 和 Xcode 11。 12 | 13 | ### CocoaPods 14 | 15 | 1. 在 Podfile 中添加 pod 'WebCacheFile'。 16 | 2. 执行 pod install 或 pod update。 17 | 3. 导入 。 18 | 19 | ### 手动安装 20 | 21 | 1. 下载 WebCacheFile 文件夹内的所有内容。 22 | 2. 将 WebCacheFile 内的源文件添加(拖放)到你的工程。 23 | 3. 导入 WebCacheFile.h。 24 | 25 | ## 使用 26 | 27 | ``` 28 | @implementation WkViewController 29 | 30 | - (void)viewDidLoad { 31 | [super viewDidLoad]; 32 | 33 | // 1.注册需要拦截的协议类型 34 | [WebCacheFile initRegister:@[@"http", @"https"]]; 35 | 36 | // 2. 37 | [WebCacheFile setInterceptResourceTypes:@[@"js", @"css", @"png", @"jpg", @"gif"]]; 38 | // [self addWKWebView]; 39 | } 40 | 41 | - (void)dealloc { 42 | [WebCacheFile unregister]; 43 | } 44 | 45 | @end 46 | ``` 47 | 48 | 代码中已有Demo工程,可下载下来运行。示例截图: 49 | ![](assets/urldownload.png) 50 | 第一次加载,会去下载。 51 | 52 | ![](assets/urllocal.png) 53 | 第2+次加载,直接返回本地文件数据。 54 | 55 | 56 | 注意,对于在WkWebView里 post 请求 body 数据被清空的临时处理方案时,在js层将body数据放入request header中,key固定为`#define WkPostBodyKey @"headerBody"`。 57 | 58 | ``` 59 | Failed to execute 'setRequestHeader' on 'XMLHttpRequest': Value is not a valid ByteString 60 | ``` 61 | 若提示上面信息,即请求的头信息中不能出现中文或UTF-8码的字符,需要js端使用`encodeURIComponent`编码内容,iOS端使用`- (NSString *)URLUTF8DecodingString`解码。 62 | 63 | ------- 64 | 65 | 参考资料: 66 | * [IOS 使用NSURLProtocol 拦截网络请求实现缓存](https://blog.csdn.net/bobbob32/article/details/83512069) 67 | * [WKWebView实现网页静态资源优先从本地加载](https://blog.csdn.net/hanhailong18/article/details/79394856) 68 | * [移动 H5 首屏秒开优化方案探讨](http://blog.cnbang.net/tech/3477/) 69 | * [App内网页启动加速实践:静态资源预加载视角](https://zhuanlan.zhihu.com/p/77144845) 70 | * [WKWebView 那些坑](https://mp.weixin.qq.com/s/rhYKLIbXOsUJC_n6dt9UfA?) -------------------------------------------------------------------------------- /WebCacheFile.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint WebCacheFile.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see https://guides.cocoapods.org/syntax/podspec.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |spec| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | spec.name = "WebCacheFile" 19 | spec.version = "0.1.1" 20 | spec.summary = "Cache the web file locally for faster access next time" 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | spec.description = <<-DESC 28 | Cache the web file locally for faster access next time. 29 | 将web文件缓存到本地,提高下次访问速度。 30 | DESC 31 | 32 | spec.homepage = "https://github.com/stelalae/WebCacheFile" 33 | # spec.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" 34 | 35 | 36 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 37 | # 38 | # Licensing your code is important. See https://choosealicense.com for more info. 39 | # CocoaPods will detect a license file if there is a named LICENSE* 40 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 41 | # 42 | 43 | # spec.license = "MIT (example)" 44 | spec.license = { :type => "MIT", :file => "LICENSE" } 45 | 46 | 47 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 48 | # 49 | # Specify the authors of the library, with email addresses. Email addresses 50 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 51 | # accepts just a name if you'd rather not provide an email address. 52 | # 53 | # Specify a social_media_url where others can refer to, for example a twitter 54 | # profile URL. 55 | # 56 | 57 | spec.author = { "LeiYinchun" => "a@wyolo.com" } 58 | # Or just: spec.author = "LeiYinchun" 59 | # spec.authors = { "LeiYinchun" => "a@wyolo.com" } 60 | # spec.social_media_url = "https://twitter.com/LeiYinchun" 61 | 62 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 63 | # 64 | # If this Pod runs only on iOS or OS X, then specify the platform and 65 | # the deployment target. You can optionally include the target after the platform. 66 | # 67 | 68 | # spec.platform = :ios 69 | spec.platform = :ios, "9.0" 70 | 71 | # When using multiple platforms 72 | # spec.ios.deployment_target = "5.0" 73 | # spec.osx.deployment_target = "10.7" 74 | # spec.watchos.deployment_target = "2.0" 75 | # spec.tvos.deployment_target = "9.0" 76 | 77 | 78 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 79 | # 80 | # Specify the location from where the source should be retrieved. 81 | # Supports git, hg, bzr, svn and HTTP. 82 | # 83 | 84 | spec.source = { :git => "https://github.com/stelalae/WebCacheFile.git", :tag => "#{spec.version}" } 85 | 86 | 87 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 88 | # 89 | # CocoaPods is smart about how it includes source code. For source files 90 | # giving a folder will include any swift, h, m, mm, c & cpp files. 91 | # For header files it will include any header in the folder. 92 | # Not including the public_header_files will make all headers public. 93 | # 94 | 95 | spec.source_files = "WebCacheFile", "WebCacheFile/**/*.{h,m}" 96 | spec.exclude_files = "WebCacheFile/Exclude" 97 | 98 | spec.public_header_files = "WebCacheFile/WebCacheFile.h" 99 | 100 | 101 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 102 | # 103 | # A list of resources included with the Pod. These are copied into the 104 | # target bundle with a build phase script. Anything else will be cleaned. 105 | # You can preserve files from being cleaned, please don't preserve 106 | # non-essential files like tests, examples and documentation. 107 | # 108 | 109 | # spec.resource = "icon.png" 110 | # spec.resources = "Resources/*.png" 111 | 112 | # spec.preserve_paths = "FilesToSave", "MoreFilesToSave" 113 | 114 | 115 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 116 | # 117 | # Link your library with frameworks, or libraries. Libraries do not include 118 | # the lib prefix of their name. 119 | # 120 | 121 | # spec.framework = "SomeFramework" 122 | # spec.frameworks = "SomeFramework", "AnotherFramework" 123 | 124 | # spec.library = "iconv" 125 | # spec.libraries = "iconv", "xml2" 126 | 127 | 128 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 129 | # 130 | # If your library depends on compiler flags you can set them in the xcconfig hash 131 | # where they will only apply to your library. If you depend on other Podspecs 132 | # you can include multiple dependencies to ensure it works. 133 | 134 | # spec.requires_arc = true 135 | 136 | # spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 137 | spec.dependency "AFNetworking" 138 | spec.dependency "JKSandBoxManager" 139 | 140 | end 141 | -------------------------------------------------------------------------------- /WebCacheFile/NSString+MD5.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+MD5.h 3 | // Originally created for MyFile 4 | // 5 | // Created by Árpád Goretity, 2011. Some infos were grabbed from StackOverflow. 6 | // Released into the public domain. You can do whatever you want with this within the limits of applicable law (so nothing nasty!). 7 | // I'm not responsible for any damage related to the use of this software. There's NO WARRANTY AT ALL. 8 | // 9 | 10 | #import 11 | #import 12 | 13 | @interface NSString (MD5) 14 | 15 | // MD5 hash of the file on the filesystem specified by path 16 | + (NSString *) stringWithMD5OfFile: (NSString *) path; 17 | // The string's MD5 hash 18 | - (NSString *) MD5Hash; 19 | 20 | 21 | - (NSDictionary *)parseUrlParams; 22 | 23 | /** 24 | 对字符串的每个字符进行UTF-8编码 25 | 26 | @return 百分号编码后的字符串 27 | */ 28 | - (NSString *)URLUTF8EncodingString; 29 | 30 | /** 31 | 对字符串的每个字符进行彻底的 UTF-8 解码 32 | 连续编码2次,需要连续解码2次,第三次继续解码时,则返回为空 33 | @return 百分号编码解码后的字符串 34 | */ 35 | - (NSString *)URLUTF8DecodingString; 36 | 37 | @end 38 | 39 | -------------------------------------------------------------------------------- /WebCacheFile/NSString+MD5.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+MD5.m 3 | // Originally created for MyFile 4 | // 5 | // Created by Árpád Goretity, 2011. Some infos were grabbed from StackOverflow. 6 | // Released into the public domain. You can do whatever you want with this within the limits of applicable law (so nothing nasty!). 7 | // I'm not responsible for any damage related to the use of this software. There's NO WARRANTY AT ALL. 8 | // 9 | 10 | #import "NSString+MD5.h" 11 | 12 | @implementation NSString (MD5) 13 | 14 | + (NSString *) stringWithMD5OfFile: (NSString *) path { 15 | 16 | NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath: path]; 17 | if (handle == nil) { 18 | return nil; 19 | } 20 | 21 | CC_MD5_CTX md5; 22 | CC_MD5_Init (&md5); 23 | 24 | BOOL done = NO; 25 | 26 | while (!done) { 27 | 28 | NSData *fileData = [[NSData alloc] initWithData: [handle readDataOfLength: 4096]]; 29 | CC_MD5_Update (&md5, [fileData bytes], (CC_LONG) [fileData length]); 30 | 31 | if ([fileData length] == 0) { 32 | done = YES; 33 | } 34 | } 35 | 36 | unsigned char digest[CC_MD5_DIGEST_LENGTH]; 37 | CC_MD5_Final (digest, &md5); 38 | NSString *s = [NSString stringWithFormat: @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", 39 | digest[0], digest[1], 40 | digest[2], digest[3], 41 | digest[4], digest[5], 42 | digest[6], digest[7], 43 | digest[8], digest[9], 44 | digest[10], digest[11], 45 | digest[12], digest[13], 46 | digest[14], digest[15]]; 47 | 48 | return s; 49 | 50 | } 51 | 52 | - (NSString *) MD5Hash { 53 | 54 | CC_MD5_CTX md5; 55 | CC_MD5_Init (&md5); 56 | CC_MD5_Update (&md5, [self UTF8String], (CC_LONG) [self length]); 57 | 58 | unsigned char digest[CC_MD5_DIGEST_LENGTH]; 59 | CC_MD5_Final (digest, &md5); 60 | NSString *s = [NSString stringWithFormat: @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", 61 | digest[0], digest[1], 62 | digest[2], digest[3], 63 | digest[4], digest[5], 64 | digest[6], digest[7], 65 | digest[8], digest[9], 66 | digest[10], digest[11], 67 | digest[12], digest[13], 68 | digest[14], digest[15]]; 69 | 70 | return s; 71 | 72 | } 73 | 74 | 75 | - (NSDictionary *)parseUrlParams { 76 | NSMutableDictionary *parm = [[NSMutableDictionary alloc] init]; 77 | NSURL *url = [NSURL URLWithString:self]; 78 | if(!url) { 79 | return parm; 80 | } 81 | //传入url创建url组件类 82 | NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithString:url.absoluteString]; 83 | //回调遍历所有参数,添加入字典 84 | [urlComponents.queryItems enumerateObjectsUsingBlock:^(NSURLQueryItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 85 | [parm setObject:obj.value forKey:obj.name]; 86 | }]; 87 | return parm; 88 | } 89 | 90 | 91 | - (NSString *)URLUTF8EncodingString 92 | { 93 | if (self.length == 0) { 94 | return self; 95 | } 96 | NSString *encodeStr = [self stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; 97 | return encodeStr; 98 | } 99 | 100 | - (NSString *)URLUTF8DecodingString 101 | { 102 | if (self.length == 0) { 103 | return self; 104 | } 105 | NSString *decodedPre = self; 106 | NSString *decodedNext = [decodedPre stringByRemovingPercentEncoding]; 107 | while (decodedNext != nil && ![decodedNext isEqualToString:decodedPre]) { 108 | decodedPre = decodedNext; 109 | decodedNext = [decodedPre stringByRemovingPercentEncoding]; 110 | } 111 | return decodedNext; 112 | } 113 | 114 | @end 115 | 116 | -------------------------------------------------------------------------------- /WebCacheFile/WebCacheFile.h: -------------------------------------------------------------------------------- 1 | // 2 | // WebCacheFile.h 3 | // WebCacheFile 4 | // 5 | // Created by LeiYinchun on 2020/4/26. 6 | // Copyright © 2020年 LeiYinchun. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | #define WkPostBodyKey @"headerBody" 14 | 15 | 16 | @interface WebCacheFile : NSURLProtocol 17 | 18 | /** 19 | *注入拦截 20 | */ 21 | + (void)initRegister:(NSArray * _Nullable)scheme; 22 | 23 | 24 | + (void)unregister; 25 | 26 | /** 27 | *设置拦截的资源类型,建议默认 @[@"js", @"css"] 28 | */ 29 | + (void)setInterceptResourceTypes:(NSArray * _Nullable)resourceTypes; 30 | 31 | /** 32 | *设置拦截的资源类型,建议默认 @[@"post"] 33 | */ 34 | + (void)setInterceptMethods:(NSArray * _Nullable)methods; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /WebCacheFile/WebCacheFile.m: -------------------------------------------------------------------------------- 1 | // 2 | // WebCacheFile.m 3 | // WebCacheFile 4 | // 5 | // Created by LeiYinchun on 2020/4/26. 6 | // Copyright © 2020年 LeiYinchun. All rights reserved. 7 | // 8 | 9 | #import "WebCacheFile.h" 10 | #import "NSString+MD5.h" 11 | #import 12 | #import 13 | 14 | #ifdef WebCacheFile_DEBUG 15 | #define XLog(format, ...) NSLog((@"函数名:%s " "[行号:%d]\n" format), __FUNCTION__, __LINE__, ##__VA_ARGS__); 16 | #else 17 | #define XLog(...); 18 | #endif 19 | 20 | @interface WebCacheFile () 21 | 22 | @property (nonatomic, strong) AFURLSessionManager *manager; 23 | 24 | @end 25 | 26 | static NSString* const RequestedFlag = @"RequestedFlag"; 27 | static NSString* const FileCacheDir = @"webcachefiles"; 28 | static NSArray* gResourceTypes = nil; 29 | static NSArray* gMethods = nil; 30 | 31 | @implementation WebCacheFile 32 | 33 | #pragma mark - 协议拦截 34 | 35 | + (BOOL)canInitWithRequest:(NSURLRequest *)request 36 | { 37 | //已经处理 38 | if([NSURLProtocol propertyForKey:RequestedFlag inRequest:request]) { 39 | return NO; 40 | } 41 | 42 | return [self isNeedInterceptWithResourceTypes:request] || [self isNeedInterceptWithMethods:request]; 43 | } 44 | 45 | + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request 46 | { 47 | return request; 48 | } 49 | 50 | + (void)unregister { 51 | [WebCacheFile unregisterClass:[WebCacheFile class]]; 52 | } 53 | 54 | - (void)startLoading { 55 | 56 | //标记该请求已经处理 57 | NSMutableURLRequest *mutableReqeust = [super.request mutableCopy]; 58 | [NSURLProtocol setProperty:@YES forKey:RequestedFlag inRequest:mutableReqeust]; 59 | 60 | // 响应资源文件的拦截 61 | if([WebCacheFile isNeedInterceptWithResourceTypes:super.request]) { 62 | #ifdef WebCacheFile_DEBUG 63 | NSString *extension = mutableReqeust.URL.pathExtension; 64 | XLog(@"resource url: %@, extension: %@", mutableReqeust.URL.absoluteString, extension); 65 | #endif 66 | NSString *filePath = [self makeCacheFilePathByRequest]; 67 | if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { 68 | XLog(@"=== response local file: %@", filePath); 69 | [self responseWithFile:filePath]; 70 | } else { 71 | [self downloadResourcesRequest:[mutableReqeust copy]]; 72 | } 73 | return; 74 | } 75 | 76 | //响应指定方法的拦截 77 | if([WebCacheFile isNeedInterceptWithMethods:super.request]) { 78 | XLog(@"method url: %@, type: %@", mutableReqeust.URL.absoluteString, mutableReqeust.HTTPMethod); 79 | NSMutableDictionary *requestHeaders = [mutableReqeust.allHTTPHeaderFields mutableCopy]; 80 | // NSString *bodyDataStr = [[NSString alloc] initWithData:mutableReqeust.HTTPBody encoding:NSUTF8StringEncoding]; 81 | NSString *bodyDataStr = [requestHeaders objectForKey:WkPostBodyKey]; 82 | if(bodyDataStr) { 83 | [requestHeaders removeObjectForKey:WkPostBodyKey]; 84 | bodyDataStr = [bodyDataStr URLUTF8DecodingString]; 85 | } else { 86 | 87 | } 88 | bodyDataStr = bodyDataStr?:@"{}"; 89 | NSData *jsonData = [bodyDataStr dataUsingEncoding:NSUTF8StringEncoding]; 90 | [mutableReqeust setAllHTTPHeaderFields:requestHeaders]; 91 | [mutableReqeust setHTTPBody:jsonData]; 92 | [self postRequest:[mutableReqeust copy]]; 93 | return; 94 | } 95 | } 96 | 97 | - (void)stopLoading { 98 | } 99 | 100 | #pragma mark - 协议自定义实现 101 | 102 | //组装数据,响应被拦截的请求 103 | - (void)reponseIntercept:(NSURLResponse * _Nullable)response andData:(NSData * _Nullable)data { 104 | [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; 105 | [[self client] URLProtocol:self didLoadData:data]; 106 | [[self client] URLProtocolDidFinishLoading:self]; 107 | } 108 | 109 | - (void)responseWithFile:(NSString *)filePath { 110 | if(filePath) { 111 | NSString *mimeType = [self getMimeTypeWithFilePath:filePath]; 112 | NSURLResponse *response = [[NSURLResponse alloc] initWithURL:super.request.URL 113 | MIMEType:mimeType 114 | expectedContentLength:-1 115 | textEncodingName:nil]; 116 | NSData *data = [NSData dataWithContentsOfFile:filePath]; 117 | [self reponseIntercept:response andData:data]; 118 | } else { 119 | NSURLResponse *response = [[NSURLResponse alloc] initWithURL:super.request.URL 120 | MIMEType:nil 121 | expectedContentLength:-1 122 | textEncodingName:nil]; 123 | [self reponseIntercept:response andData:nil]; 124 | } 125 | } 126 | 127 | //普通请求 128 | - (void)postRequest:(NSURLRequest *)request{ 129 | NSURLSessionDataTask *task = [self.manager dataTaskWithRequest:request 130 | uploadProgress:^(NSProgress * _Nonnull uploadProgress) { 131 | 132 | } downloadProgress:^(NSProgress * _Nonnull downloadProgress) { 133 | 134 | } completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) { 135 | if(error) { 136 | XLog(@"=== downloadResourcesRequest error: %@ - %@", request.URL.absoluteString, error.description); 137 | [self reponseIntercept:response andData:nil]; 138 | } else { 139 | NSData *data= [NSJSONSerialization dataWithJSONObject:responseObject options:NSJSONWritingPrettyPrinted error:nil]; 140 | [self reponseIntercept:response andData:data]; 141 | } 142 | }]; 143 | [task resume]; 144 | } 145 | 146 | //下载资源文件 147 | - (void)downloadResourcesRequest:(NSURLRequest *)request { 148 | NSString *name = [NSString stringWithFormat:@"%@", super.request.URL.lastPathComponent]; 149 | NSString *targetFilePath =[self makeCacheFilePathByRequest]; 150 | 151 | NSURLSessionDownloadTask *task = [self.manager downloadTaskWithRequest:request progress:^(NSProgress *downloadProgress) { 152 | 153 | } destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) { 154 | NSURL *path = [NSURL fileURLWithPath:JKSandBoxPathTemp]; 155 | return [path URLByAppendingPathComponent:[NSString stringWithFormat:@"%@", name]]; 156 | 157 | } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) { 158 | if(error) { 159 | XLog(@"=== downloadResourcesRequest error: %@ - %@", request.URL.absoluteString, error.description); 160 | [self responseWithFile:nil]; 161 | } else { 162 | XLog(@"=== response download file: %@", targetFilePath); 163 | [JKSandBoxManager moveFileFrom:filePath.path to:targetFilePath]; 164 | [self responseWithFile:targetFilePath]; 165 | } 166 | }]; 167 | [task resume]; 168 | } 169 | 170 | - (NSString *)getMimeTypeWithFilePath:(NSString *)filePath { 171 | CFStringRef pathExtension = (__bridge_retained CFStringRef)[filePath pathExtension]; 172 | CFStringRef type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension, NULL); 173 | CFRelease(pathExtension); 174 | 175 | //The UTI can be converted to a mime type: 176 | NSString *mimeType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass(type, kUTTagClassMIMEType); 177 | if (type != NULL) 178 | CFRelease(type); 179 | 180 | return mimeType; 181 | } 182 | 183 | - (NSString *)makeCacheFilePathByRequest { 184 | NSString *dir =[JKSandBoxManager createCacheFilePathWithFolderName:FileCacheDir]; 185 | NSString *name = [NSString stringWithFormat:@"%@", super.request.URL.lastPathComponent]; 186 | NSString *path =[dir stringByAppendingString:[NSString stringWithFormat:@"/%@", name]]; 187 | return path; 188 | } 189 | 190 | - (AFURLSessionManager *)manager { 191 | if (!_manager) { 192 | NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; 193 | _manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; 194 | } 195 | return _manager; 196 | } 197 | 198 | #pragma mark - 对外设置 199 | 200 | + (void)initRegister:(NSArray * _Nullable)scheme { 201 | if(!scheme || scheme.count<1) { 202 | return; 203 | } 204 | 205 | [NSURLProtocol registerClass:[WebCacheFile class]]; 206 | 207 | Class cls = NSClassFromString(@"WKBrowsingContextController"); 208 | SEL sel = NSSelectorFromString(@"registerSchemeForCustomProtocol:"); 209 | if ([(id)cls respondsToSelector:sel]) { 210 | #pragma clang diagnostic push 211 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 212 | for (NSString *item in scheme) { 213 | [(id)cls performSelector:sel withObject:item]; 214 | } 215 | #pragma clang diagnostic pop 216 | } 217 | } 218 | 219 | + (void)setInterceptResourceTypes:(NSArray * _Nullable)resourceTypes { 220 | if(!resourceTypes) { 221 | return; 222 | } 223 | gResourceTypes = resourceTypes; 224 | } 225 | 226 | + (void)setInterceptMethods:(NSArray * _Nullable)methods { 227 | if(!methods) { 228 | return; 229 | } 230 | gMethods = methods; 231 | } 232 | 233 | + (BOOL)isNeedInterceptWithResourceTypes:(NSURLRequest *)request { 234 | //是否是属于被拦截的资源类型 235 | NSString *extension = request.URL.pathExtension; 236 | return [[self resourceTypes] indexOfObjectPassingTest:^BOOL(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 237 | return [extension compare:obj options:NSCaseInsensitiveSearch] == NSOrderedSame; 238 | }] != NSNotFound; 239 | } 240 | 241 | + (BOOL)isNeedInterceptWithMethods:(NSURLRequest *)request { 242 | //是否是属于被拦截的请求类型 243 | NSString *method = request.HTTPMethod; 244 | return [[self methods] indexOfObjectPassingTest:^BOOL(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 245 | return [method compare:obj options:NSCaseInsensitiveSearch] == NSOrderedSame; 246 | }] != NSNotFound; 247 | } 248 | 249 | + (NSArray *)methods{ 250 | if (!gMethods) { 251 | return @[@"post"]; 252 | } 253 | return gMethods; 254 | } 255 | 256 | + (NSArray *)resourceTypes 257 | { 258 | if (!gResourceTypes) { 259 | return @[@"js", @"css"]; 260 | } 261 | return gResourceTypes; 262 | } 263 | 264 | @end 265 | -------------------------------------------------------------------------------- /assets/urldownload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stechrayo/WebCacheFile/e3e1136516c0c27e14584b34d3982e60ec85cbb1/assets/urldownload.png -------------------------------------------------------------------------------- /assets/urllocal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stechrayo/WebCacheFile/e3e1136516c0c27e14584b34d3982e60ec85cbb1/assets/urllocal.png -------------------------------------------------------------------------------- /demo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 51; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 54E94B4E2462ACE100798B7C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 54E94B4D2462ACE100798B7C /* AppDelegate.m */; }; 11 | 54E94B542462ACE100798B7C /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 54E94B532462ACE100798B7C /* ViewController.m */; }; 12 | 54E94B592462ACE200798B7C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 54E94B582462ACE200798B7C /* Assets.xcassets */; }; 13 | 54E94B5C2462ACE200798B7C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 54E94B5A2462ACE200798B7C /* LaunchScreen.storyboard */; }; 14 | 54E94B5F2462ACE200798B7C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 54E94B5E2462ACE200798B7C /* main.m */; }; 15 | 54E94B6B2462AFF200798B7C /* WebCacheFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 54E94B672462AFF200798B7C /* WebCacheFile.m */; }; 16 | 54E94B6C2462AFF200798B7C /* NSString+MD5.m in Sources */ = {isa = PBXBuildFile; fileRef = 54E94B6A2462AFF200798B7C /* NSString+MD5.m */; }; 17 | 54E94B6F2462B40100798B7C /* WkViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 54E94B6E2462B40100798B7C /* WkViewController.m */; }; 18 | 6A200A7E94CEE93B954DE02D /* Pods_demo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 131E3D9628EA364368653B40 /* Pods_demo.framework */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | 131E3D9628EA364368653B40 /* Pods_demo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_demo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | 214A3151C893FB44375E32E1 /* Pods-demo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-demo.debug.xcconfig"; path = "Target Support Files/Pods-demo/Pods-demo.debug.xcconfig"; sourceTree = ""; }; 24 | 54C7615C2463AD4100BA6399 /* Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Prefix.pch; sourceTree = ""; }; 25 | 54E94B492462ACE000798B7C /* demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 26 | 54E94B4C2462ACE100798B7C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 27 | 54E94B4D2462ACE100798B7C /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 28 | 54E94B522462ACE100798B7C /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 29 | 54E94B532462ACE100798B7C /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 30 | 54E94B582462ACE200798B7C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 31 | 54E94B5B2462ACE200798B7C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 32 | 54E94B5D2462ACE200798B7C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | 54E94B5E2462ACE200798B7C /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 34 | 54E94B672462AFF200798B7C /* WebCacheFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebCacheFile.m; sourceTree = ""; }; 35 | 54E94B682462AFF200798B7C /* NSString+MD5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+MD5.h"; sourceTree = ""; }; 36 | 54E94B692462AFF200798B7C /* WebCacheFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebCacheFile.h; sourceTree = ""; }; 37 | 54E94B6A2462AFF200798B7C /* NSString+MD5.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+MD5.m"; sourceTree = ""; }; 38 | 54E94B6D2462B40100798B7C /* WkViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WkViewController.h; sourceTree = ""; }; 39 | 54E94B6E2462B40100798B7C /* WkViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WkViewController.m; sourceTree = ""; }; 40 | A4BB46BCCB7F88AC4F26B764 /* Pods-demo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-demo.release.xcconfig"; path = "Target Support Files/Pods-demo/Pods-demo.release.xcconfig"; sourceTree = ""; }; 41 | /* End PBXFileReference section */ 42 | 43 | /* Begin PBXFrameworksBuildPhase section */ 44 | 54E94B462462ACE000798B7C /* Frameworks */ = { 45 | isa = PBXFrameworksBuildPhase; 46 | buildActionMask = 2147483647; 47 | files = ( 48 | 6A200A7E94CEE93B954DE02D /* Pods_demo.framework in Frameworks */, 49 | ); 50 | runOnlyForDeploymentPostprocessing = 0; 51 | }; 52 | /* End PBXFrameworksBuildPhase section */ 53 | 54 | /* Begin PBXGroup section */ 55 | 54E94B402462ACE000798B7C = { 56 | isa = PBXGroup; 57 | children = ( 58 | 54E94B662462AFF200798B7C /* WebCacheFile */, 59 | 54E94B4B2462ACE000798B7C /* demo */, 60 | 54E94B4A2462ACE000798B7C /* Products */, 61 | B2052A716143FD0289F3453C /* Pods */, 62 | F4CB02CFC9BF9988A47F7253 /* Frameworks */, 63 | ); 64 | sourceTree = ""; 65 | }; 66 | 54E94B4A2462ACE000798B7C /* Products */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 54E94B492462ACE000798B7C /* demo.app */, 70 | ); 71 | name = Products; 72 | sourceTree = ""; 73 | }; 74 | 54E94B4B2462ACE000798B7C /* demo */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 54E94B4C2462ACE100798B7C /* AppDelegate.h */, 78 | 54E94B4D2462ACE100798B7C /* AppDelegate.m */, 79 | 54E94B582462ACE200798B7C /* Assets.xcassets */, 80 | 54E94B5D2462ACE200798B7C /* Info.plist */, 81 | 54E94B5A2462ACE200798B7C /* LaunchScreen.storyboard */, 82 | 54E94B5E2462ACE200798B7C /* main.m */, 83 | 54E94B522462ACE100798B7C /* ViewController.h */, 84 | 54E94B532462ACE100798B7C /* ViewController.m */, 85 | 54E94B6D2462B40100798B7C /* WkViewController.h */, 86 | 54E94B6E2462B40100798B7C /* WkViewController.m */, 87 | 54C7615C2463AD4100BA6399 /* Prefix.pch */, 88 | ); 89 | path = demo; 90 | sourceTree = ""; 91 | }; 92 | 54E94B662462AFF200798B7C /* WebCacheFile */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 54E94B672462AFF200798B7C /* WebCacheFile.m */, 96 | 54E94B682462AFF200798B7C /* NSString+MD5.h */, 97 | 54E94B692462AFF200798B7C /* WebCacheFile.h */, 98 | 54E94B6A2462AFF200798B7C /* NSString+MD5.m */, 99 | ); 100 | path = WebCacheFile; 101 | sourceTree = ""; 102 | }; 103 | B2052A716143FD0289F3453C /* Pods */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 214A3151C893FB44375E32E1 /* Pods-demo.debug.xcconfig */, 107 | A4BB46BCCB7F88AC4F26B764 /* Pods-demo.release.xcconfig */, 108 | ); 109 | path = Pods; 110 | sourceTree = ""; 111 | }; 112 | F4CB02CFC9BF9988A47F7253 /* Frameworks */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 131E3D9628EA364368653B40 /* Pods_demo.framework */, 116 | ); 117 | name = Frameworks; 118 | sourceTree = ""; 119 | }; 120 | /* End PBXGroup section */ 121 | 122 | /* Begin PBXNativeTarget section */ 123 | 54E94B482462ACE000798B7C /* demo */ = { 124 | isa = PBXNativeTarget; 125 | buildConfigurationList = 54E94B622462ACE200798B7C /* Build configuration list for PBXNativeTarget "demo" */; 126 | buildPhases = ( 127 | 648F0D17CF9A1DD140CFFF29 /* [CP] Check Pods Manifest.lock */, 128 | 54E94B452462ACE000798B7C /* Sources */, 129 | 54E94B462462ACE000798B7C /* Frameworks */, 130 | 54E94B472462ACE000798B7C /* Resources */, 131 | 7F83F4E51C2098311465D09B /* [CP] Embed Pods Frameworks */, 132 | ); 133 | buildRules = ( 134 | ); 135 | dependencies = ( 136 | ); 137 | name = demo; 138 | productName = demo; 139 | productReference = 54E94B492462ACE000798B7C /* demo.app */; 140 | productType = "com.apple.product-type.application"; 141 | }; 142 | /* End PBXNativeTarget section */ 143 | 144 | /* Begin PBXProject section */ 145 | 54E94B412462ACE000798B7C /* Project object */ = { 146 | isa = PBXProject; 147 | attributes = { 148 | LastUpgradeCheck = 1130; 149 | ORGANIZATIONNAME = leiyinchun; 150 | TargetAttributes = { 151 | 54E94B482462ACE000798B7C = { 152 | CreatedOnToolsVersion = 11.3.1; 153 | }; 154 | }; 155 | }; 156 | buildConfigurationList = 54E94B442462ACE000798B7C /* Build configuration list for PBXProject "demo" */; 157 | compatibilityVersion = "Xcode 9.3"; 158 | developmentRegion = en; 159 | hasScannedForEncodings = 0; 160 | knownRegions = ( 161 | en, 162 | Base, 163 | ); 164 | mainGroup = 54E94B402462ACE000798B7C; 165 | productRefGroup = 54E94B4A2462ACE000798B7C /* Products */; 166 | projectDirPath = ""; 167 | projectRoot = ""; 168 | targets = ( 169 | 54E94B482462ACE000798B7C /* demo */, 170 | ); 171 | }; 172 | /* End PBXProject section */ 173 | 174 | /* Begin PBXResourcesBuildPhase section */ 175 | 54E94B472462ACE000798B7C /* Resources */ = { 176 | isa = PBXResourcesBuildPhase; 177 | buildActionMask = 2147483647; 178 | files = ( 179 | 54E94B5C2462ACE200798B7C /* LaunchScreen.storyboard in Resources */, 180 | 54E94B592462ACE200798B7C /* Assets.xcassets in Resources */, 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | }; 184 | /* End PBXResourcesBuildPhase section */ 185 | 186 | /* Begin PBXShellScriptBuildPhase section */ 187 | 648F0D17CF9A1DD140CFFF29 /* [CP] Check Pods Manifest.lock */ = { 188 | isa = PBXShellScriptBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | ); 192 | inputFileListPaths = ( 193 | ); 194 | inputPaths = ( 195 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 196 | "${PODS_ROOT}/Manifest.lock", 197 | ); 198 | name = "[CP] Check Pods Manifest.lock"; 199 | outputFileListPaths = ( 200 | ); 201 | outputPaths = ( 202 | "$(DERIVED_FILE_DIR)/Pods-demo-checkManifestLockResult.txt", 203 | ); 204 | runOnlyForDeploymentPostprocessing = 0; 205 | shellPath = /bin/sh; 206 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 207 | showEnvVarsInLog = 0; 208 | }; 209 | 7F83F4E51C2098311465D09B /* [CP] Embed Pods Frameworks */ = { 210 | isa = PBXShellScriptBuildPhase; 211 | buildActionMask = 2147483647; 212 | files = ( 213 | ); 214 | inputFileListPaths = ( 215 | "${PODS_ROOT}/Target Support Files/Pods-demo/Pods-demo-frameworks-${CONFIGURATION}-input-files.xcfilelist", 216 | ); 217 | name = "[CP] Embed Pods Frameworks"; 218 | outputFileListPaths = ( 219 | "${PODS_ROOT}/Target Support Files/Pods-demo/Pods-demo-frameworks-${CONFIGURATION}-output-files.xcfilelist", 220 | ); 221 | runOnlyForDeploymentPostprocessing = 0; 222 | shellPath = /bin/sh; 223 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-demo/Pods-demo-frameworks.sh\"\n"; 224 | showEnvVarsInLog = 0; 225 | }; 226 | /* End PBXShellScriptBuildPhase section */ 227 | 228 | /* Begin PBXSourcesBuildPhase section */ 229 | 54E94B452462ACE000798B7C /* Sources */ = { 230 | isa = PBXSourcesBuildPhase; 231 | buildActionMask = 2147483647; 232 | files = ( 233 | 54E94B6C2462AFF200798B7C /* NSString+MD5.m in Sources */, 234 | 54E94B542462ACE100798B7C /* ViewController.m in Sources */, 235 | 54E94B6F2462B40100798B7C /* WkViewController.m in Sources */, 236 | 54E94B4E2462ACE100798B7C /* AppDelegate.m in Sources */, 237 | 54E94B5F2462ACE200798B7C /* main.m in Sources */, 238 | 54E94B6B2462AFF200798B7C /* WebCacheFile.m in Sources */, 239 | ); 240 | runOnlyForDeploymentPostprocessing = 0; 241 | }; 242 | /* End PBXSourcesBuildPhase section */ 243 | 244 | /* Begin PBXVariantGroup section */ 245 | 54E94B5A2462ACE200798B7C /* LaunchScreen.storyboard */ = { 246 | isa = PBXVariantGroup; 247 | children = ( 248 | 54E94B5B2462ACE200798B7C /* Base */, 249 | ); 250 | name = LaunchScreen.storyboard; 251 | sourceTree = ""; 252 | }; 253 | /* End PBXVariantGroup section */ 254 | 255 | /* Begin XCBuildConfiguration section */ 256 | 54E94B602462ACE200798B7C /* Debug */ = { 257 | isa = XCBuildConfiguration; 258 | buildSettings = { 259 | ALWAYS_SEARCH_USER_PATHS = NO; 260 | CLANG_ANALYZER_NONNULL = YES; 261 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 262 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 263 | CLANG_CXX_LIBRARY = "libc++"; 264 | CLANG_ENABLE_MODULES = YES; 265 | CLANG_ENABLE_OBJC_ARC = YES; 266 | CLANG_ENABLE_OBJC_WEAK = YES; 267 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 268 | CLANG_WARN_BOOL_CONVERSION = YES; 269 | CLANG_WARN_COMMA = YES; 270 | CLANG_WARN_CONSTANT_CONVERSION = YES; 271 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 272 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 273 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 274 | CLANG_WARN_EMPTY_BODY = YES; 275 | CLANG_WARN_ENUM_CONVERSION = YES; 276 | CLANG_WARN_INFINITE_RECURSION = YES; 277 | CLANG_WARN_INT_CONVERSION = YES; 278 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 279 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 280 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 281 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 282 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 283 | CLANG_WARN_STRICT_PROTOTYPES = YES; 284 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 285 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 286 | CLANG_WARN_UNREACHABLE_CODE = YES; 287 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 288 | COPY_PHASE_STRIP = NO; 289 | DEBUG_INFORMATION_FORMAT = dwarf; 290 | ENABLE_STRICT_OBJC_MSGSEND = YES; 291 | ENABLE_TESTABILITY = YES; 292 | GCC_C_LANGUAGE_STANDARD = gnu11; 293 | GCC_DYNAMIC_NO_PIC = NO; 294 | GCC_NO_COMMON_BLOCKS = YES; 295 | GCC_OPTIMIZATION_LEVEL = 0; 296 | GCC_PREPROCESSOR_DEFINITIONS = ( 297 | "DEBUG=1", 298 | "$(inherited)", 299 | ); 300 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 301 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 302 | GCC_WARN_UNDECLARED_SELECTOR = YES; 303 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 304 | GCC_WARN_UNUSED_FUNCTION = YES; 305 | GCC_WARN_UNUSED_VARIABLE = YES; 306 | IPHONEOS_DEPLOYMENT_TARGET = 13.2; 307 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 308 | MTL_FAST_MATH = YES; 309 | ONLY_ACTIVE_ARCH = YES; 310 | SDKROOT = iphoneos; 311 | }; 312 | name = Debug; 313 | }; 314 | 54E94B612462ACE200798B7C /* Release */ = { 315 | isa = XCBuildConfiguration; 316 | buildSettings = { 317 | ALWAYS_SEARCH_USER_PATHS = NO; 318 | CLANG_ANALYZER_NONNULL = YES; 319 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 320 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 321 | CLANG_CXX_LIBRARY = "libc++"; 322 | CLANG_ENABLE_MODULES = YES; 323 | CLANG_ENABLE_OBJC_ARC = YES; 324 | CLANG_ENABLE_OBJC_WEAK = YES; 325 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 326 | CLANG_WARN_BOOL_CONVERSION = YES; 327 | CLANG_WARN_COMMA = YES; 328 | CLANG_WARN_CONSTANT_CONVERSION = YES; 329 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 330 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 331 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 332 | CLANG_WARN_EMPTY_BODY = YES; 333 | CLANG_WARN_ENUM_CONVERSION = YES; 334 | CLANG_WARN_INFINITE_RECURSION = YES; 335 | CLANG_WARN_INT_CONVERSION = YES; 336 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 337 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 338 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 339 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 340 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 341 | CLANG_WARN_STRICT_PROTOTYPES = YES; 342 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 343 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 344 | CLANG_WARN_UNREACHABLE_CODE = YES; 345 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 346 | COPY_PHASE_STRIP = NO; 347 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 348 | ENABLE_NS_ASSERTIONS = NO; 349 | ENABLE_STRICT_OBJC_MSGSEND = YES; 350 | GCC_C_LANGUAGE_STANDARD = gnu11; 351 | GCC_NO_COMMON_BLOCKS = YES; 352 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 353 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 354 | GCC_WARN_UNDECLARED_SELECTOR = YES; 355 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 356 | GCC_WARN_UNUSED_FUNCTION = YES; 357 | GCC_WARN_UNUSED_VARIABLE = YES; 358 | IPHONEOS_DEPLOYMENT_TARGET = 13.2; 359 | MTL_ENABLE_DEBUG_INFO = NO; 360 | MTL_FAST_MATH = YES; 361 | SDKROOT = iphoneos; 362 | VALIDATE_PRODUCT = YES; 363 | }; 364 | name = Release; 365 | }; 366 | 54E94B632462ACE200798B7C /* Debug */ = { 367 | isa = XCBuildConfiguration; 368 | baseConfigurationReference = 214A3151C893FB44375E32E1 /* Pods-demo.debug.xcconfig */; 369 | buildSettings = { 370 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 371 | CODE_SIGN_STYLE = Automatic; 372 | DEVELOPMENT_TEAM = 965844UVNN; 373 | GCC_PREFIX_HEADER = "$(PROJECT_DIR)/demo/Prefix.pch"; 374 | INFOPLIST_FILE = demo/Info.plist; 375 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 376 | LD_RUNPATH_SEARCH_PATHS = ( 377 | "$(inherited)", 378 | "@executable_path/Frameworks", 379 | ); 380 | MARKETING_VERSION = 1.0.0; 381 | PRODUCT_BUNDLE_IDENTIFIER = com.leiyinchun.demo; 382 | PRODUCT_NAME = "$(TARGET_NAME)"; 383 | TARGETED_DEVICE_FAMILY = 1; 384 | }; 385 | name = Debug; 386 | }; 387 | 54E94B642462ACE200798B7C /* Release */ = { 388 | isa = XCBuildConfiguration; 389 | baseConfigurationReference = A4BB46BCCB7F88AC4F26B764 /* Pods-demo.release.xcconfig */; 390 | buildSettings = { 391 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 392 | CODE_SIGN_STYLE = Automatic; 393 | DEVELOPMENT_TEAM = 965844UVNN; 394 | GCC_PREFIX_HEADER = "$(PROJECT_DIR)/demo/Prefix.pch"; 395 | INFOPLIST_FILE = demo/Info.plist; 396 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 397 | LD_RUNPATH_SEARCH_PATHS = ( 398 | "$(inherited)", 399 | "@executable_path/Frameworks", 400 | ); 401 | MARKETING_VERSION = 1.0.0; 402 | PRODUCT_BUNDLE_IDENTIFIER = com.leiyinchun.demo; 403 | PRODUCT_NAME = "$(TARGET_NAME)"; 404 | TARGETED_DEVICE_FAMILY = 1; 405 | }; 406 | name = Release; 407 | }; 408 | /* End XCBuildConfiguration section */ 409 | 410 | /* Begin XCConfigurationList section */ 411 | 54E94B442462ACE000798B7C /* Build configuration list for PBXProject "demo" */ = { 412 | isa = XCConfigurationList; 413 | buildConfigurations = ( 414 | 54E94B602462ACE200798B7C /* Debug */, 415 | 54E94B612462ACE200798B7C /* Release */, 416 | ); 417 | defaultConfigurationIsVisible = 0; 418 | defaultConfigurationName = Release; 419 | }; 420 | 54E94B622462ACE200798B7C /* Build configuration list for PBXNativeTarget "demo" */ = { 421 | isa = XCConfigurationList; 422 | buildConfigurations = ( 423 | 54E94B632462ACE200798B7C /* Debug */, 424 | 54E94B642462ACE200798B7C /* Release */, 425 | ); 426 | defaultConfigurationIsVisible = 0; 427 | defaultConfigurationName = Release; 428 | }; 429 | /* End XCConfigurationList section */ 430 | }; 431 | rootObject = 54E94B412462ACE000798B7C /* Project object */; 432 | } 433 | -------------------------------------------------------------------------------- /demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo.xcodeproj/xcshareddata/xcschemes/demo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /demo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /demo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // demo 4 | // 5 | // Created by leiyinchun on 2020/5/6. 6 | // Copyright © 2020 leiyinchun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (nonatomic, strong) UIWindow *window; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /demo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // demo 4 | // 5 | // Created by leiyinchun on 2020/5/6. 6 | // Copyright © 2020 leiyinchun. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "ViewController.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | // Override point for customization after application launch. 21 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 22 | ViewController *vc = [ViewController new]; 23 | UINavigationController *naVC = [[UINavigationController alloc] initWithRootViewController:vc]; 24 | self.window.rootViewController = naVC; 25 | [self.window makeKeyAndVisible]; 26 | return YES; 27 | } 28 | 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /demo/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 | -------------------------------------------------------------------------------- /demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSAppTransportSecurity 24 | 25 | NSAllowsArbitraryLoads 26 | 27 | 28 | UILaunchStoryboardName 29 | LaunchScreen 30 | UIRequiredDeviceCapabilities 31 | 32 | armv7 33 | 34 | UISupportedInterfaceOrientations 35 | 36 | UIInterfaceOrientationPortrait 37 | 38 | UISupportedInterfaceOrientations~ipad 39 | 40 | UIInterfaceOrientationPortrait 41 | UIInterfaceOrientationPortraitUpsideDown 42 | UIInterfaceOrientationLandscapeLeft 43 | UIInterfaceOrientationLandscapeRight 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /demo/Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix.pch 3 | // demo 4 | // 5 | // Created by leiyinchun on 2020/5/7. 6 | // Copyright © 2020 leiyinchun. All rights reserved. 7 | // 8 | 9 | #ifndef Prefix_pch 10 | #define Prefix_pch 11 | 12 | // Include any system framework and library headers here that should be included in all compilation units. 13 | // You will also need to set the Prefix Header build setting of one or more of your targets to reference this file. 14 | 15 | #define WebCacheFile_DEBUG 16 | 17 | #endif /* Prefix_pch */ 18 | -------------------------------------------------------------------------------- /demo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // demo 4 | // 5 | // Created by leiyinchun on 2020/5/6. 6 | // Copyright © 2020 leiyinchun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /demo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // demo 4 | // 5 | // Created by leiyinchun on 2020/5/6. 6 | // Copyright © 2020 leiyinchun. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "WkViewController.h" 11 | 12 | @interface ViewController () 13 | 14 | @end 15 | 16 | @implementation ViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | // Do any additional setup after loading the view. 21 | 22 | self.view.backgroundColor = [UIColor whiteColor]; 23 | UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; 24 | button.frame = CGRectMake(0, 0, 69, 30); 25 | [button setTitle:@"点击" forState:UIControlStateNormal]; 26 | [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 27 | button.center = self.view.center; 28 | [button addTarget:self action:@selector(buttonClicked) forControlEvents:UIControlEventTouchUpInside]; 29 | [self.view addSubview:button]; 30 | } 31 | 32 | 33 | - (void)buttonClicked { 34 | NSString *str = @"https://www.163.com"; 35 | WkViewController *webVC = [WkViewController new]; 36 | webVC.urlStr = str; 37 | [self.navigationController pushViewController:webVC animated:YES]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /demo/WkViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // WkViewController.h 3 | // demo 4 | // 5 | // Created by leiyinchun on 2020/5/6. 6 | // Copyright © 2020 leiyinchun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface WkViewController : UIViewController 14 | 15 | @property (nonatomic, strong) NSString *urlStr; 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /demo/WkViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // WkViewController.m 3 | // demo 4 | // 5 | // Created by leiyinchun on 2020/5/6. 6 | // Copyright © 2020 leiyinchun. All rights reserved. 7 | // 8 | 9 | #import "WkViewController.h" 10 | #import 11 | #import "WebCacheFile.h" 12 | 13 | #define mainw [UIScreen mainScreen].bounds.size.width 14 | #define mainh [UIScreen mainScreen].bounds.size.height 15 | 16 | @interface WkViewController () 17 | 18 | @property (nonatomic, strong) WKWebView *wkWebView; 19 | 20 | @end 21 | 22 | @implementation WkViewController 23 | 24 | - (void)viewDidLoad { 25 | [super viewDidLoad]; 26 | // Do any additional setup after loading the view. 27 | 28 | [WebCacheFile initRegister:@[@"http", @"https"]]; 29 | [WebCacheFile setInterceptResourceTypes:@[@"js", @"css", @"png", @"jpg", @"gif"]]; 30 | [self addWKWebView]; 31 | } 32 | 33 | - (void)dealloc { 34 | [WebCacheFile unregister]; 35 | } 36 | 37 | - (void)addWKWebView 38 | { 39 | WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; 40 | config.selectionGranularity = WKSelectionGranularityDynamic; 41 | 42 | config.preferences = [[WKPreferences alloc] init]; 43 | config.preferences.minimumFontSize = 10; 44 | config.preferences.javaScriptEnabled = YES; 45 | config.processPool = [[WKProcessPool alloc] init]; 46 | config.userContentController = [[WKUserContentController alloc] init]; 47 | 48 | WKWebView *wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, mainw, mainh) configuration:config]; 49 | wkWebView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 50 | wkWebView.UIDelegate = self; 51 | wkWebView.navigationDelegate = self; 52 | 53 | NSURL *url = [NSURL URLWithString:_urlStr]; 54 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:20]; 55 | 56 | [wkWebView loadRequest:request]; 57 | [self.view addSubview:wkWebView]; 58 | _wkWebView = wkWebView; 59 | 60 | __weak __typeof(&*self) weakself = self; 61 | dispatch_async(dispatch_get_main_queue(), ^{ 62 | [weakself.wkWebView evaluateJavaScript:@"window.appVersion='8.8.8'" completionHandler:^(id _Nullable obj, NSError * _Nullable error) { 63 | NSLog(@"evaluateJavaScript error: %@", error.description); 64 | }]; 65 | }); 66 | } 67 | 68 | - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation { 69 | } 70 | 71 | - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation { 72 | } 73 | 74 | - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { 75 | if (webView.title.length > 0) { 76 | self.title = webView.title; 77 | } 78 | } 79 | 80 | - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error { 81 | } 82 | 83 | - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler { 84 | decisionHandler(WKNavigationResponsePolicyAllow); 85 | } 86 | 87 | - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction*)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { 88 | decisionHandler(WKNavigationActionPolicyAllow); 89 | } 90 | 91 | - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{ 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /demo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // demo 4 | // 5 | // Created by leiyinchun on 2020/5/6. 6 | // Copyright © 2020 leiyinchun. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | NSString * appDelegateClassName; 14 | @autoreleasepool { 15 | // Setup code that might create autoreleased objects goes here. 16 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 17 | } 18 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 19 | } 20 | --------------------------------------------------------------------------------