关于前端:iOS-SDK-的-H5-打通方案演进-数据采集

53次阅读

共计 14608 个字符,预计需要花费 37 分钟才能阅读完成。

一、前言

在介绍 iOS SDK 的 H5 买通计划之前,咱们先理解一下什么是 App 与 H5 买通。

所谓“买通”,是指 H5 集成 JavaScript 数据采集 SDK 后,H5 触发的事件不是间接同步给服务端,而是先发给 App 端的数据采集 SDK,经 App 端数据采集 SDK 二次加工解决后缓存到本地,再通过适合的上传策略同步到服务端。

二、APP 与 H5 买通的起因

对于 App 与 H5 买通的起因,咱们次要是从以下几个角度思考:

2.1 数据失落率

在业界,App 端采集数据的失落率个别在 1% 左右,而 H5 采集数据的失落率个别在 5% 左右(次要是因为缓存、网络或切换页面等起因)。

因而,如果 App 与 H5 买通,H5 触发的所有事件都能够先发给 App 端数据采集 SDK,通过 App 端二次加工解决后存入本地缓存,在合乎特定策略之后再进行数据同步,即可把数据失落率由 5% 降到 1% 左右。

2.2 数据准确性

家喻户晓,H5 无奈间接获取设施相干的信息,只能通过解析 UserAgent 值获取到无限的信息。而解析 UserAgent 值,至多会面临以下两个问题:

(1)有些信息通过解析 UserAgent 值基本获取不到,比方应用程序的版本号、设施具体型号等;

(2)有些信息通过解析 UserAgent 值能够获取到,但内容可能不正确。

如果 App 与 H5 买通,由 App 端数据采集 SDK 补充这些信息,即可确保事件信息的准确性和完整性。

2.3 用户标识

如果用户在 App 端注册或登录之前应用咱们的产品,咱们个别都是应用匿名 ID 来标识用户。而 App 与 H5 标识匿名用户的规定不一样(iOS 个别应用 IDFA 或 IDFV,H5 个别应用 Cookie),从而就会导致一个用户应用了咱们的产品,后果产生了两个匿名用户。如果 App 与 H5 买通,就能够将两个匿名 ID 做归一化解决(以 App 端匿名 ID 为准)。

2.4 根底性能

基于 App 与 H5 买通,能够实现诸如 App 内嵌 H5 可视化全埋点、App 内嵌 H5 弹框等更加高级的性能。

介绍完买通的起因之后,咱们来看下 App 与 H5 如何进行买通。

三、买通计划演进

对于 App 与 H5 的买通,已经始终是咱们的一个痛点,为此咱们也做了继续地摸索和迭代。在这个过程中咱们踩了很多坑,也积攒了一些教训,当初将依照计划演进的程序为大家一一介绍这几种形式,并剖析其背景、实现和优缺点。

3.1 计划一

3.1.1 背景和原理

iOS SDK 从 v1.6.8 版本开始反对 App 与 H5 买通,也就是买通计划的原始版本。

家喻户晓,在理论开发中,一个技术计划的应用,都是为了解决某些问题。在晚期的事件剖析中,H5 的匿名 ID(个别应用 Cookie)常常变动,并且与 App 端(iOS 个别应用 IDFA 或 IDFV)不同。因而,导致一个 App 用户在内嵌 H5 页面的行为序列无奈和原生页面的行为分割起来,为业务剖析造成很大困扰。然而,如果将 App 端的匿名 ID 传给 H5,就能保障 App 内嵌 H5 页面应用的匿名 ID 和原生页面统一,从而解决上述问题。

原理:

在 WebView 加载 H5 页面实现后,iOS SDK 调用 JS 办法将匿名 ID 传给 JS SDK,JS SDK 采集的埋点数据就能够应用 App 的匿名 ID,从而使得 App 进入 H5 页面前后的用户行为序列精确关联。次要流程如图 3-1 所示:


图 3-1 买通计划一的流程图

3.1.2 具体实现

具体实现次要分为上面几个步骤:

