Appearance
27统计分析:如何架构灵活的统计分析服务,助力产品增长?
App 上架是产品发展的一个里程碑,同时也是产品长远发展的起点。那有没有什么方法能帮助我们持续地改善产品功能与用户体验,并制定产品发展的方向与目标呢?当然有!我们可以使用统计分析服务(Analytics)。
统计分析服务能为我们提供用户交互度指标(Engagement Metrics),这些指标包括用户会话的活跃度、活跃的设备类型以及用户留存率等。 有了这些指标,我们就能衡量每次发布的新功能是否能有效提高用户的交互频率,从而及时调整产品发展的方向。
市面上有许多统计分析服务,为了方便使用各种不同的服务,我们为 Moments App 架构了一套灵活的统计分析模块,同时使用 Firebase 作为例子来演示如何支持一种统计分析服务。之所以选择 Firebase,其主要原因有如下三个:
Firebase 功能齐全,除了统计分析服务以外,几乎包含了我们优化 App 所需的各种服务,例如崩溃报告、远程配置与遥控功能开关、App 分发服务、A/B 测试等;
免费版的 Firebase 足够使用,与功能相当的收费产品相比,能省下不少钱;
Firebase 配置方便,只需搭建一次就能长久使用。
配置 Firebase 服务
在讲述如何使用 Firebase 的统计分析服务前,我们先看看如何为 Moments App 配置 Firebase 的服务。
1. 创建项目
首先,我们登录到 Firebase 网站来新建一个项目。请注意,在新建的时候必须选择"Enable Google Analytics for this project"(为该项目启动统计分析服务)选项,否则将没办法使用 A/B 测试等一系列的服务。
然后在新项目里添加 App,我的做法是为开发环境、测试环境和生产环境各自添加不同的 App。下图演示了如何添加测试环境的 Internal App。
这里关键是要填写正确的 Bundle ID。你可以到各个 Target 的 xcconfig 文件里面分别找到它们的 Bundle ID,例如在 InternalTarget.xcconfig 文件里面有如下的定义:
java
PRODUCT_BUNDLE_IDENTIFIER = com.ibanimatable.moments.internal
2. 下载 Firebase 配置文件
第二步是为三个环境的 App 分别下载 Firebase 的配置文件。当我们把三个配置文件下载完毕后,为了方便管理,可以使用"GoogleService-Info-<环境名称>.plist"的命名方式来改名,然后把所有的配置文件都拖到 Moments 项目里面,如下图所示:
同时在各个 Target 的 xcconfig 文件分别添加名叫FIREBASE_CONFIG_FILENAME
的 Build Setting,如下所示:
java
FIREBASE_CONFIG_FILENAME = GoogleService-Info-Development
接着在 Build Phases 上添加"Copy Firebase Config File"步骤的配置信息,并输入下面的脚本:
java
cp "${PROJECT_DIR}/Moments/Configurations/Firebase/${FIREBASE_CONFIG_FILENAME}.plist" "${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}/GoogleService-Info.plist"
具体配置如下图所示:
有了这个步骤的配置,Xcode 在构建的过程中就会执行。因为步骤配置里的 Shell 脚本中使用了 xcconfig 里面的FIREBASE_CONFIG_FILENAME
变量,所以当我们构建不同环境的 App 时,Xcode 会自动拷贝对应的 Firebase 配置文件到不同的 App 里面,这样就使得不同的 App 能把用户事件发送到不同的统计分析数据服务,进而保证生产环境的数据不会受到污染。
3. 安装 Firebase SDK
下载完配置文件以后,下一步是安装 Firebase SDK。官方推荐的方式是使用 CocoaPods 来安装 Firebase SDK。我们只需要把 Firebase 添加到 Podfile 即可,具体代码如下:
java
def thirdparty_pods
pod 'Firebase/Analytics', '= 7.0.0'
end
target 'Moments' do
...
thirdparty_pods
...
end
我们通过pod
命令来添加统计分析服务,然后把这些 Pod 通过thirdparty_pods
函数添加到 Moments target 里面,最后重新执行bundle exec pod install
命令就能完成 Firebase SDK 的安装了。
4. 初始化 Firebase 服务
安装完 Firebase SDK 后,我们还需要在 App 里面进行初始化 Firebase 服务,只需要两步,具体代码如下:
swift
import Firebase // 1
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure() // 2
return true
}
}
第一步是通过import Firebase
语句来引入 Firebase 库,第二步是在application(_:didFinishLaunchingWithOptions:)
函数里调用FirebaseApp.configure()
函数来启动 Firebase 服务。
好了,至此我们就为 Moments App 配置好 Firebase 服务了。
统计分析模块
下面我们以 Moments App 为例子,看看如何架构与实现一个灵活的统计分析模块以及如何使用该模块。
1. 统计分析模块的架构与实现
首先,我们看一下统计分析模块的架构图,如下图所示:
这里我们从右往左看,根据依赖关系,统计分析模块由用户活动事件(Event)、事件跟踪提供者(Provider)和事件跟踪数据仓库(Repo)这三部分组成。
下面我们分别看一下它们的实现。
所有的事件类型都遵循了一个名叫TrackingEventType
的空协议(Protocol),其定义如下:
swift
protocol TrackingEventType { }
然后就可以定义该协议的字类型。根据用途的不同,我们把事件分成以下三类:
ScreenviewsTrackingEvent
,用于记录页面事件;ActionTrackingEventType
,用于记录行为事件,例如点击了某个按钮;TrackingEvent
,用于记录通用的事件。
接着我们定义了TrackingProvider
协议来发送事件。该协议定义了三个方法来分别发送不同的事件,具体定义如下:
swift
protocol TrackingProvider {
func trackScreenviews(_ event: TrackingEventType)
func trackAction(_ event: TrackingEventType)
func trackEvent(_ event: TrackingEventType)
}
其中,trackScreenviews()
用于发送页面事件,trackAction()
负责发送行为事件,而trackEvent()
用于发送通用的事件。
当要支持某种统计分析服务(例如 Firebase)的时候,我们就需要为TrackingProvider
提供一个具体的实现类型,比如,下面就是FirebaseTrackingProvider
的具体实现:
swift
struct FirebaseTrackingProvider: TrackingProvider {
func trackScreenviews(_ event: TrackingEventType) {
guard let event = event as? ScreenviewsTrackingEvent else {
return
}
Analytics.logEvent(AnalyticsEventSelectContent, parameters: [
AnalyticsParameterScreenName: event.screenName,
AnalyticsParameterScreenClass: event.screenClass])
}
func trackAction(_ event: TrackingEventType) {
guard let event = event as? FirebaseActionTrackingEvent else {
return
}
Analytics.logEvent(AnalyticsEventSelectContent, parameters: event.parameters)
}
func trackEvent(_ event: TrackingEventType) {
guard let event = event as? TrackingEvent else {
return
}
Analytics.logEvent(event.name, parameters: event.parameters)
}
}
FirebaseTrackingProvider
遵循了TrackingProvider
协议,并实现了trackScreenviews()
、trackAction()
和trackEvent()
三个方法。在这些方法里面,都是通过guard
语句来检查输入类型是否正确,并通过 Firebase SDK 所提供的Analytics.logEvent()
方法来发送事件。
假如我们需要支持新的统计分析服务时,就可以为该服务提供一个遵循TrackingProvider
协议的实现类型。例如,当我们支持 Mixpanel 时,就可实现一个名叫MixpanelTrackingProvider
的结构体。
有了TrackingProvider
的实例以后,我们就可以通过 Repo 来管理它们。这里我们一起看一下TrackingRepoType
协议和它的实现类型TrackingRepo
。
TrackingRepoType
的实现如下:
swift
protocol TrackingRepoType {
func register(trackingProvider: TrackingProvider)
func trackScreenviews(_ event: TrackingEventType)
func trackAction(_ event: TrackingEventType)
func trackEvent(_ event: TrackingEventType)
}
该协议定义了register(trackingProvider:)
方法来注册各种TrackingProvider
,然后还定义了三个方法来发送不同类型的事件。
TrackingRepo
的具体实现如下:
swift
final class TrackingRepo: TrackingRepoType {
static let shared: TrackingRepo = .init()
private var providers = [TrackingProvider]()
private init() { }
func register(trackingProvider: TrackingProvider) {
providers.append(trackingProvider)
}
func trackScreenviews(_ event: TrackingEventType) {
providers.forEach { $0.trackScreenviews(event) }
}
func trackAction(_ event: TrackingEventType) {
providers.forEach { $0.trackAction(event) }
}
func trackEvent(_ event: TrackingEventType) {
providers.forEach { $0.trackEvent(event) }
}
}
TrackingRepo
实现了TrackingRepoType
协议的所有方法,其中register(trackingProvider:)
方法把各个注册的TrackingProvider
的实例都保存在providers
属性里面,而trackScreenviews()
、trackAction()
和trackEvent()
三个方法则分别调用了providers
属性所对应的方法。你可以看到,假如我们注册了多个统计分析服务的 Provider,TrackingRepo
会把每个事件依次发送给各个后台服务。
到此为止,我们就实现了一套灵活的统计分析模块。有了它,我们可以很便捷地添加或者替换不同的统计分析服务。
2. 统计分析模块的使用
要使用统计分析模块,需要两步,第一步是注册TrackingProvider
的实例,代码如下:
swift
[FirebaseTrackingProvider()].forEach {
TrackingRepo.shared.register(trackingProvider: $0)
}
我们通过调用TrackingRepo
的register
方法来注册FirebaseTrackingProvider
的实例,这样就能把事件发送到 Firebase 统计分析服务了。如果有需要,我们还可以同时注册多个统计分析服务的 Provider。
第二步是使用TrackingRepo
的实例来发送事件。例如,下面的代码演示了如何发送页面事件:
swift
trackingRepo.trackScreenviews(ScreenviewsTrackingEvent(screenName: L10n.Tracking.momentsScreen, screenClass: String(describing: self)))
我们通过调用trackingRepo
的trackScreenviews()
方法来发送进入朋友圈页面的事件。
3. 为不同统计分析服务自定义事件内容
不同的统计分析服务所接收的事件内容可能不一样。这里我们以点赞按钮事件为例子看看如何为 Firebase 服务自定义事件。
首先我们看一下LikeActionTrackingEvent
的定义:
swift
struct LikeActionTrackingEvent: ActionTrackingEventType {
let momentID: String
let userID: String
}
该事件只有两个属性,其中momentID
表示点赞的朋友圈信息的 ID,而userID
表示点赞用户的 ID。为了特定给 Firebase 统计分析服务定制事件的内容,我们定义了一个名叫FirebaseActionTrackingEvent
的空协议:
swift
protocol FirebaseActionTrackingEvent: ActionTrackingEventType { }
然后给LikeActionTrackingEvent
提供了一个类型扩展,具体代码如下:
swift
extension LikeActionTrackingEvent: FirebaseActionTrackingEvent {
var parameters: [String : Any] {
return [
AnalyticsParameterItemID: "moment-id-\(momentID)-user-id-\(userID)",
AnalyticsParameterItemName: "moment-like"
]
}
}
该类型扩展使得LikeActionTrackingEvent
遵循了FirebaseActionTrackingEvent
协议,并为parameters
属性提供了一个默认的实现,在实现里面使用了在 Firebase SDK 里面定义的两个常量:AnalyticsParameterItemID
和AnalyticsParameterItemName
。当我们使用了这些常量时,Firebase 统计分析后台就会把事件自动映射成选择内容的 Item ID 和名字。
那提供这样一个类型扩展到底有什么好处呢?我们再看一下FirebaseTrackingProvider
里trackAction
方法的实现。
swift
func trackAction(_ event: TrackingEventType) {
guard let event = event as? FirebaseActionTrackingEvent else {
return
}
Analytics.logEvent(AnalyticsEventSelectContent, parameters: event.parameters)
}
在该方法里面,我们通过guard
语句检查传递进来的事件是否为FirebaseActionTrackingEvent
类型,如果不是,程序就直接退出了。如果是,就调用event.parameters
属性来获取事件的内容,这时候就会调用类型扩展里parameters
属性的默认实现,类型扩展方法能保证FirebaseTrackingProvider
只发送遵循了FirebaseActionTrackingEvent
协议的事件类型。
假如我们需要为其他统计分析服务自定义事件的内容时,该怎么做呢?例如,为 Mixpanel 自定义事件,可以通过下面的代码实现:
swift
extension LikeActionTrackingEvent: MixpanelActionTrackingEvent {
var parameters: [String : Any] {
return [
MixPanelEventKey: "moment-id-\(momentID)-user-id-\(userID)",
MixPanelEventName: "moment-like"
]
}
}
通过类型扩展来遵循不同的事件协议,我们就可以很灵活地为各个统计分析服务发送不同内容的事件了。
最后,我们再看一下如何发送LikeActionTrackingEvent
事件,具体实现如下:
swift
trackingRepo.trackAction(LikeActionTrackingEvent(momentID: momentID, userID: userID))
trackingRepo
会自动把事件发送到各个注册的统计分析服务中,并且根据LikeActionTrackingEvent
的类型扩展来准备不同的事件内容。
4. Firebase 统计分析报告
完成了上述的开发工作后,我们就能收集用户行为数据了,并且还可以在 Firebase 的统计分析服务上查看相关的报告。下图是 Moments App 的统计分析报告:
我们可以在 Analyics 菜单下看到各种各样的报告,如事件统计、设备类型以及用户留存率等。这些报告能协助我们更准确地做出产品决定,比如,通过 iOS 活跃版本的报告能帮我们决定 App 支持 iOS 的最低版本号,假如绝大部分用户都使用 iOS 13 以上的版本,我们就引入 SwiftUI 和 Combine 等新技术。
我建议你仔细阅读统计分析服务的相关文档,并熟悉各种统计报告以及指标,从而助力产品的增长。
总结
在这一讲,我们讲述了如何架构一个灵活的统计分析模块,有了这个模块,我们就可以很方便地支持和替换不同的统计分析服务。同时,我们还以 Firebase 为例子讲述了如何配置 Firebase 的统计分析服务。总之,我希望你能好好地利用这些分析报告和指标,进而助力产品的增长。
思考题
请参照 FirebaseTrackingProvider 来编写一个遵循 TrackingProvider 协议的 SystemLogTrackingProvider 来打印日志,在该 Provider 里通过系统提供的 os_log() 方法来打印事件。
请把你的答案写到留言区或者提交一个 PR 哦。下一讲我将介绍如何使用 Firebase 的崩溃报告服务去解决线上的 Bug,记得按时来听课。
源码地址
统计分析模块源码地址:https://github.com/lagoueduCol/iOS-linyongjian/tree/main/Moments/Moments/Foundations/Analytics