一、前言

神策分析 iOS SDK,是一款轻量级用于 iOS 端的数据采集埋点 SDK。神策分析 iOS SDK 不仅有代码埋点功能,还通过使用运行时机制(Runtime)中的相关技术实现 iOS 端的全埋点(无埋点、无码埋点、无痕埋点、自动埋点)、点击图、可视化全埋点等功能,下面将以 SDK 版本 v1.11.16 为例介绍神策分析 iOS SDK 的架构。

二、目录结构

神策分析 iOS SDK 自开源以来,从最开始满足代码埋点的需求,到后来支持全埋点、可视化全埋点,SDK 的代码不断优化,目录结构也不断发生调整。目前开源代码中存在 Example、SensorsAnalyticsSDK 两个工程,其中,Example 是一个测试工程,SensorsAnalyticsSDK 是神策分析 iOS SDK 工程,如下图所示:

Example 工程主要包含了下面几个目录:

  • SensorsData:基于 Objective-C 的 SDK 示例代码
  • SensorsDataSwift:基于 Swift 的 SDK 示例代码
  • SensorsDataExtention:App Extension 的示例代码

SensorsAnalyticsSDK 工程主要包含了下面几个目录:

  • SensorsAnalyticsSDK:神策分析 iOS SDK 的源码
  • SensorsAnalyticsExtension:App Extension 的数据管理类源码
  • SensorsAnalyticsTests:神策分析 iOS SDK 的单元测试源码

三、数据流程

上一节中对于神策分析 iOS SDK 的架构进行了详细的介绍,为了便于大家进一步理解 SDK 的架构,下面给出数据从采集到上传的流程,如图所示:

四、架构解析

4.1 简介

如上所述,神策分析 iOS SDK 是一款轻量级用于 iOS 端的数据采集埋点 SDK。而数据采集是构建数据平台的核心要素,数据采集是否丰富(完整),采集的数据是否准确,采集是否及时,采集的数据能否关联打通,都直接影响整个数据平台的应用效果。因此,神策分析 iOS SDK 采用了高效、可靠的架构来保证数据的采集。

4.2 架构图

神策分析 iOS SDK 总体架构图如下所示:

下面,我们对 SDK 架构进行详细的解释。

4.3 架构详解

4.3.1 配置模块

配置模块主要包括了初始化配置和远程配置两个功能,下面分别介绍下这两个功能:

1. 初始化配置

在引入神策分析 iOS SDK 之后,创建初始化配置项 SAConfigOptions,无特殊需求可以直接使用默认配置初始化 SDK。如果用户需要进行配置的更新,可以通过修改 SAConfigOptions 的属性。

配置项 SAConfigOptions 的说明:

  • SAConfigOptions 是个模型类,没有执行额外的逻辑
  • SAConfigOptions 禁用了 – init 和 – new 方法,不允许通过除 – initWithServerURL:launchOptions: 以外的方法初始化对象
  • SAConfigOptions 中某些属性的 set 方法对于数值进行了判断
  • SAConfigOptions 遵守了 NSCopying 协议,可执行 copy 方法来获取一个克隆对象。

2. 远程配置

远程配置通过访问对应的数据接收地址下的 config 接口,得到 disableDebugMode、disableSDK 和 autoTrackMode 这三个配置项的值,来控制相应的功能。

  1. 初始化 SDK 时可配置 SAConfigOptions 中的 disableRandomTimeRequestRemoteConfig 参数,控制是否禁用随机时间请求远程配置,目的是通过设置可分散降低 SDK 远程配置请求的频次;该值的作用如下:
    1. 为 true,则每次 App 启动后都会访问远程配置接口获取远程配置;
    2. 为 false,则会在每次 App 启动时判断是否距离上次请求已超过一个随机时间,超过了则会发送请求,并记录当次的请求时间。可以通过以下两个子参数来控制随机时间的上下限:
      1. 远程配置请求最小间隔时长 minRequestHourInterval(单位为小时,默认值 24);
      2. 远程配置请求最大间隔时长 maxRequestHourInterval(单位为小时,默认值 48);
  2.  App 启动后,SDK 会根据上面设定的规则判断是否满足请求远程配置条件,满足条件则触发远程配置请求。然后根据请求结果作出以下处理:
    1. 若请求失败,则会自动重试最多 3 次;
    2. 若请求成功,则更新配置并令之生效。
  3.  具体时间约束规则,可参考 iOS SDK 源码中 SensorsAnalyticsSDK.m 类中 shouldRequestRemoteConfig 方法。