(1)从图 3-1 的买通计划流程能够晓得,为了保障 JS SDK 曾经加载实现,iOS SDK 须要在 H5 页面加载实现后能力调用 JS 办法传值。那么监听 H5 页面加载实现的机会,便成了买通的要害。

对于 UIWebView 而言,咱们晓得从 UIWebViewDelegate 的代理办法中能够获取 UIWebView 加载 H5 页面的进度,UIWebViewDelegate 的代理办法如下:

API_UNAVAILABLE(tvos) @protocol UIWebViewDelegate <NSObject>

@optional
/// 是否开始加载 H5

  • (BOOL)webView:(UIWebView )webView shouldStartLoadWithRequest:(NSURLRequest )request navigationType:(UIWebViewNavigationType)navigationType API_DEPRECATED(“No longer supported.”, ios(2.0, 12.0));

/// 曾经开始加载 H5

  • (void)webViewDidStartLoad:(UIWebView *)webView API_DEPRECATED(“No longer supported.”, ios(2.0, 12.0));

/// H5 页面加载实现

  • (void)webViewDidFinishLoad:(UIWebView *)webView API_DEPRECATED(“No longer supported.”, ios(2.0, 12.0));

/// H5 页面加载失败

  • (void)webView:(UIWebView )webView didFailLoadWithError:(NSError )error API_DEPRECATED(“No longer supported.”, ios(2.0, 12.0));

@end
因而,咱们在 UIWebView 的 H5 页面加载实现调用 JS 办法,只须要实现 – webViewDidFinishLoad: 办法即可,如下所示:

UIWebView

  • (void)webViewDidFinishLoad:(UIWebView *)webView {
    [[SensorsAnalyticsSDK sharedInstance] showUpWebView:webView];
    }
    对于 WKWebView 而言,查阅 Apple 的 WKWebView 相干 API 文档没有适合的代理办法去监听 H5 页面加载实现,不过发现了 loading 这个属性,阐明如下:

/*! @abstract A Boolean value indicating whether the view is currently
loading content.
@discussion @link WKWebView @/link is key-value observing (KVO) compliant
for this property.
*/
@property (nonatomic, readonly, getter=isLoading) BOOL loading;
loading 属性示意以后页面是否正在加载,如果 loading = NO(即页面不再加载了),示意 H5 页面曾经加载实现。因而,咱们应用 KVO 监听 loading 的属性变动,就能够晓得 WKWebView 加载 H5 页面是否实现,具体实现如下所示:

WKWebView

// 通过观察者监听 WKWebView 加载进度
[_webView addObserver:self forKeyPath:@”loading” options:NSKeyValueObservingOptionNew context:nil];

  • (void)observeValueForKeyPath:(NSString )keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> )change context:(void *)context {
    if (!_webView.loading) {

      [[SensorsAnalyticsSDK sharedInstance] showUpWebView:_webView];

    }
    }
    (2)监听 H5 页面加载实现后,即示意 JS SDK 曾经准备就绪。此时应用 WebView 调用 JS 办法,将 App 端应用的匿名 ID 传给 JS SDK 即可,具体实现如下所示:

  • (void)showUpWebView:(id)webView {
    NSString *js = [NSString stringWithFormat:@”sensorsdata_app_js_bridge_call_js(‘%@’)”, [self webViewJavascriptBridgeCallbackInfo]];
    if ([webView isKindOfClass:[UIWebView class]]) {//UIWebView

      // UIWebView 调用 JS 办法
      [webView stringByEvaluatingJavaScriptFromString:js];

    } else if([webView isKindOfClass:[WKWebView class]]) {//WKWebView

      // WKWebView 调用 JS 办法
      [webView evaluateJavaScript:js completionHandler:^(id _Nullable response, NSError * _Nullable error) {NSLog(@"response: %@ error: %@", response, error);
      }];

    }
    }
    (3)JS SDK 后续采集的所有事件,都应用 iOS SDK 传递的匿名 ID 作为以后的用户标识,具体实现如下所示:

window.sensorsdata_app_js_bridge_call_js = function(data) {

setAppInfo(data);

};

