├── .DS_Store ├── Demo ├── .DS_Store ├── WechatIMG1822.png └── SSWKURLDemo │ ├── .DS_Store │ ├── SSWKURLDemo │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── ViewController.h │ ├── AppDelegate.h │ ├── SceneDelegate.h │ ├── main.m │ ├── AppDelegate.m │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ ├── Info.plist │ ├── SceneDelegate.m │ └── ViewController.m │ └── SSWKURLDemo.xcodeproj │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcuserdata │ │ └── sgcy.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ ├── xcuserdata │ └── sgcy.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ └── xcschememanagement.plist │ └── project.pbxproj ├── SSWKURL ├── SSUtils.h ├── SSCache.h ├── SSResourceLoader.h ├── SSWKURL.h ├── SSUtils.m ├── SSResourceLoader.m ├── SSCache.m └── SSWKURL.m └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lilidan/SSWKURL/HEAD/.DS_Store -------------------------------------------------------------------------------- /Demo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lilidan/SSWKURL/HEAD/Demo/.DS_Store -------------------------------------------------------------------------------- /Demo/WechatIMG1822.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lilidan/SSWKURL/HEAD/Demo/WechatIMG1822.png -------------------------------------------------------------------------------- /Demo/SSWKURLDemo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lilidan/SSWKURL/HEAD/Demo/SSWKURLDemo/.DS_Store -------------------------------------------------------------------------------- /Demo/SSWKURLDemo/SSWKURLDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Demo/SSWKURLDemo/SSWKURLDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/SSWKURLDemo/SSWKURLDemo.xcodeproj/xcuserdata/sgcy.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /Demo/SSWKURLDemo/SSWKURLDemo.xcodeproj/project.xcworkspace/xcuserdata/sgcy.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lilidan/SSWKURL/HEAD/Demo/SSWKURLDemo/SSWKURLDemo.xcodeproj/project.xcworkspace/xcuserdata/sgcy.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Demo/SSWKURLDemo/SSWKURLDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // SSWKURLDemo 4 | // 5 | // Created by sgcy on 2020/5/11. 6 | // Copyright © 2020 sgcy. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Demo/SSWKURLDemo/SSWKURLDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // SSWKURLDemo 4 | // 5 | // Created by sgcy on 2020/5/11. 6 | // Copyright © 2020 sgcy. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Demo/SSWKURLDemo/SSWKURLDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Demo/SSWKURLDemo/SSWKURLDemo/SceneDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.h 3 | // SSWKURLDemo 4 | // 5 | // Created by sgcy on 2020/5/11. 6 | // Copyright © 2020 sgcy. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SceneDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow * window; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /SSWKURL/SSUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSUtils.h 3 | // SSWKURLDemo 4 | // 5 | // Created by sgcy on 2021/1/21. 6 | // Copyright © 2021 sgcy. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface SSUtils : NSObject 14 | 15 | + (NSString *)requestIdForRequest:(NSURLRequest *)request; 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /Demo/SSWKURLDemo/SSWKURLDemo.xcodeproj/xcuserdata/sgcy.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SSWKURLDemo.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Demo/SSWKURLDemo/SSWKURLDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SSWKURLDemo 4 | // 5 | // Created by sgcy on 2020/5/11. 6 | // Copyright © 2020 sgcy. 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 | -------------------------------------------------------------------------------- /SSWKURL/SSCache.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSCache.h 3 | // SSWKURLDemo 4 | // 5 | // Created by sgcy on 2021/1/20. 6 | // Copyright © 2021 sgcy. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface SSCache : NSObject 14 | 15 | + (instancetype)sharedCache; 16 | 17 | - (NSData *)dataForRequestId:(NSString *)requestId; 18 | - (NSDictionary *)responseHeadersWithRequestID:(NSString *)requestId; 19 | 20 | - (void)saveData:(NSData *)data forRequestId:(NSString *)requestId; 21 | - (void)saveResponseHeaders:(NSDictionary *)responseHeaders forRequestId:(NSString *)requestId; 22 | - (void)finishRequestForRequestId:(NSString *)requestId; 23 | 24 | 25 | @end 26 | 27 | NS_ASSUME_NONNULL_END 28 | -------------------------------------------------------------------------------- /SSWKURL/SSResourceLoader.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSResourceLoader.h 3 | // SSWKURLDemo 4 | // 5 | // Created by sgcy on 2021/1/20. 6 | // Copyright © 2021 sgcy. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface SSReourceItem : NSObject 14 | 15 | @property (nonatomic,strong) NSURLResponse *response; 16 | @property (nonatomic,strong) NSData *data; 17 | @property (nonatomic,strong) NSError *error; 18 | 19 | @end 20 | 21 | 22 | @interface SSResourceLoader : NSObject 23 | 24 | + (instancetype)sharedLoader; 25 | 26 | - (SSReourceItem *)loadResource:(NSURLRequest *)request; 27 | 28 | - (void)preloadResourceWithRequest:(NSURLRequest *)request; 29 | 30 | @end 31 | 32 | NS_ASSUME_NONNULL_END 33 | -------------------------------------------------------------------------------- /SSWKURL/SSWKURL.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSWKURL.h 3 | // SSWKURL 4 | // 5 | // Created by sgcy on 2020/4/21. 6 | // Copyright © 2020 sgcy. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | 13 | @interface SSWKURLProtocol:NSObject 14 | 15 | @property (nonatomic,readonly,copy) NSURLRequest *request; 16 | 17 | + (BOOL)canInitWithRequest:(NSURLRequest *)request; 18 | + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request; 19 | - (void)startLoading:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler; 20 | - (void)stopLoading; 21 | 22 | @end 23 | 24 | 25 | @interface WKWebViewConfiguration(ssRegisterURLProtocol) 26 | 27 | - (void)ssRegisterURLProtocol:(Class)protocolClass; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # SSWKURL 3 | 4 | Intercept and cache requests of WKWebView. 5 | 6 | 介绍[iOS完美请求拦截](https://www.jianshu.com/p/7b28cbd8f92a) 7 | [WKWebView缓存+请求拦截](https://www.jianshu.com/p/44f4fa1d3d12) 8 | 9 | 网易团队采用了更完整细致(原理相同)的解决方案,建议采用他们的:[WKWebView 请求拦截探索与实践](https://juejin.cn/post/6922625242796032007) 10 | 11 | ## Features 12 | 13 | - Intercept All Requests of WKWebView. 14 | - Cache requests. 15 | 16 | 17 | 18 | 19 | ## Installation 20 | 21 | Drag the `SSWKURL.h` and `SSWKURL.m` files into your project. 22 | 23 | 24 | ## Usage 25 | 26 | Subclass `SSWKURLProtocol` like `NSURLProtol`. 27 | 28 | Implement your own `-startLoading:` and `-stopLoading`. 29 | 30 | Then call `[yourWKURLConfiguration ssRegisterURLProtocol:[YourSSWKURLProtocol class]];` 31 | 32 | Then You could moniter requests or load cache for requests. 33 | 34 | ## Compare to VasSonic 35 | 36 | - support WKWebview 37 | - iOS SDK > 12 38 | 39 | 40 | ## Blog 41 | 42 | [WKWebView完美(?)网络请求拦截](https://www.jianshu.com/p/7b28cbd8f92a) 43 | [WKWebView缓存+请求拦截](https://www.jianshu.com/p/44f4fa1d3d12) 44 | 网易团队采用了更完整细致(原理相同)的解决方案,建议采用他们的:[WKWebView 请求拦截探索与实践](https://juejin.cn/post/6922625242796032007) 45 | 46 | -------------------------------------------------------------------------------- /SSWKURL/SSUtils.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSUtils.m 3 | // SSWKURLDemo 4 | // 5 | // Created by sgcy on 2021/1/21. 6 | // Copyright © 2021 sgcy. All rights reserved. 7 | // 8 | 9 | #import "SSUtils.h" 10 | #import 11 | 12 | @implementation SSUtils 13 | 14 | + (NSString *)requestIdForRequest:(NSURLRequest *)request 15 | { 16 | NSString *url = request.URL.absoluteString; 17 | NSDictionary *headers = request.allHTTPHeaderFields; 18 | if (headers) { 19 | if ([NSJSONSerialization isValidJSONObject:headers]) { 20 | NSData *jsonData = [NSJSONSerialization dataWithJSONObject:headers options:1 error:nil]; 21 | if (jsonData) { 22 | NSString *str = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; 23 | // url = [url stringByAppendingString:str]; 24 | } 25 | } 26 | } 27 | if (request.HTTPBody) { 28 | //POST BODY? 29 | } 30 | return [self md5:url]; 31 | } 32 | 33 | + (NSString *)md5:(NSString *)string { 34 | const char *cStr = [string UTF8String]; 35 | unsigned char digest[CC_MD5_DIGEST_LENGTH]; 36 | CC_MD5(cStr, (CC_LONG)strlen(cStr), digest); 37 | NSMutableString *result = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2]; 38 | for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) { 39 | [result appendFormat:@"%02X", digest[i]]; 40 | } 41 | return result; 42 | } 43 | 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Demo/SSWKURLDemo/SSWKURLDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // SSWKURLDemo 4 | // 5 | // Created by sgcy on 2020/5/11. 6 | // Copyright © 2020 sgcy. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | #pragma mark - UISceneSession lifecycle 25 | 26 | 27 | - (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options { 28 | // Called when a new scene session is being created. 29 | // Use this method to select a configuration to create the new scene with. 30 | return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; 31 | } 32 | 33 | 34 | - (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions { 35 | // Called when the user discards a scene session. 36 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 37 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 38 | } 39 | 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /Demo/SSWKURLDemo/SSWKURLDemo/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 | -------------------------------------------------------------------------------- /Demo/SSWKURLDemo/SSWKURLDemo/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 | -------------------------------------------------------------------------------- /SSWKURL/SSResourceLoader.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSResourceLoader.m 3 | // SSWKURLDemo 4 | // 5 | // Created by sgcy on 2021/1/20. 6 | // Copyright © 2021 sgcy. All rights reserved. 7 | // 8 | 9 | #import "SSResourceLoader.h" 10 | #import "SSCache.h" 11 | #import "SSUtils.h" 12 | 13 | @implementation SSReourceItem 14 | 15 | @end 16 | 17 | 18 | @interface SSResourceLoader() 19 | 20 | @property (nonatomic,strong) NSMutableData *data; 21 | @property (nonatomic,strong) NSURLResponse *response; 22 | 23 | @end 24 | 25 | @implementation SSResourceLoader 26 | 27 | + (instancetype)sharedLoader 28 | { 29 | static SSResourceLoader *defaultLoader = nil; 30 | static dispatch_once_t onceToken; 31 | dispatch_once(&onceToken, ^{ 32 | defaultLoader = [[self alloc]init]; 33 | }); 34 | return defaultLoader; 35 | } 36 | 37 | - (SSReourceItem *)loadResource:(NSURLRequest *)request 38 | { 39 | //load from cache 40 | NSString *requestId = [SSUtils requestIdForRequest:request]; 41 | NSDictionary *responseHeaders = [[SSCache sharedCache] responseHeadersWithRequestID:requestId]; 42 | if (responseHeaders) { 43 | SSReourceItem *item = [[SSReourceItem alloc] init]; 44 | NSHTTPURLResponse *resp = [[NSHTTPURLResponse alloc]initWithURL:request.URL statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:responseHeaders]; 45 | item.response = resp; 46 | NSData *data = [[SSCache sharedCache] dataForRequestId:requestId]; 47 | if (data) { 48 | item.data = data; 49 | } 50 | return item; 51 | }else{ 52 | return nil; 53 | } 54 | } 55 | 56 | - (void)preloadResourceWithRequest:(NSURLRequest *)request 57 | { 58 | //preload from cache 59 | SSReourceItem *item = [self loadResource:request]; 60 | //preload from network 61 | if (!item) { 62 | 63 | } 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /Demo/SSWKURLDemo/SSWKURLDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Demo/SSWKURLDemo/SSWKURLDemo/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 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIMainStoryboardFile 45 | Main 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | NSAppTransportSecurity 64 | 65 | NSAllowsArbitraryLoads 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /Demo/SSWKURLDemo/SSWKURLDemo/SceneDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.m 3 | // SSWKURLDemo 4 | // 5 | // Created by sgcy on 2020/5/11. 6 | // Copyright © 2020 sgcy. All rights reserved. 7 | // 8 | 9 | #import "SceneDelegate.h" 10 | 11 | @interface SceneDelegate () 12 | 13 | @end 14 | 15 | @implementation SceneDelegate 16 | 17 | - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { 18 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 19 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 20 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 21 | } 22 | 23 | 24 | - (void)sceneDidDisconnect:(UIScene *)scene { 25 | // Called as the scene is being released by the system. 26 | // This occurs shortly after the scene enters the background, or when its session is discarded. 27 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 28 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 29 | } 30 | 31 | 32 | - (void)sceneDidBecomeActive:(UIScene *)scene { 33 | // Called when the scene has moved from an inactive state to an active state. 34 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 35 | } 36 | 37 | 38 | - (void)sceneWillResignActive:(UIScene *)scene { 39 | // Called when the scene will move from an active state to an inactive state. 40 | // This may occur due to temporary interruptions (ex. an incoming phone call). 41 | } 42 | 43 | 44 | - (void)sceneWillEnterForeground:(UIScene *)scene { 45 | // Called as the scene transitions from the background to the foreground. 46 | // Use this method to undo the changes made on entering the background. 47 | } 48 | 49 | 50 | - (void)sceneDidEnterBackground:(UIScene *)scene { 51 | // Called as the scene transitions from the foreground to the background. 52 | // Use this method to save data, release shared resources, and store enough scene-specific state information 53 | // to restore the scene back to its current state. 54 | } 55 | 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /Demo/SSWKURLDemo/SSWKURLDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // SSWKURLDemo 4 | // 5 | // Created by sgcy on 2020/5/11. 6 | // Copyright © 2020 sgcy. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import 11 | #import "SSWKURL.h" 12 | 13 | @interface ViewController () 14 | 15 | @property (nonatomic,strong) NSDictionary *timing; 16 | @property (nonatomic,strong) WKWebView *webView; 17 | @property (nonatomic,strong) NSDate *beginDate; 18 | 19 | @end 20 | 21 | @implementation ViewController 22 | 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | // Do any additional se3tup after loading the view. 26 | WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; 27 | [config.userContentController addScriptMessageHandler:self name:@"event"]; 28 | 29 | 30 | NSString *jScript = @"window.webkit.messageHandlers.event.postMessage({\"\":\"\"});"; 31 | WKUserScript *js = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]; 32 | [config.userContentController addUserScript:js]; 33 | 34 | [config ssRegisterURLProtocol:[SSWKURLProtocol class]]; 35 | WKWebView *wkWebView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config]; 36 | wkWebView.navigationDelegate = self; 37 | [self.view addSubview:wkWebView]; 38 | 39 | // NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://v.youku.com/v_show/id_XNDc1NDI5MzQ0OA==.html"]]; 40 | NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.bilibili.com/video/BV1rf4y1e7MD?p=2"]]; 41 | 42 | // NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.iqiyi.com/v_1pxfxfkiehg.html?vfrm=pcw_home&vfrmblk=D&vfrmrst=712211_focus_A_image4"]]; 43 | 44 | 45 | [wkWebView loadRequest:request]; 46 | 47 | self.webView = wkWebView; 48 | self.beginDate = [NSDate date]; 49 | } 50 | 51 | 52 | 53 | - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message 54 | { 55 | NSDate *nowDate = [NSDate date]; 56 | NSTimeInterval delta = [nowDate timeIntervalSinceDate:self.beginDate]; 57 | if (delta > 0.5) { 58 | NSLog(@"%f",delta); 59 | NSLog(@"--------------"); 60 | } 61 | 62 | // [self.webView evaluateJavaScript:@"JSON.stringify(window.performance.timing.toJSON())" completionHandler:^(NSString * _Nullable timingStr, NSError * _Nullable error) { 63 | // if (!error) {、 64 | // self.timing = [NSJSONSerialization JSONObjectWithData:[timingStr dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:nil]; 65 | // NSInteger before = [self.timing[@"domainLookupStart"] integerValue] - [self.timing[@"navigationStart"] integerValue]; 66 | // NSInteger dnsTime = [self.timing[@"domainLookupEnd"] integerValue] - [self.timing[@"domainLookupStart"] integerValue]; 67 | // NSInteger handShakeTime = [self.timing[@"connectEnd"] integerValue] - [self.timing[@"connectStart"] integerValue]; 68 | // 69 | // NSInteger requestTime = [self.timing[@"responseStart"] integerValue] - [self.timing[@"requestStart"] integerValue]; 70 | // NSInteger responseTime = [self.timing[@"responseEnd"] integerValue] - [self.timing[@"responseStart"] integerValue]; 71 | // NSInteger domTime = [self.timing[@"domComplete"] integerValue] - [self.timing[@"responseEnd"] integerValue]; 72 | // 73 | // NSLog(@"BEFORE %d",before); 74 | // NSLog(@"DNS %d",dnsTime); 75 | // NSLog(@"TCP %d",handShakeTime); 76 | // NSLog(@"REQUEST %d",requestTime); 77 | // NSLog(@"RESPONSE %d",responseTime); 78 | // NSLog(@"DOM %d",domTime); 79 | // 80 | // 81 | // } 82 | // }]; 83 | } 84 | 85 | - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation 86 | { 87 | NSLog(@""); 88 | } 89 | 90 | - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error 91 | { 92 | NSLog(@""); 93 | } 94 | 95 | - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView 96 | { 97 | NSLog(@""); 98 | 99 | } 100 | 101 | - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error 102 | { 103 | NSLog(@""); 104 | 105 | } 106 | 107 | 108 | 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /SSWKURL/SSCache.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSCache.m 3 | // SSWKURLDemo 4 | // 5 | // Created by sgcy on 2021/1/20. 6 | // Copyright © 2021 sgcy. All rights reserved. 7 | // 8 | 9 | #import "SSCache.h" 10 | 11 | @interface SSCache() 12 | 13 | @property (nonatomic,strong) NSMutableDictionary *responseCache; 14 | @property (nonatomic,strong) NSMutableDictionary *dataCache; 15 | @property (nonatomic,strong) NSMutableDictionary *requestFinished; 16 | @property (nonatomic,strong) NSLock *lock; 17 | @property (nonatomic,strong) NSString *rootCachePath; 18 | 19 | @end 20 | 21 | @implementation SSCache 22 | 23 | 24 | + (instancetype)sharedCache 25 | { 26 | static SSCache *sharedCache = nil; 27 | static dispatch_once_t onceToken; 28 | dispatch_once(&onceToken, ^{ 29 | sharedCache = [[self alloc]init]; 30 | }); 31 | return sharedCache; 32 | } 33 | 34 | - (instancetype)init 35 | { 36 | if (self = [super init]) { 37 | self.responseCache = [[NSMutableDictionary alloc] init]; 38 | self.dataCache = [[NSMutableDictionary alloc] init]; 39 | self.requestFinished = [[NSMutableDictionary alloc] init]; 40 | self.lock = [[NSLock alloc] init]; 41 | [self setupCacheDirectory]; 42 | } 43 | return self; 44 | } 45 | 46 | - (NSData *)dataForRequestId:(NSString *)requestId 47 | { 48 | //load from memory 49 | NSData *data = self.dataCache[requestId]; 50 | if (data) { 51 | NSNumber *finished = self.requestFinished[requestId]; 52 | if (finished && finished.boolValue) { 53 | return data; 54 | } 55 | } 56 | 57 | //load from disk 58 | NSString *cacheFilePath = [self filePathWithType:1 sessionID:requestId]; 59 | return [NSData dataWithContentsOfFile:cacheFilePath]; 60 | } 61 | 62 | - (NSDictionary *)responseHeadersWithRequestID:(NSString *)requestId 63 | { 64 | //load from memory 65 | NSDictionary *responseHeaders = self.responseCache[requestId]; 66 | if (responseHeaders) { 67 | return responseHeaders; 68 | } 69 | 70 | //load from disk 71 | NSString *responsePath = [self filePathWithType:0 sessionID:requestId]; 72 | return [NSDictionary dictionaryWithContentsOfFile:responsePath]; 73 | } 74 | 75 | - (void)saveData:(NSData *)data forRequestId:(NSString *)requestId 76 | { 77 | NSMutableData *mutaData = self.dataCache[requestId]; 78 | if (!mutaData) { 79 | mutaData = [[NSMutableData alloc] init]; 80 | } 81 | [mutaData appendData:data]; 82 | [self.dataCache setObject:mutaData forKey:requestId]; 83 | } 84 | 85 | - (void)saveResponseHeaders:(NSDictionary *)responseHeaders forRequestId:(NSString *)requestId 86 | { 87 | [self.responseCache setObject:responseHeaders forKey:requestId]; 88 | } 89 | 90 | - (void)finishRequestForRequestId:(NSString *)requestId 91 | { 92 | [self.requestFinished setObject:@(YES) forKey:requestId]; 93 | 94 | NSDictionary *responseHeaders = self.responseCache[requestId]; 95 | if (responseHeaders) { 96 | NSString *responsePath = [self filePathWithType:0 sessionID:requestId]; 97 | BOOL isSuccess = [responseHeaders writeToFile:responsePath atomically:YES]; 98 | } 99 | 100 | NSData *data = self.dataCache[requestId]; 101 | if (data) { 102 | NSString *dataPath = [self filePathWithType:1 sessionID:requestId]; 103 | BOOL isSuccess = [data writeToFile:dataPath atomically:YES]; 104 | } 105 | 106 | } 107 | 108 | #pragma mark - file 109 | 110 | - (BOOL)setupCacheDirectory 111 | { 112 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 113 | _rootCachePath = [[self createDirectoryIfNotExist:[paths objectAtIndex:0] withSubPath:@"SSCache"] copy]; 114 | return _rootCachePath.length > 0; 115 | } 116 | 117 | - (BOOL)checkCacheTypeExist:(NSInteger)type sessionID:(NSString *)sessionID 118 | { 119 | NSString *cachePath = [self filePathWithType:type sessionID:sessionID]; 120 | 121 | return [[NSFileManager defaultManager] fileExistsAtPath:cachePath]; 122 | } 123 | 124 | - (NSString *)filePathWithType:(NSInteger)type sessionID:(NSString *)sessionID 125 | { 126 | NSString *fileDir = [self sessionSubCacheDir:sessionID]; 127 | if (fileDir.length == 0) { 128 | return nil; 129 | } 130 | NSString *cacheFileName = [sessionID stringByAppendingPathExtension:[@(type) stringValue]]; 131 | return [fileDir stringByAppendingPathComponent:cacheFileName]; 132 | } 133 | 134 | - (NSString *)sessionSubCacheDir:(NSString *)sessionID 135 | { 136 | return [self createDirectoryIfNotExist:_rootCachePath withSubPath:sessionID]; 137 | } 138 | 139 | - (NSString *)createDirectoryIfNotExist:(NSString *)parent withSubPath:(NSString *)subPath 140 | { 141 | if(parent.length == 0 || subPath.length == 0){ 142 | return nil; 143 | } 144 | 145 | BOOL isDir = YES; 146 | NSString *path = [parent stringByAppendingPathComponent:subPath]; 147 | if (![[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) { 148 | NSError *error = nil; 149 | [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error]; 150 | if (error) { 151 | return nil; 152 | } 153 | } 154 | return path; 155 | } 156 | 157 | @end 158 | -------------------------------------------------------------------------------- /SSWKURL/SSWKURL.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSWKURL.m 3 | // SSWKURL 4 | // 5 | // Created by sgcy on 2020/4/21. 6 | // Copyright © 2020 sgcy. All rights reserved. 7 | // 8 | 9 | #import "SSWKURL.h" 10 | #import 11 | #import "SSCache.h" 12 | #import "SSUtils.h" 13 | #import "SSResourceLoader.h" 14 | 15 | @interface WKWebView(handlesURLScheme) 16 | 17 | 18 | @end 19 | 20 | @implementation WKWebView(handlesURLScheme) 21 | 22 | 23 | + (BOOL)handlesURLScheme:(NSString *)urlScheme 24 | { 25 | return NO; 26 | } 27 | 28 | @end 29 | 30 | #pragma mark - 31 | 32 | typedef BOOL (^HTTPDNSCookieFilter)(NSHTTPCookie *, NSURL *); 33 | 34 | @interface NSURLRequest(requestId) 35 | 36 | @property (nonatomic,assign) BOOL ss_stop; 37 | - (NSString *)requestId; 38 | - (NSString *)requestRepresent; 39 | 40 | @end 41 | 42 | static char *kNSURLRequestSSTOPKEY = "kNSURLRequestSSTOPKEY"; 43 | 44 | @implementation NSURLRequest(requestId) 45 | 46 | - (BOOL)ss_stop 47 | { 48 | return [objc_getAssociatedObject(self, kNSURLRequestSSTOPKEY) boolValue]; 49 | } 50 | 51 | - (void)setSs_stop:(BOOL)ss_stop 52 | { 53 | objc_setAssociatedObject(self, kNSURLRequestSSTOPKEY, @(ss_stop), OBJC_ASSOCIATION_ASSIGN); 54 | } 55 | 56 | - (NSString *)requestId 57 | { 58 | return [@([self hash]) stringValue]; 59 | } 60 | 61 | - (NSString *)requestRepresent 62 | { 63 | return [NSString stringWithFormat:@"%@---%@",self.URL.absoluteString,self.HTTPMethod]; 64 | } 65 | 66 | + (BOOL)allowsAnyHTTPSCertificateForHost:(NSString *)host { 67 | return YES; 68 | } 69 | 70 | + (void)setAllowsAnyHTTPSCertificate:(BOOL)allow forHost:(NSString *)host { 71 | 72 | } 73 | 74 | 75 | @end 76 | 77 | #pragma mark - 78 | 79 | @interface SSWKTaskDelegate : NSObject 80 | 81 | @property (nonatomic,weak) id schemeTask; 82 | 83 | @end 84 | 85 | @implementation SSWKTaskDelegate 86 | 87 | - (void)URLSession:(NSURLSession *)session 88 | task:(NSURLSessionTask *)task 89 | didCompleteWithError:(NSError *)error 90 | { 91 | if (error) { 92 | [self.schemeTask didFailWithError:error]; 93 | }else{ 94 | [self.schemeTask didFinish]; 95 | [[SSCache sharedCache] finishRequestForRequestId:[SSUtils requestIdForRequest:task.currentRequest]]; 96 | } 97 | } 98 | 99 | - (void)URLSession:(NSURLSession *)session 100 | dataTask:(NSURLSessionDataTask *)dataTask 101 | didReceiveData:(NSData *)data 102 | { 103 | if (dataTask.state == NSURLSessionTaskStateCanceling) { 104 | return; 105 | } 106 | [self.schemeTask didReceiveData:data]; 107 | [[SSCache sharedCache] saveData:data forRequestId:[SSUtils requestIdForRequest:dataTask.currentRequest]]; 108 | } 109 | 110 | 111 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask 112 | didReceiveResponse:(NSURLResponse *)response 113 | completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler; 114 | { 115 | if (dataTask.state == NSURLSessionTaskStateCanceling) { 116 | return; 117 | } 118 | [self.schemeTask didReceiveResponse:response]; 119 | if ([response isKindOfClass:[NSHTTPURLResponse class]]) { 120 | NSHTTPURLResponse *httpRes = (NSHTTPURLResponse *)response; 121 | [[SSCache sharedCache] saveResponseHeaders:httpRes.allHeaderFields forRequestId:[SSUtils requestIdForRequest:dataTask.currentRequest]]; 122 | 123 | } 124 | } 125 | 126 | 127 | @end 128 | 129 | 130 | #pragma mark - 131 | 132 | @interface SSWKURLProtocol() 133 | 134 | @property (nonatomic,readwrite,copy) NSURLRequest *request; 135 | 136 | @end 137 | 138 | @implementation SSWKURLProtocol 139 | 140 | 141 | @end 142 | 143 | 144 | 145 | @interface SSWKURLHandler:NSObject 146 | 147 | @property (nonatomic,strong) Class protocolClass; 148 | @property (nonatomic,strong) NSURLSession *session; 149 | @property (nonatomic,strong) dispatch_queue_t queue; 150 | 151 | @property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue; 152 | @property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier; 153 | @property (readwrite, nonatomic, strong) NSLock *lock; 154 | 155 | @end 156 | 157 | 158 | @implementation SSWKURLHandler{ 159 | HTTPDNSCookieFilter cookieFilter; 160 | } 161 | 162 | static SSWKURLHandler *sharedInstance = nil; 163 | 164 | + (SSWKURLHandler *)sharedInstance{ 165 | static dispatch_once_t onceToken; 166 | dispatch_once(&onceToken, ^{ 167 | sharedInstance = [[self alloc]init]; 168 | sharedInstance->cookieFilter = ^BOOL(NSHTTPCookie *cookie, NSURL *URL) { 169 | if ([URL.host containsString:cookie.domain]) { 170 | return YES; 171 | } 172 | return NO; 173 | }; 174 | }); 175 | return sharedInstance; 176 | } 177 | 178 | - (instancetype)init 179 | { 180 | if (self = [super init]) { 181 | self.lock = [[NSLock alloc] init]; 182 | self.operationQueue = [[NSOperationQueue alloc] init]; 183 | self.operationQueue.maxConcurrentOperationCount = 1; 184 | self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init]; 185 | [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:@"https"]; 186 | 187 | } 188 | return self; 189 | } 190 | 191 | 192 | - (NSURLSession *)session 193 | { 194 | if (!_session) { 195 | NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; 196 | _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:self.operationQueue]; 197 | } 198 | return _session; 199 | } 200 | 201 | 202 | - (dispatch_queue_t)queue 203 | { 204 | if (!_queue) { 205 | _queue = dispatch_queue_create("SSWKURLHandler.queue", DISPATCH_QUEUE_SERIAL); 206 | _queue = dispatch_get_main_queue(); 207 | } 208 | return _queue; 209 | } 210 | 211 | - (void)webView:(WKWebView *)webView startURLSchemeTask:(id )urlSchemeTask 212 | { 213 | // if (![self.protocolClass isKindOfClass:[SSWKURLProtocol class]]) { 214 | // @throw [NSException exceptionWithName:@"SSWKURLProtolRegisterFail" reason:@"URLProtocol is not subclass of SSWKURLProtol" userInfo:@{}]; 215 | // } 216 | NSURLRequest *request = [urlSchemeTask request]; 217 | NSMutableURLRequest *mutaRequest = [request mutableCopy]; 218 | [mutaRequest setValue:[self getRequestCookieHeaderForURL:request.URL] forHTTPHeaderField:@"Cookie"]; 219 | request = [mutaRequest copy]; 220 | 221 | BOOL shouldCache = YES; 222 | if (request.HTTPMethod && ![request.HTTPMethod.uppercaseString isEqualToString:@"GET"]) { 223 | shouldCache = NO; 224 | } 225 | NSString *hasAjax = [request valueForHTTPHeaderField:@"X-Requested-With"]; 226 | if (hasAjax != nil) { 227 | shouldCache = NO; 228 | } 229 | 230 | // 231 | SSReourceItem *item = [[SSResourceLoader sharedLoader] loadResource:request]; 232 | 233 | NSDictionary *responseHeaders = [(NSHTTPURLResponse *)item.response allHeaderFields]; 234 | NSString *contentRange = responseHeaders[@"content-range"]; 235 | NSString *contentType = responseHeaders[@"Content-Type"]; 236 | if ([contentType isEqualToString:@"video/mp4"]) { 237 | shouldCache = NO; 238 | } 239 | 240 | if (item && shouldCache) { 241 | [urlSchemeTask didReceiveResponse:item.response]; 242 | if (item.data) { 243 | [urlSchemeTask didReceiveData:item.data]; 244 | } 245 | [urlSchemeTask didFinish]; 246 | 247 | }else{ 248 | NSURLSessionTask *task = [self.session dataTaskWithRequest:request]; 249 | SSWKTaskDelegate *delegate = [[SSWKTaskDelegate alloc] init]; 250 | delegate.schemeTask = urlSchemeTask; 251 | [self setDelegate:delegate forTask:task]; 252 | [task resume]; 253 | } 254 | } 255 | 256 | - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id )urlSchemeTask 257 | { 258 | dispatch_async(self.queue, ^{ 259 | urlSchemeTask.request.ss_stop = YES; 260 | }); 261 | } 262 | 263 | #pragma mark - wkwebview信任https接口 264 | - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler { 265 | if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { 266 | NSURLCredential *card = [[NSURLCredential alloc]initWithTrust:challenge.protectionSpace.serverTrust]; 267 | completionHandler(NSURLSessionAuthChallengeUseCredential, card); 268 | } 269 | } 270 | 271 | 272 | - (NSArray *)handleHeaderFields:(NSDictionary *)headerFields forURL:(NSURL *)URL { 273 | NSArray *cookieArray = [NSHTTPCookie cookiesWithResponseHeaderFields:headerFields forURL:URL]; 274 | if (cookieArray.count == 0) { 275 | return cookieArray; 276 | } 277 | if (cookieArray != nil) { 278 | NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; 279 | for (NSHTTPCookie *cookie in cookieArray) { 280 | if (cookieFilter(cookie, URL)) { 281 | [cookieStorage setCookie:cookie]; 282 | } 283 | } 284 | } 285 | return cookieArray; 286 | } 287 | 288 | - (NSString *)getRequestCookieHeaderForURL:(NSURL *)URL { 289 | NSArray *cookieArray = [self searchAppropriateCookies:URL]; 290 | if (cookieArray != nil && cookieArray.count > 0) { 291 | NSDictionary *cookieDic = [NSHTTPCookie requestHeaderFieldsWithCookies:cookieArray]; 292 | if ([cookieDic objectForKey:@"Cookie"]) { 293 | return cookieDic[@"Cookie"]; 294 | } 295 | } 296 | return nil; 297 | } 298 | 299 | - (NSArray *)searchAppropriateCookies:(NSURL *)URL { 300 | NSMutableArray *cookieArray = [NSMutableArray array]; 301 | NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; 302 | for (NSHTTPCookie *cookie in [cookieStorage cookies]) { 303 | if (cookieFilter(cookie, URL)) { 304 | [cookieArray addObject:cookie]; 305 | } 306 | } 307 | return cookieArray; 308 | } 309 | 310 | #pragma mark - delegate 311 | 312 | 313 | - (void)setDelegate:(SSWKTaskDelegate *)delegate 314 | forTask:(NSURLSessionTask *)task 315 | { 316 | [self.lock lock]; 317 | self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate; 318 | [self.lock unlock]; 319 | } 320 | 321 | - (SSWKTaskDelegate *)delegateForTask:(NSURLSessionTask *)task { 322 | NSParameterAssert(task); 323 | SSWKTaskDelegate *delegate = nil; 324 | [self.lock lock]; 325 | delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)]; 326 | [self.lock unlock]; 327 | 328 | return delegate; 329 | } 330 | 331 | - (void)removeDelegateForTask:(NSURLSessionTask *)task { 332 | NSParameterAssert(task); 333 | [self.lock lock]; 334 | [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)]; 335 | [self.lock unlock]; 336 | } 337 | 338 | - (void)URLSession:(NSURLSession *)session 339 | task:(NSURLSessionTask *)task 340 | didCompleteWithError:(NSError *)error 341 | { 342 | SSWKTaskDelegate *delegate = [self delegateForTask:task]; 343 | [delegate URLSession:session task:task didCompleteWithError:error]; 344 | } 345 | 346 | - (void)URLSession:(NSURLSession *)session 347 | dataTask:(NSURLSessionDataTask *)dataTask 348 | didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask 349 | { 350 | // SSWKTaskDelegate *delegate = [self delegateForTask:task]; 351 | SSWKTaskDelegate *delegate = [self delegateForTask:dataTask]; 352 | if (delegate) { 353 | [self removeDelegateForTask:dataTask]; 354 | [self setDelegate:delegate forTask:downloadTask]; 355 | } 356 | 357 | } 358 | 359 | - (void)URLSession:(NSURLSession *)session 360 | dataTask:(NSURLSessionDataTask *)dataTask 361 | didReceiveData:(NSData *)data 362 | { 363 | SSWKTaskDelegate *delegate = [self delegateForTask:dataTask]; 364 | [delegate URLSession:session dataTask:dataTask didReceiveData:data]; 365 | } 366 | 367 | 368 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask 369 | didReceiveResponse:(NSURLResponse *)response 370 | completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler; 371 | { 372 | SSWKTaskDelegate *delegate = [self delegateForTask:dataTask]; 373 | [delegate URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler]; 374 | 375 | NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow; 376 | if (completionHandler) { 377 | completionHandler(disposition); 378 | } 379 | } 380 | 381 | //- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task 382 | // didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge 383 | // completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler; 384 | //{ 385 | // NSLog(@""); 386 | // 387 | //} 388 | 389 | 390 | //} 391 | 392 | @end 393 | 394 | @implementation WKWebViewConfiguration(ssRegisterURLProtocol) 395 | 396 | - (void)ssRegisterURLProtocol:(Class)protocolClass 397 | { 398 | SSWKURLHandler *handler = [SSWKURLHandler sharedInstance]; 399 | handler.protocolClass = protocolClass; 400 | [self setURLSchemeHandler:handler forURLScheme:@"https"]; 401 | [self setURLSchemeHandler:handler forURLScheme:@"http"]; 402 | } 403 | 404 | @end 405 | -------------------------------------------------------------------------------- /Demo/SSWKURLDemo/SSWKURLDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D645EE3A24690218001F70E8 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D645EE3924690218001F70E8 /* AppDelegate.m */; }; 11 | D645EE3D24690218001F70E8 /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D645EE3C24690218001F70E8 /* SceneDelegate.m */; }; 12 | D645EE4024690218001F70E8 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D645EE3F24690218001F70E8 /* ViewController.m */; }; 13 | D645EE4324690218001F70E8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D645EE4124690218001F70E8 /* Main.storyboard */; }; 14 | D645EE452469021C001F70E8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D645EE442469021C001F70E8 /* Assets.xcassets */; }; 15 | D645EE482469021C001F70E8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D645EE462469021C001F70E8 /* LaunchScreen.storyboard */; }; 16 | D645EE4B2469021C001F70E8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = D645EE4A2469021C001F70E8 /* main.m */; }; 17 | D645EE6F246903F2001F70E8 /* SSWKURL.m in Sources */ = {isa = PBXBuildFile; fileRef = D645EE6D246903F2001F70E8 /* SSWKURL.m */; }; 18 | D66739FA25B8317B00DC44C8 /* SSCache.m in Sources */ = {isa = PBXBuildFile; fileRef = D66739F925B8317B00DC44C8 /* SSCache.m */; }; 19 | D66739FD25B9218F00DC44C8 /* SSUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = D66739FC25B9218F00DC44C8 /* SSUtils.m */; }; 20 | D67A2F1525B825960012A787 /* SSResourceLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = D67A2F1425B825960012A787 /* SSResourceLoader.m */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | D645EE3524690218001F70E8 /* SSWKURLDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SSWKURLDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | D645EE3824690218001F70E8 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 26 | D645EE3924690218001F70E8 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 27 | D645EE3B24690218001F70E8 /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; 28 | D645EE3C24690218001F70E8 /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; 29 | D645EE3E24690218001F70E8 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 30 | D645EE3F24690218001F70E8 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 31 | D645EE4224690218001F70E8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 32 | D645EE442469021C001F70E8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 33 | D645EE472469021C001F70E8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 34 | D645EE492469021C001F70E8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35 | D645EE4A2469021C001F70E8 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 36 | D645EE6D246903F2001F70E8 /* SSWKURL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSWKURL.m; sourceTree = ""; }; 37 | D645EE6E246903F2001F70E8 /* SSWKURL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSWKURL.h; sourceTree = ""; }; 38 | D66739F825B8317B00DC44C8 /* SSCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SSCache.h; sourceTree = ""; }; 39 | D66739F925B8317B00DC44C8 /* SSCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SSCache.m; sourceTree = ""; }; 40 | D66739FB25B9218F00DC44C8 /* SSUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SSUtils.h; sourceTree = ""; }; 41 | D66739FC25B9218F00DC44C8 /* SSUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SSUtils.m; sourceTree = ""; }; 42 | D67A2F1325B825960012A787 /* SSResourceLoader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SSResourceLoader.h; sourceTree = ""; }; 43 | D67A2F1425B825960012A787 /* SSResourceLoader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SSResourceLoader.m; sourceTree = ""; }; 44 | /* End PBXFileReference section */ 45 | 46 | /* Begin PBXFrameworksBuildPhase section */ 47 | D645EE3224690218001F70E8 /* Frameworks */ = { 48 | isa = PBXFrameworksBuildPhase; 49 | buildActionMask = 2147483647; 50 | files = ( 51 | ); 52 | runOnlyForDeploymentPostprocessing = 0; 53 | }; 54 | /* End PBXFrameworksBuildPhase section */ 55 | 56 | /* Begin PBXGroup section */ 57 | D645EE2C24690218001F70E8 = { 58 | isa = PBXGroup; 59 | children = ( 60 | D645EE3724690218001F70E8 /* SSWKURLDemo */, 61 | D645EE3624690218001F70E8 /* Products */, 62 | ); 63 | sourceTree = ""; 64 | }; 65 | D645EE3624690218001F70E8 /* Products */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | D645EE3524690218001F70E8 /* SSWKURLDemo.app */, 69 | ); 70 | name = Products; 71 | sourceTree = ""; 72 | }; 73 | D645EE3724690218001F70E8 /* SSWKURLDemo */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | D645EE6C246903F2001F70E8 /* SSWKURL */, 77 | D645EE3824690218001F70E8 /* AppDelegate.h */, 78 | D645EE3924690218001F70E8 /* AppDelegate.m */, 79 | D645EE3B24690218001F70E8 /* SceneDelegate.h */, 80 | D645EE3C24690218001F70E8 /* SceneDelegate.m */, 81 | D645EE3E24690218001F70E8 /* ViewController.h */, 82 | D645EE3F24690218001F70E8 /* ViewController.m */, 83 | D645EE4124690218001F70E8 /* Main.storyboard */, 84 | D645EE442469021C001F70E8 /* Assets.xcassets */, 85 | D645EE462469021C001F70E8 /* LaunchScreen.storyboard */, 86 | D645EE492469021C001F70E8 /* Info.plist */, 87 | D645EE4A2469021C001F70E8 /* main.m */, 88 | ); 89 | path = SSWKURLDemo; 90 | sourceTree = ""; 91 | }; 92 | D645EE6C246903F2001F70E8 /* SSWKURL */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | D645EE6D246903F2001F70E8 /* SSWKURL.m */, 96 | D645EE6E246903F2001F70E8 /* SSWKURL.h */, 97 | D67A2F1325B825960012A787 /* SSResourceLoader.h */, 98 | D67A2F1425B825960012A787 /* SSResourceLoader.m */, 99 | D66739F825B8317B00DC44C8 /* SSCache.h */, 100 | D66739F925B8317B00DC44C8 /* SSCache.m */, 101 | D66739FB25B9218F00DC44C8 /* SSUtils.h */, 102 | D66739FC25B9218F00DC44C8 /* SSUtils.m */, 103 | ); 104 | name = SSWKURL; 105 | path = ../../../SSWKURL; 106 | sourceTree = ""; 107 | }; 108 | /* End PBXGroup section */ 109 | 110 | /* Begin PBXNativeTarget section */ 111 | D645EE3424690218001F70E8 /* SSWKURLDemo */ = { 112 | isa = PBXNativeTarget; 113 | buildConfigurationList = D645EE4E2469021C001F70E8 /* Build configuration list for PBXNativeTarget "SSWKURLDemo" */; 114 | buildPhases = ( 115 | D645EE3124690218001F70E8 /* Sources */, 116 | D645EE3224690218001F70E8 /* Frameworks */, 117 | D645EE3324690218001F70E8 /* Resources */, 118 | ); 119 | buildRules = ( 120 | ); 121 | dependencies = ( 122 | ); 123 | name = SSWKURLDemo; 124 | productName = SSWKURLDemo; 125 | productReference = D645EE3524690218001F70E8 /* SSWKURLDemo.app */; 126 | productType = "com.apple.product-type.application"; 127 | }; 128 | /* End PBXNativeTarget section */ 129 | 130 | /* Begin PBXProject section */ 131 | D645EE2D24690218001F70E8 /* Project object */ = { 132 | isa = PBXProject; 133 | attributes = { 134 | LastUpgradeCheck = 1140; 135 | ORGANIZATIONNAME = sgcy; 136 | TargetAttributes = { 137 | D645EE3424690218001F70E8 = { 138 | CreatedOnToolsVersion = 11.4; 139 | }; 140 | }; 141 | }; 142 | buildConfigurationList = D645EE3024690218001F70E8 /* Build configuration list for PBXProject "SSWKURLDemo" */; 143 | compatibilityVersion = "Xcode 9.3"; 144 | developmentRegion = en; 145 | hasScannedForEncodings = 0; 146 | knownRegions = ( 147 | en, 148 | Base, 149 | ); 150 | mainGroup = D645EE2C24690218001F70E8; 151 | productRefGroup = D645EE3624690218001F70E8 /* Products */; 152 | projectDirPath = ""; 153 | projectRoot = ""; 154 | targets = ( 155 | D645EE3424690218001F70E8 /* SSWKURLDemo */, 156 | ); 157 | }; 158 | /* End PBXProject section */ 159 | 160 | /* Begin PBXResourcesBuildPhase section */ 161 | D645EE3324690218001F70E8 /* Resources */ = { 162 | isa = PBXResourcesBuildPhase; 163 | buildActionMask = 2147483647; 164 | files = ( 165 | D645EE482469021C001F70E8 /* LaunchScreen.storyboard in Resources */, 166 | D645EE452469021C001F70E8 /* Assets.xcassets in Resources */, 167 | D645EE4324690218001F70E8 /* Main.storyboard in Resources */, 168 | ); 169 | runOnlyForDeploymentPostprocessing = 0; 170 | }; 171 | /* End PBXResourcesBuildPhase section */ 172 | 173 | /* Begin PBXSourcesBuildPhase section */ 174 | D645EE3124690218001F70E8 /* Sources */ = { 175 | isa = PBXSourcesBuildPhase; 176 | buildActionMask = 2147483647; 177 | files = ( 178 | D645EE4024690218001F70E8 /* ViewController.m in Sources */, 179 | D645EE3A24690218001F70E8 /* AppDelegate.m in Sources */, 180 | D67A2F1525B825960012A787 /* SSResourceLoader.m in Sources */, 181 | D645EE4B2469021C001F70E8 /* main.m in Sources */, 182 | D645EE6F246903F2001F70E8 /* SSWKURL.m in Sources */, 183 | D66739FD25B9218F00DC44C8 /* SSUtils.m in Sources */, 184 | D66739FA25B8317B00DC44C8 /* SSCache.m in Sources */, 185 | D645EE3D24690218001F70E8 /* SceneDelegate.m in Sources */, 186 | ); 187 | runOnlyForDeploymentPostprocessing = 0; 188 | }; 189 | /* End PBXSourcesBuildPhase section */ 190 | 191 | /* Begin PBXVariantGroup section */ 192 | D645EE4124690218001F70E8 /* Main.storyboard */ = { 193 | isa = PBXVariantGroup; 194 | children = ( 195 | D645EE4224690218001F70E8 /* Base */, 196 | ); 197 | name = Main.storyboard; 198 | sourceTree = ""; 199 | }; 200 | D645EE462469021C001F70E8 /* LaunchScreen.storyboard */ = { 201 | isa = PBXVariantGroup; 202 | children = ( 203 | D645EE472469021C001F70E8 /* Base */, 204 | ); 205 | name = LaunchScreen.storyboard; 206 | sourceTree = ""; 207 | }; 208 | /* End PBXVariantGroup section */ 209 | 210 | /* Begin XCBuildConfiguration section */ 211 | D645EE4C2469021C001F70E8 /* Debug */ = { 212 | isa = XCBuildConfiguration; 213 | buildSettings = { 214 | ALWAYS_SEARCH_USER_PATHS = NO; 215 | CLANG_ANALYZER_NONNULL = YES; 216 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 217 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 218 | CLANG_CXX_LIBRARY = "libc++"; 219 | CLANG_ENABLE_MODULES = YES; 220 | CLANG_ENABLE_OBJC_ARC = YES; 221 | CLANG_ENABLE_OBJC_WEAK = YES; 222 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 223 | CLANG_WARN_BOOL_CONVERSION = YES; 224 | CLANG_WARN_COMMA = YES; 225 | CLANG_WARN_CONSTANT_CONVERSION = YES; 226 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 227 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 228 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 229 | CLANG_WARN_EMPTY_BODY = YES; 230 | CLANG_WARN_ENUM_CONVERSION = YES; 231 | CLANG_WARN_INFINITE_RECURSION = YES; 232 | CLANG_WARN_INT_CONVERSION = YES; 233 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 234 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 235 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 236 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 237 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 238 | CLANG_WARN_STRICT_PROTOTYPES = YES; 239 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 240 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 241 | CLANG_WARN_UNREACHABLE_CODE = YES; 242 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 243 | COPY_PHASE_STRIP = NO; 244 | DEBUG_INFORMATION_FORMAT = dwarf; 245 | ENABLE_STRICT_OBJC_MSGSEND = YES; 246 | ENABLE_TESTABILITY = YES; 247 | GCC_C_LANGUAGE_STANDARD = gnu11; 248 | GCC_DYNAMIC_NO_PIC = NO; 249 | GCC_NO_COMMON_BLOCKS = YES; 250 | GCC_OPTIMIZATION_LEVEL = 0; 251 | GCC_PREPROCESSOR_DEFINITIONS = ( 252 | "DEBUG=1", 253 | "$(inherited)", 254 | ); 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 = 13.4; 262 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 263 | MTL_FAST_MATH = YES; 264 | ONLY_ACTIVE_ARCH = YES; 265 | SDKROOT = iphoneos; 266 | }; 267 | name = Debug; 268 | }; 269 | D645EE4D2469021C001F70E8 /* Release */ = { 270 | isa = XCBuildConfiguration; 271 | buildSettings = { 272 | ALWAYS_SEARCH_USER_PATHS = NO; 273 | CLANG_ANALYZER_NONNULL = YES; 274 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 275 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 276 | CLANG_CXX_LIBRARY = "libc++"; 277 | CLANG_ENABLE_MODULES = YES; 278 | CLANG_ENABLE_OBJC_ARC = YES; 279 | CLANG_ENABLE_OBJC_WEAK = YES; 280 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 281 | CLANG_WARN_BOOL_CONVERSION = YES; 282 | CLANG_WARN_COMMA = YES; 283 | CLANG_WARN_CONSTANT_CONVERSION = YES; 284 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 285 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 286 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 287 | CLANG_WARN_EMPTY_BODY = YES; 288 | CLANG_WARN_ENUM_CONVERSION = YES; 289 | CLANG_WARN_INFINITE_RECURSION = YES; 290 | CLANG_WARN_INT_CONVERSION = YES; 291 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 292 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 293 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 294 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 295 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 296 | CLANG_WARN_STRICT_PROTOTYPES = YES; 297 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 298 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 299 | CLANG_WARN_UNREACHABLE_CODE = YES; 300 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 301 | COPY_PHASE_STRIP = NO; 302 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 303 | ENABLE_NS_ASSERTIONS = NO; 304 | ENABLE_STRICT_OBJC_MSGSEND = YES; 305 | GCC_C_LANGUAGE_STANDARD = gnu11; 306 | GCC_NO_COMMON_BLOCKS = YES; 307 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 308 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 309 | GCC_WARN_UNDECLARED_SELECTOR = YES; 310 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 311 | GCC_WARN_UNUSED_FUNCTION = YES; 312 | GCC_WARN_UNUSED_VARIABLE = YES; 313 | IPHONEOS_DEPLOYMENT_TARGET = 13.4; 314 | MTL_ENABLE_DEBUG_INFO = NO; 315 | MTL_FAST_MATH = YES; 316 | SDKROOT = iphoneos; 317 | VALIDATE_PRODUCT = YES; 318 | }; 319 | name = Release; 320 | }; 321 | D645EE4F2469021C001F70E8 /* Debug */ = { 322 | isa = XCBuildConfiguration; 323 | buildSettings = { 324 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 325 | CODE_SIGN_STYLE = Automatic; 326 | DEVELOPMENT_TEAM = 76Q3S5QZHK; 327 | INFOPLIST_FILE = SSWKURLDemo/Info.plist; 328 | LD_RUNPATH_SEARCH_PATHS = ( 329 | "$(inherited)", 330 | "@executable_path/Frameworks", 331 | ); 332 | PRODUCT_BUNDLE_IDENTIFIER = sgcy.SSWKURLDemo; 333 | PRODUCT_NAME = "$(TARGET_NAME)"; 334 | TARGETED_DEVICE_FAMILY = "1,2"; 335 | }; 336 | name = Debug; 337 | }; 338 | D645EE502469021C001F70E8 /* Release */ = { 339 | isa = XCBuildConfiguration; 340 | buildSettings = { 341 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 342 | CODE_SIGN_STYLE = Automatic; 343 | DEVELOPMENT_TEAM = 76Q3S5QZHK; 344 | INFOPLIST_FILE = SSWKURLDemo/Info.plist; 345 | LD_RUNPATH_SEARCH_PATHS = ( 346 | "$(inherited)", 347 | "@executable_path/Frameworks", 348 | ); 349 | PRODUCT_BUNDLE_IDENTIFIER = sgcy.SSWKURLDemo; 350 | PRODUCT_NAME = "$(TARGET_NAME)"; 351 | TARGETED_DEVICE_FAMILY = "1,2"; 352 | }; 353 | name = Release; 354 | }; 355 | /* End XCBuildConfiguration section */ 356 | 357 | /* Begin XCConfigurationList section */ 358 | D645EE3024690218001F70E8 /* Build configuration list for PBXProject "SSWKURLDemo" */ = { 359 | isa = XCConfigurationList; 360 | buildConfigurations = ( 361 | D645EE4C2469021C001F70E8 /* Debug */, 362 | D645EE4D2469021C001F70E8 /* Release */, 363 | ); 364 | defaultConfigurationIsVisible = 0; 365 | defaultConfigurationName = Release; 366 | }; 367 | D645EE4E2469021C001F70E8 /* Build configuration list for PBXNativeTarget "SSWKURLDemo" */ = { 368 | isa = XCConfigurationList; 369 | buildConfigurations = ( 370 | D645EE4F2469021C001F70E8 /* Debug */, 371 | D645EE502469021C001F70E8 /* Release */, 372 | ); 373 | defaultConfigurationIsVisible = 0; 374 | defaultConfigurationName = Release; 375 | }; 376 | /* End XCConfigurationList section */ 377 | }; 378 | rootObject = D645EE2D24690218001F70E8 /* Project object */; 379 | } 380 | --------------------------------------------------------------------------------