远程配置请求流程如下图所示:

下面分别介绍下这三个配置项的取值含义:

1. disableDebugMode

  • true:关闭 debugMode
  • false:不做处理
  • 默认值为 false。

2. disableSDK

  • true:关闭 SDK
  • false:开启 SDK
  • 默认值为 false。

3. autoTrackMode

  • -1:不做任何处理
  • 0:关闭全埋点的所有事件
  • 默认值为 -1

这里需要注意的是,如果要开启全埋点中的部分事件,需要用全埋点事件对应的枚举值进行组合。

我们先来看下全埋点事件对应的枚举值:

/**
 * @abstract
 * AutoTrack 中的事件类型
 *
 * @discussion
 *   SensorsAnalyticsEventTypeAppStart - $AppStart
 *   SensorsAnalyticsEventTypeAppEnd - $AppEnd
 *   SensorsAnalyticsEventTypeAppClick - $AppClick
 *   SensorsAnalyticsEventTypeAppViewScreen - $AppViewScreen
 */
typedef NS_OPTIONS(NSInteger, SensorsAnalyticsAutoTrackEventType) {
    SensorsAnalyticsEventTypeNone      = 0,
    SensorsAnalyticsEventTypeAppStart      = 1 << 0,
    SensorsAnalyticsEventTypeAppEnd        = 1 << 1,
    SensorsAnalyticsEventTypeAppClick      = 1 << 2,
    SensorsAnalyticsEventTypeAppViewScreen = 1 << 3,
};

从代码中可以知道,全埋点事件对应的十进制枚举值分别为:

  • $AppStart:1
  • $AppEnd:2
  • $AppClick:4
  • $AppViewScreen:8

例如,开启全埋点中的 $AppStart 和 $AppClick 事件,那 autoTrackMode 的值就是 5(1 + 4 = 5)。

4.3.2 数据采集模块

数据采集模块是 SDK 的核心模块,主要负责准确、完整地采集数据。作为数据采集模块,主要提供了下面几个功能:

  • 代码埋点
  • 全埋点
  • 点击图
  • 可视化全埋点

关于这些功能的介绍,可以参考《神策分析 iOS SDK 功能介绍》,这里不再敖述。

在上述 SDK 架构图中可以知道数据采集模块中还包含了预置属性,关于预置属性的含义如下所述:一般情况下,任何用户触发的任何事件都需要携带一些最基本的信息,比如操作系统类型、操作系统版本号、运营商信息、应用程序版本号、设备厂商等,这些信息都可以由 SDK 自动采集,我们把这些由 SDK 默认自动采集的事件信息(属性)称为预置属性。

4.3.3 数据存储模块

为了最大限度地保证事件数据的准确性和及时性,会要求数据采集 SDK 尽快将事件数据同步到服务端。但在某些情况下,例如用户处于断网环境,或者根据实际需求(对用户体验要求比较高的情况)会要求只有在 Wi-Fi 环境中才会同步数据,可能会导致事件数据无法同步或者同步失败。因此,数据采集 SDK 一般采取的策略是:先把事件数据缓存在本地,待符合一定的策略之后,再去同步数据。

在 iOS 应用程序中,一般通过文件缓存或数据库缓存的方式进行本地缓存,这两种方式都可以作为数据采集 SDK 中的缓存。其中,数据库缓存一般是指使用 SQLite 数据库。