function setAppInfo(data) {
// 解析 $type 和 $distinct_id,拼接数据
}

3.1.3 优缺点

长处:

因为 iOS SDK 不必解决 JS 端的数据,只须要传大量信息到 JS 端,对 iOS SDK 的侵入较小;
能够同时兼容 UIWebView 和 WKWebView,满足更多客户需要。
毛病:

H5 产生的埋点数据,持续由 JS SDK 上报到服务端,数据失落的危险较大;
如果 WebView 加载实现,JS SDK 尚未加载,会导致买通失败;
只有开启买通,iOS SDK 就会把 App 端的匿名 ID 发给 JS SDK。因为不会辨别我的项目,这样可能会导致数据错乱。例如,客户 A 的 App 内嵌了客户 B 的 H5,可能导致客户 B 的 H5 页面应用了客户 A 的匿名 ID,而后数据还是发到了客户 B 的服务端,导致客户 B 的用户数据错乱;
对于客户来说买通的集成比较复杂。例如,如果客户 App 我的项目中应用了多个 UIWebView 或 WKWebView,须要在多处实现协定办法并调用 SDK 接口,接入的工作量会比拟大。

3.2 计划二

3.2.1 背景和原理

为了减小加载 JS SDK 对客户 H5 页面的影响,前期 JS SDK 反对异步加载(即 H5 页面加载实现后才开始加载 JS SDK)。这就导致一个问题:可能 WebView 加载实现时,JS SDK 尚未加载,此时计划一会买通失败。为了解决这个问题,咱们开发出了计划二。

因为 JS SDK 反对异步加载,所以 iOS SDK 不再依赖于 WebView 加载实现的状态去判断 JS SDK 是否加载实现。因而,计划二的关键问题是:iOS SDK 应该什么机会去调用 JS 的办法发送 App 端的数据,即 iOS SDK 怎么能力晓得 JS SDK 加载实现?

原理:

在 JS SDK 加载实现并初始化后,发送一个 iframe(sensorsanalytics://getAppInfo)申请。而后在 App 的 WebView 的协定办法中拦挡 H5 的页面申请。如果呈现 sensorsanalytics://getAppInfo 这个申请,即认为 JS SDK 加载实现。此时,调用 JS 的办法发送 App 端的数据,就能保障买通胜利。次要流程如图 3-2 所示:

图 3-2 买通计划二的流程图

3.2.2 具体实现

具体实现次要分为上面几个步骤:

(1)JS SDK 初始化后发送 iframe 申请:

function calliOS() {

if (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream) {var iframe = document.createElement("iframe");
    iframe.setAttribute("src", "sensorsanalytics://getAppInfo");
    document.documentElement.appendChild(iframe);
    iframe.parentNode.removeChild(iframe);
    iframe = null;
}

}
(2)iOS SDK 拦挡申请。对于拦挡 H5 页面中的申请,在 UIWebView 和 WKWebView 实现略有不同,别离示例如下。

对于 UIWebView 拦挡申请,须要实现 UIWebViewDelegate 中的如下代理办法:

  • (BOOL)webView:(UIWebView )webView shouldStartLoadWithRequest:(NSURLRequest )request navigationType:(UIWebViewNavigationType)navigationType {
    if ([[SensorsAnalyticsSDK sharedInstance] showUpWebView:webView WithRequest:request]) {

      return NO;

    }
    return YES;
    }
    对于 WKWebView 拦挡申请,须要实现 WKNavigationDelegate 中的如下代理办法:

  • (void)webView:(WKWebView )webView decidePolicyForNavigationAction:(WKNavigationAction )navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    if ([[SensorsAnalyticsSDK sharedInstance] showUpWebView:webView WithRequest:navigationAction.request]) {

      decisionHandler(WKNavigationActionPolicyCancel);
      return;

    }
    decisionHandler(WKNavigationActionPolicyAllow);
    }
    (3)判断 sensorsanalytics://getAppInfo 申请,调用 JS 办法发送数据:

  • (BOOL)showUpWebView:(id)webView WithRequest:(NSURLRequest *)request {
    NSString* jsonString = [self webViewJavascriptBridgeCallbackInfo];
    NSString *scheme = @”sensorsanalytics://getAppInfo”;
    NSString *js = [NSString stringWithFormat:@”sensorsdata_app_js_bridge_call_js(‘%@’)”, jsonString];

    // 判断零碎是否反对 WKWebView
    Class wkWebViewClass = NSClassFromString(@”WKWebView”);

    if ([webView isKindOfClass:[UIWebView class]]) {//UIWebView

      // 判断以后 request 是否为 JS SDK 发送的 iframe 申请
      if ([request.URL.absoluteString rangeOfString:scheme].location != NSNotFound) {[webView stringByEvaluatingJavaScriptFromString:js];
          return YES;
      }
      return NO;

    } else if(wkWebViewClass && [webView isKindOfClass:wkWebViewClass]) {//WKWebView

      // WKWebView 逻辑和上述相似 

    } else{

      SADebug(@"showUpWebView: not UIWebView or WKWebView");
      return NO;

    }
    }
    (4)JS SDK 采集的事件,都应用 iOS SDK 发送的匿名 ID 作为以后用户的标识。

