You need to enable JavaScript to run this app.
导航
WKWebView
最近更新时间:2024.06.11 14:04:52首次发布时间:2023.03.22 20:49:16

方案描述

如果您的 app 使用了 WKWebView,您可以使用以下方案拦截 WKWebView 中的请求。

方案一:使用 iOS 运行时函数替换框架 RSSwizzle

注意

  • 仅 iOS 11 及以后版本支持 RSSwizzle。
  • RSSwizzle 是一个第三方开源项目。如果您使用该框架,您需要自行评估和承担相关风险。

RSSwizzle 是一个 iOS 运行时函数替换框架,可以在运行时动态修改类的方法实现。RSSwizzle 可以在运行时动态地进行方法交换,以替换系统或第三方库的某些方法,而不需要修改源码。

方案二:使用 WKWebView 的私有 API

警告

使用私有 API 存在审核风险。您需要谨慎考虑。

  1. 通过 WKWebView 的私有 API 注册 scheme,保证 NSURLProtocol 可以拦截 WKWebView 中的请求。
  2. 根据您使用的网络库类型创建请求。
  3. 使用 WKWebView.loadRequest 加载请求。

前提条件

警告

对于没有在控制台添加的域名,HTTPDNS 服务端的解析会失败,您只能获得 Local DNS 服务器的解析结果。参见 添加需要解析的域名了解如何添加域名。

实现步骤

注意

为了演示需要,示例代码仅提供了集成方案中最基本的逻辑。移动解析 HTTPDNS 仅保证 HTTPDNS SDK 本身的 可用性。在生产环境下,您需要自行保证集成方案的健壮性。

  1. 使用 iOS 运行时函数替换框架 RSSwizzle 或 WKWebView 的私有 API 拦截 WKWebView 中的请求。

    • 方案一:您自定义一个 SchemeHandler,然后通过 RSSwizzle 实现请求拦截。

      // 自定义一个 SchemeHandler
      @implementation TTWebviewSchemeHandler
      
      - (void)webView:(nonnull WKWebView *)webView startURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask API_AVAILABLE(ios(11.0)) {
          NSURLRequest* request = [NSURLRequest requestWithURL:urlSchemeTask.request.URL];
          NSURLSession* session = [TTDnsSdkConfig sharedInstance].myHttpDnsSession;
          [[session dataTaskWithRequest:request completionHandler:^(NSData* _Nullable data, NSURLResponse* _Nullable response, NSError* _Nullable error) {
              NSString* errorMsg = [[error description] substringToIndex:100];
              if (!error) {
                  NSLog(@"TTWebviewSchemeHandler hook success");
                  NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
                  [urlSchemeTask didReceiveResponse:response];
                  [urlSchemeTask didReceiveData:data];
                  [urlSchemeTask didFinish];
              } else {
                  NSLog(@"TTWebviewSchemeHandler hook error");
              }
          }] resume];
      }
      
      - (void)webView:(nonnull WKWebView *)webView stopURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask API_AVAILABLE(ios(11.0)){
          NSLog(@"stopURLSchemeTask host %@",urlSchemeTask.request.URL.host);
      }
      @end
      
      // 通过 RSSwizzle 实现请求拦截
      WKWebViewConfiguration *config = [WKWebViewConfiguration new];
      RSSwizzleClassMethod(WKWebView,
                @selector(handlesURLScheme:),
                RSSWReturnType(BOOL),
                RSSWArguments(NSString *name),
                RSSWReplacement(
                                  {
                                        NSLog(@" swizzled ");
                                        return NO;
                                  }));
      TTWebviewSchemeHandler* myHandler = [[TTWebviewSchemeHandler alloc] init];
      if (@available(iOS 11.0, *)) {
          [config setURLSchemeHandler:myHandler forURLScheme: @"https"];
      } else {
             // Fallback on earlier versions
      }
      
    • 方案二:您在 WKBrowsingContextController 中通过 registerSchemeForCustomProtocol 注册 scheme。然后,您使用 performSelector 匹配 NSURLProtocol。 这样,您就可以通过 NSURLProtocol 拦截请求。

    WKWebViewConfiguration *config = [WKWebViewConfiguration new];
    //注册scheme后,sharedSession protocol会拦截
    Class cls = NSClassFromString(@"WKBrowsingContextController");
    SEL sel = NSSelectorFromString(@"registerSchemeForCustomProtocol:");
    if ([cls respondsToSelector:sel]) {
       // 通过http和https的请求,同理可通过其他的Scheme 但是要满足ULR Loading System
       [cls performSelector:sel withObject:@"http"];
       [cls performSelector:sel withObject:@"https"];
       }
                
    WKWebViewConfiguration * config = [[WKWebViewConfiguration alloc] init];
    
  2. 根据您的网络库场景,参考 网络库(自定义 NSURLProtocol)通过 NSURLSession 创建请求。

    NSURL* url = [NSURL URLWithString:self.urlInput.text];
    NSURLRequest* request = [NSURLRequest requestWithURL:url];
    
  3. 使用 WKWebView.loadRequest 加载请求。

    [self.wkWebView loadRequest:request];