对于写入性能,SQLite 数据库优于文件。对于读取性能,可以参考 SQLite 官方的 测试结果。根据测试结果可以知道,如果单条数据小于 100 KB 时,从 SQLite 数据库中读取数据速度更快;单条数据大于 100 KB 时,从文件中读取速度更快。当然,SQLite 官方测试的环境是在 Linux 工作站上进行的,上述测试结果肯定会受到硬件和操作系统的影响。我们使用 iPhone 设备进行测试后发现,单条数据的阈值在 20 KB 左右。

因此,一般情况下,数据采集 SDK 都是使用 SQLite 数据库来存储数据,神策分析 iOS SDK 采用的也是 SQLite 数据库存储方案。通过数据库缓存事件数据,可以实现对数据的插入、查询及删除功能。

4.3.4 网络模块

在数据存储模块中,介绍了如何把事件数据存储到客户端本地。如果事件数据一直缓存在本地,是没有意义的。我们还需要把数据同步到服务端,然后再经过服务端的存储、抽取、分析和展现,才能充分发挥数据的价值。因此,网络模块主要负责如何把缓存在本地的事件数据同步给服务端。

苹果在 Foundation 框架中,为我们提供了封装好的网络请求相关的 API。但在实际的 iOS 应用程序开发中,开发人员很少会直接基于 Foundation 框架进行开发,绝大部分都会使用第三方库,例如 AFNetworking 等。作为通用的数据采集 SDK,要求尽量不依赖任何第三方库。因此,我们就需要基于 Foundation 框架进行开发。另外,同步数据功能相对比较简单,我们直接使用 Foundation 框架中的 NSURLSession 类即可满足我们的需求。

对于数据同步来说,我们需要指定一些自动数据同步的策略,一方面是为了降低用户使用 SDK 的难度和成本,另一方面更是为了确保数据的正确性、完整性和及时性。

在神策分析 iOS SDK 中,我们主要采用了下面三种自动数据同步策略:

1. 策略一:客户端本地存储的数据超过一定条数时同步数据(比如 100 条)

每次事件触发并入库后,我们检查一下已缓存的事件条数是否超过了我们定义的阈值,如果已超,则调用 – flush 方法同步数据。

2. 策略二:客户端每隔一定的时间间隔同步一次(比如每隔 15s 就同步一次)

通过开启一个定时器,每隔一定时间间隔调用一次 – flush 方法同步数据

3. 策略三:应用程序进入后台时尽可能同步本地缓存的所有数据

应用程序一旦进入后台,下次什么时候启动就不得而知了。因此,每当应用程序进入后台的时候,我们都要尝试同步数据,最大限度的保证数据的完整性。

需要注意的是,上述三种策略是自动数据同步的策略,如果需要手动触发数据同步,可以直接调用 – flush 方法同步数据。

4.3.5 辅助工具

神策分析 iOS SDK 提供了许多埋点相关的工具类,例如 Gzip 压缩、Object 转成 Json 字符串等。关于这些辅助工具的功能和实现就不逐一介绍了,具体可以参考 SDK 的源码。

4.3.6 日志服务

在 SDK 的使用过程中可能会遇到各种各样的问题,通过控制台输出的日志可以帮助我们迅速排查问题。扫码打开调试模式后,SDK 同时自动开启日志输出功能;也可在测试阶段通过 – enableLog: 接口打开 SDK 日志输出功能。

SDK 日志输出功能开启后,在 IDE(如 Xcode)控制台中筛选 SALog 关键词,可以看到埋点触发和数据上报相关的日志,如下所示:

  • 埋点事件触发成功时,SDK 会输出【track event】开头的事件数据
  • 埋点事件触发失败时,SDK 会输出相应的错误原因
  • 事件数据上报成功时,SDK 会输出【valid message】字段开头的事件数据
  • 事件数据上报失败时,SDK 会输出【invalid message】字段开头的事件数据并输出错误原因

例如,下图标识了一个事件数据上报成功的日志:

五、总结

本文围绕神策分析 iOS SDK 的架构进行了全面的介绍,旨在让大家对于 SDK 的架构有一个清晰的了解。关于 SDK 的具体技术实现等相关知识,会在后续的文章中逐步向大家介绍。