3.2.3 优缺点

长处:

这种买通计划,因为 iOS SDK 不必解决 JS 端的数据,只须要传大量信息到 JS,对 iOS SDK 的侵入较小;
能够同时兼容 UIWebView 和 WKWebView,满足更多客户须要;
反对 JS SDK 在 H5 页面异步加载状况下的 App 与 H5 买通。
毛病:

H5 产生的埋点数据,持续由 JS SDK 上报到服务端,数据失落的危险较大;
只有开启买通,JS SDK 就会把 App 的匿名 ID 发给 JS SDK。因为不会辨别我的项目,这样可能会导致数据错乱。例如,客户 A 的 App 内嵌了客户 B 的 H5,可能导致客户 B 的 H5 页面应用了客户 A 的匿名 ID,而后数据还是发到了客户 B 的服务端,导致客户 B 的用户数据错乱;
对于客户来说买通的集成比较复杂。例如,客户 App 我的项目中应用了多个 UIWebView 或 WKWebView,须要在多处实现协定办法并调用 SDK 接口,接入的工作量会比拟大。

3.3 计划三

3.3.1 背景和原理

对于上述两种计划,对性能影响较大的两个问题是:

App 与 H5 买通后,App 发送匿名 ID 给 JS SDK 时不辨别我的项目,对其余客户数据造成较大影响;
H5 的埋点数据通过 JS SDK 上报,数据失落的危险较大,并且局部数据无奈采集。
为了解决上述两个问题,咱们对 App 与 H5 买通计划做了较大的批改,从而推出了计划三:

为了解决上述第一个问题,咱们独自减少了开启买通的接口。如果客户调用并开启买通,iOS SDK 会批改以后 App 环境的 UA 值,拼接以后接入 SA 的 project(我的项目名)和 host(域名)。这样 JS SDK 能够判断以后 H5 页面是否须要买通,并且还能够校验是否为同一个我的项目;
为了解决上述第二个问题,咱们在 JS SDK 减少是否须要买通的判断,如果须要买通就将 H5 产生的埋点数据发送到 App,由 iOS SDK 解决并缓存到本地,而后依据适合的策略上传到服务端。
原理:

iOS SDK 批改 UA 来标记以后 App 集成神策的我的项目信息,JS SDK 依据 UA 值判断是否须要买通。如果须要买通,JS SDK 将埋点数据发往 App,iOS SDK 解析并解决 JS SDK 端产生的埋点数据,再依据适合的策略上报到服务端。次要流程如图 3-3 所示:

图 3-3 买通计划三的流程图

3.3.2 实现

具体实现次要分为上面几个步骤:

(1)iOS SDK 开启买通的接口,默认校验以后数据接管地址中的我的项目。也就是将神策数据接管地址中的 host 和 project 写入以后的 UA 环境,以便 JS SDK 解析:

// 开启买通,默认校验 serverURL

  • (void)addWebViewUserAgentSensorsDataFlag {
    dispatch_async(dispatch_get_main_queue(), ^{

       
      SAServerUrl *url = [[SAServerUrl alloc] initWithUrl:self.serverURL];
      UIWebView *tempWebView = [[UIWebView alloc] initWithFrame:CGRectZero];
      // 获取 UA
      NSString *userAgent = [tempWebView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
      if ([userAgent rangeOfString:@"sa-sdk-ios"].location != NSNotFound) {return;}
      // 拼接以后 SA 我的项目中的 host 和 project
      userAgent = [userAgent stringByAppendingString:[NSString stringWithFormat: @"/sa-sdk-ios/sensors-verify/%@?%@", url.host, url.project]];
       
      // 写入 UA
      NSDictionary *dictionnary = [[NSDictionary alloc] initWithObjectsAndKeys:userAgent, @"UserAgent", nil];
      [[NSUserDefaults standardUserDefaults] registerDefaults:dictionnary];
      [[NSUserDefaults standardUserDefaults] synchronize];

    });
    }
    (2)JS SDK 获取以后环境的 UA 值,如果判断以后环境为 App 内嵌 H5 并且开启买通(UA 中蕴含 /sa-sdk-ios/sensors-verify),就会解析 UA 中的 host 和 project。如果依据 host 和 project 判断以后 H5 与 App 集成的是同一个神策我的项目,则示意须要进行 App 与 H5 买通。此时 JS SDK 触发 iframe 申请,发送埋点数据到 App:

JS SDK

if (sd.bridge.iOS_UA_bridge()) {
iframe = document.createElement(‘iframe’);
iframe.setAttribute(‘src’, ‘sensorsanalytics://trackEvent?event=’ + encodeURIComponent(JSON.stringify(_.extend({

 server_url: sd.para.server_url

}, originData))));
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
}
(3)iOS SDK 拦挡 WebView 申请,解析 JS 埋点数据并进行解决和缓存(此处以 UIWebView 的实现为例,对于 WKWebView 拦挡申请的计划,上文已有介绍,此处不再赘述):

WebView 中拦挡申请

  • (BOOL)webView:(UIWebView )webView shouldStartLoadWithRequest:(NSURLRequest )request navigationType:(UIWebViewNavigationType)navigationType {
    if ([[SensorsAnalyticsSDK sharedInstance] showUpWebView:webView WithRequest:request]) {

      return NO;

    }
    return YES;
    }
    解析 JS SDK 触发 iframe 申请中的 URL 参数,从而获取 JS SDK 发送的埋点数据:

  • (BOOL)showUpWebView:(id)webView WithRequest:(NSURLRequest *)request {
    / 其余合法性判断 /
    NSString *urlString = request.URL.absoluteString;
    if (!urlString) {

      return YES;

    }
    if (![urlString rangeOfString:@”sensorsanalytics://trackEvent”].length) {

      return NO;

    }
    // 解析 url 中的事件数据
    NSDictionary *paramsDic = [SANetwork queryItemsWithURLString:urlString];
    if ([paramsDic count] > 0) {

      NSString *eventInfo = paramsDic[@"event"];
      if (eventInfo) {NSString *encodedString = [eventInfo stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
          [self trackFromH5WithEvent:encodedString];
      }

    }
    }

    3.3.3 优缺点

长处:

H5 产生的事件数据,iOS SDK 先通过了加工:应用 App 的匿名 ID,减少设施信息、网络和运营商信息等,从而进步了数据采集的准确性和完整性,保障了匿名 ID 的统一,为后续的行为序列剖析提供了牢靠的数据根底;
通过 iOS SDK 缓存并上报,很大水平上升高了数据失落的危险;
JS SDK 解析 H5 环境的 UA 标识判断是否买通,防止了 App 应用其余客户的 H5 可能存在的数据失落或产生脏数据问题。
毛病:

对于每个 WebView,都须要在协定办法中调用接口拦挡申请,如果一个我的项目有多个 WebView,集成工作绝对繁琐;
如果客户我的项目禁用 UIWebView,WKWebView 目前只反对异步获取 UA 再进行批改,可能会导致两个问题:如果客户也须要批改 UA,可能会导致买通失败或者客户批改 UA 失败;如果 App 首页加载 WKWebView,这个页面的 H5 会买通失败。
JS SDK 发送的 iframe 申请,在客户 App 环境中,可能被误判为非法申请并进行拦挡,导致买通失败。

3.4 计划四

3.4.1 背景和原理

随着客户数量的疾速减少,越来越简单的应用场景和客户环境,给咱们的 App 与 H5 买通计划带来新的考验。在某些简单客户环境中,一个 H5 页面可能存在于多个不同的 App 我的项目(可能是正式我的项目与测试项目,或一个团体内的多个业务线)中,各个 App 应用的神策服务器地址可能不同,并且都须要进行买通,目前上述的几种计划,都无奈满足。

同时,Apple 筹备禁用 UIWebView[1],越来越多的 App 开始从 UIWebView 迁徙到 WKWebView。然而,咱们的 App 与 H5 买通计划,在 WKWebView 买通中存在一些遗留问题,影响了客户的应用体验。

面对客户诉求和 Apple 的新规定,咱们的 App 与 H5 买通计划,也亟待再次进行优化。因而,第四版买通计划应运而生。

原理:

通过技术调研,咱们发现在 iOS 的 WKWebView 中,Apple 在 js runtime 环境里当时注入了一个

window.webkit.messageHandlers.xxxx.postMessage() 办法,咱们能够应用这个办法间接向 Native 层传值。基于这一原理,iOS 开发中 Native 与 H5 的交互能够应用全新的解决方案。iOS 曾经将 window.webkit.messageHandlers 向 H5 共享了以后的 webkit 环境,只有在 WKWebView 的 configuration.userContentController 中注入实现 WKScriptMessageHandler 协定的对象(即 JSBridge),H5 端通过调用 postMessage() 办法,即可间接向 Native 发送音讯。

为了加重客户开启 App 与 H5 买通的接入老本,计划四应用动静 swizzle 技术 hook 了 WKWebView 加载 URL 的办法。在拿到以后 WKWebView 对象后主动注入 JSBridge,从而防止在每个 WebView 中独自调用,晋升 SDK 的集成体验。

在简单的客户环境,针对不同校验策略的诉求,计划四应用了白名单策略。JS SDK 提供了接口设置 serverURL 的白名单汇合,只有 App 设置的 serverURL 蕴含在白名单中,则集成 JS SDK 的 H5 都能够买通胜利,奇妙地兼容了同一个 H5 须要在不同 App 我的项目中进行买通的场景。

计划四的次要流程如图 3-4 所示:


图 3-4 买通计划四的流程图

3.4.2 实现

具体实现次要分为上面几个步骤:

(1)开启买通后,主动执行 swizzle:

  • (void)swizzleWebViewMethod {
    static dispatch_once_t onceTokenWebView;
    dispatch_once(&onceTokenWebView, ^{

      NSError *error = NULL;
      [WKWebView sa_swizzleMethod:@selector(loadRequest:) withMethod:@selector(sensorsdata_loadRequest:) error:&error];
      /* 为了兼容不同调用形式,还须要 hook 以下三种办法,上文已做介绍,此处不再赘述:loadHTMLString:baseURL:
       if (@available(iOS 9.0, *)) {
          loadFileURL:allowingReadAccessToURL:
          loadData:MIMEType:characterEncodingName:baseURL:
       }
       */

    });
    }
    (2)WKWebView 加载 H5,调用 swizzle 的办法,注入了 SAScriptMessageHandler 对象(JavaScriptBridge)到 ScriptMessageHandler:

  • (WKNavigation )sensorsdata_loadRequest:(NSURLRequest )request {
    [[SensorsAnalyticsSDK sharedInstance] addScriptMessageHandlerWithWebView:self];
    return [self sensorsdata_loadRequest:request];
    }

// 注入 JavaScriptBridge 到 WKWebView

  • (void)addScriptMessageHandlerWithWebView:(WKWebView *)webView {
    / webView 合法性判断等解决,细节略 … /
    WKUserContentController *contentController = webView.configuration.userContentController;
    [contentController removeScriptMessageHandlerForName:SA_SCRIPT_MESSAGE_HANDLER_NAME];
    [contentController addScriptMessageHandler:[SAScriptMessageHandler sharedInstance] name:SA_SCRIPT_MESSAGE_HANDLER_NAME];

    if (![self.network.serverURL isKindOfClass:[NSURL class]] || ![self.network.serverURL absoluteString]) {

      return;

    }
    NSMutableString *javaScriptSource = [NSMutableString string];
    [javaScriptSource appendString:@”window.SensorsData_iOS_JS_Bridge = {};”];
    [javaScriptSource appendFormat:@”window.SensorsData_iOS_JS_Bridge.sensorsdata_app_server_url = ‘%@’;”, [self.network.serverURL absoluteString]];

    / 判断是否曾经被注入,避免反复注入等,细节略 … /

    // forMainFrameOnly: 标识脚本是仅应注入主框架(YES)还是注入所有框架(NO)
    WKUserScript *userScript = [[WKUserScript alloc] initWithSource:[NSString stringWithString:javaScriptSource] injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
    [contentController addUserScript:userScript];
    }
    (3)JS SDK 发送埋点数据:

if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.sensorsdataNativeTracker && window.webkit.messageHandlers.sensorsdataNativeTracker.postMessage && _.isObject(window.SensorsData_iOS_JS_Bridge) && window.SensorsData_iOS_JS_Bridge.sensorsdata_app_server_url) {

// 判断 serverURL 是否校验通过
if (sd.bridge.is_verify_success) {
    window.webkit.messageHandlers.sensorsdataNativeTracker.postMessage(JSON.stringify({
        callType: 'app_h5_track',
        data: _.extend({server_url: sd.para.server_url}, originData)
    }));
    (typeof callback == = 'function') && callback();}

}
(4)在 SAScriptMessageHandler 中接管 js 发送的埋点数据:

// 实现 WKScriptMessageHandler 协定办法

  • (void)userContentController:(WKUserContentController )userContentController didReceiveScriptMessage:(WKScriptMessage )message {

    // 获取 JS 发送的埋点数据
    NSString *body = message.body;
    /*

    1. 数据合法性校验等判断
    2. JSON 数据解析并获取埋点事件数据,再转为 JSON String
      */
      NSString *trackMessageString = [[NSString alloc] initWithData:trackMessageData encoding:NSUTF8StringEncoding];
      // 加工 js 埋点数据
      [[SensorsAnalyticsSDK sharedInstance] trackFromH5WithEvent:trackMessageString];

    }

    3.4.3 优缺点

长处:

应用 WKWebView 的相干 API 实现,绝对稳固牢靠;
一个公司的多个 APP,能够通过配置 H5 白名单,实现不同 ServerURL 的 App 都能买通 H5;
如果开启 App 与 H5 买通,只须要通过初始化配置开关设置即可,不须要对每个 WebView 反复调用,不便客户集成。
毛病:

目前通过 swizzle 计划 hook 了 WKWebView 加载 H5 的办法,如果 swizzle 逻辑呈现问题,可能导致 WKWebView 加载 H5 失败(目前测试和应用过程中均未发现);
计划只反对 WKWebView。

四、总结

通过艰巨的摸索和继续的迭代后,iOS SDK 的 H5 买通计划目前趋于稳定。当然,目前的计划只是在以后环境的最优抉择。对于当前的业务变动和技术倒退,咱们可能会面临新的挑战和难题。如果等到那一天,咱们能做的也是踊跃地迎接挑战,再次投入攻关和调研,找到适宜咱们的抉择。

这个继续摸索的过程,既是对计划的一次次变革,也是进步自我认知和积攒技术的过程。一路走来,感叹良多,想起共事常说的一句话:“做难事,必有所得。”

参考文献:

[1]ITMS-90809: Deprecated API Usage – Apple will stop accepting submissions of apps that use UIWebView APIs.https://developer.apple.com/f…

文章起源:公众号神策技术社区

正文完
 0