SwiftUI App中接入友盟统计U-APP和性能监控U-APM
- 24 Oct, 2024

目录
App接入数据统计的意义
在App中接入数据统计,能使得开发者和产品经理能够深入了解用户行为、优化产品体验,并基于数据做出明智的决策。通过收集和分析用户数据,不仅可以识别潜在的问题和改进机会,还能实现个性化服务,从而提升用户满意度和留存率。
App中接入数据统计的方式
根据使用场景,目前一般有三种在App中接入数据统计的方式:
- 使用第三方统计。如果是海外应用,可以考虑使用Firebase全家桶中的Google Analytics, 或者Flurry。国内的话,则可以考虑使用的友盟。
- 自己开发实现。如果不想依赖第三方平台,受第三方平台的限制,或者对于数据隐私有较高要求,可以自己在App里实现向数据上报的逻辑,把数据上报到服务端,由服务端收集后处理分析用户数据并展示(如使用Grafana)
- 使用开源项目。 基于一些开源的项目自己搭建数据统计平台,如posthog, aptabase。
在SwiftUI中接入友盟统计
本文主要介绍在SwiftUI中,如何接入友盟统计U-APP以及性能监控U-APM。友盟的官方文档中,都是基于Objective-C的示例代码。或者使用Swift语言在UIKIt框架下的接入方式。而没有结合SwiftUI框架的使用示例,因此也有了这篇文章,记录下自己在SwiftUI中接入友盟统计时的步骤以及所踩的坑。废话不多说,直接开干。整个过程主要分3步:
- 在友盟中创建应用
- 在App中集成友盟的SDK
- 集成测试
在友盟创建应用
注册一个友盟账号(如果没有的话),登录后,打开链接创建一个应用, 应用类型选择App端,然后选择新建
根据实际情况,填入App的相关信息,如下图所示,最后点击注册应用
接下来,我们会看到两个比较重要的信息: 第一个是红色圈出来的AppKey, 这个信息我们需要注意,后面接入SDK时会在代码中使用(AppKey在创建好应用之后,随时都可以在App的信息中查看到,不需要特意记录下来)。
第二个是选择要开始使用的服务。友盟除了数据统计,应用性能监控之外,还有消息推送,智能认证等,我们可以根据自身的需求选择开通对应的服务,这里我选择了开通移动统计U-App以及应用性能监控U-APM服务。(这里也不用特别纠结需要选择哪些服务,因为应用创建好之后,我们可以随时修改需要开通的服务)
选择好之后,点击确认开通。记录下我们的App Key, App的创建也就完成了。
在App中集成友盟的SDK
在友盟中创建好应用后,接下来就可以在App中集成SDK了。这里我们以创建一个名为HelloUMeng的项目为例。
使用Xcode新建项目
打开Xcode,创建一个名为HelloUMeng
的项目,这个是常规操作,不再赘述。
Info
本文章中后面所有使用到的项目源码,如果有需要的话,可以从Github下载
Cocoapods安装
官方推荐使用cocoapods来安装友盟的SDK, 如果App已经配置好了Cocoapods,则可以跳过这一步。没有的话,可以在项目的根目录下使用,先创建一个Gemfile
文件
# frozen_string_literal: true
source "https://rubygems.org"
gem "cocoapods"
再创建如下脚本scripts/setup.sh
,并在项目根目录下执行,可以一键配置好rbenv虚拟环境,并使用bundler安装好Cocoapods
#!/bin/sh
# Install homebrew
which brew || curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh | bash
# Install rbenv
which rbenv || curl -fsSL https://github.com/rbenv/rbenv-installer/raw/HEAD/bin/rbenv-installer | bash
# Install ruby using rbenv
latest_stable_ruby_version=$(rbenv install -l | grep -v - | tail -1)
echo $latest_stable_ruby_version > .ruby-version
if [[ ! -d "$HOME/.rbenv/versions/$ruby_version" ]]; then
rbenv install $ruby_version;
rbenv init
fi
# Install bunlder with specical version
gem install bundler
# Install all gems
bundle install
创建Podfile
引入需要使用的Pods, 如下所示:
# Uncomment the next line to define a global platform for your project
platform :ios, '17.0'
def umeng_pods
# 基础库,必须集成
pod 'UMCommon'
pod 'UMDevice'
# 开发阶段进行调试SDK及相关功能使用,可在发布 App 前移除
pod 'UMCCommonLog'
# 下面的库根据需要使用的服务选择性按照
pod 'UMAPM' # APM组件,原错误分析升级为独立产品U-APM
#pod 'UMPush' # 推送组件,由原来的UMCPush变为UMPush
#pod 'UMShare/UI' # 可选,UI模块(分享面板),由原来的UMCShare/UI变为了UMShare/UI
# 分享SDK 在线依赖其它平台仅支持手动集成[友盟+官网-开发者中心-sdk下载页-sdk下载]
#pod 'UMShare/Social/WeChat'
#pod 'UMShare/Social/Sina'
#pod 'UMShare/Social/QQ'
#pod 'UMLink' # 智能超链组件
#pod 'UMVerify' # 智能认证组件
end
target 'HelloUMeng' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for TimeMastery
umeng_pods
end
然后执行pod install
安装SDK,如果使用的是iOS 18和Xcode 16,pod install
命令会失败,并给出如下错误提示
RuntimeError - `PBXGroup` attempted to initialize an object with unknown ISA `PBXFileSystemSynchronizedRootGroup` from attributes: `{"isa"=>"PBXFileSystemSynchronizedRootGroup", "path"=>"HelloUMeng", "sourceTree"=>"<group>"}`
参考下面的文章可以解决这个问题:
CocoaPods pod init Fails with “Unknown ISA PBXFileSystemSynchronizedRootGroup” Error on Xcode 16
具体步骤如下:
- 关闭Xcode
- 在项目根目录下,删除
Podfile.lock
文件和Pods
目录 - 编辑
HelloUMeng.xcodeproj/project.pbxproj
文件,将里面的PBXFileSystemSynchronizedRootGroup
全部替换为PBXGroup
- 编辑
HelloUMeng.xcodeproj/project.pbxproj
文件, 找到objectVersion
, 将它修改为objectVersion = 56
修改后再次执行pod install
, 这样就可以安装成功了
创建桥接头文件
由于友盟的SDK都是使用Object-C编写的,要想通过Swift语言调用它们,必须创建桥接头文件.
使用Xcode打开项目,这里需要注意: 使用Cocoapods管理项目后,需要通过open HelloUMeng.xcworkspace/
命令打开项目。然后新建桥接头文件,在Xcode中,首先选中HelloUmeng这Target的目录,右键点击,选择New File from templates
输入文件名为HelloUMeng-Bridging-Header.h
, 并且不需要关联选择任何Targets
创建好文件后,编辑文件输入如下内容
#ifndef UMengBridging_Header_h
//导入UMCommon的OC的头文件
#import <UMCommon/UMCommon.h>
//导入UMAPM的OC的头文件
#import <UMAPM/UMLaunch.h>
#import <UMAPM/UMCrashConfigure.h>
#import <UMAPM/UMAPMConfig.h>
//导入UMCommonLog的OC的头文件(如需加入日志库 把此注释打开)
#import <UMCommonLog/UMCommonLogManager.h>
//导入UMCommon的OC的头文件
#import <UMCommon/UMConfigure.h>
//导入UMAnalytics的OC的头文件
#import <UMCommon/MobClick.h>
//导入UMAnalytics Game的OC的头文件(如需游戏统计,可选)
//#import <UMAnalyticsGame/MobClickGameAnalytics.h>
#endif /* UMengBridging_Header_h */
配置桥接头文件
选中项目,在Build Settings
中,找到Objective-C Bridging Header
,设置为刚刚创建的桥接头文件的路径,并设置Precompile Bridging Header
为Yes
配置好之后,运行App, 看是否会报错,如果没有报错,则说明桥接头文件配置成功。下面列出了作者在使用时遇到的一些错误,也附上了解决方法,希望可以帮助大家少踩坑。
错误解决
错误一 Building for ‘iOS-simulator’, but linking in object file
如果编译项目时遇到如下错误
Building for 'iOS-simulator', but linking in object file ( .../HelloUMeng/Pods/UMCCommonLog/UMCommonLog/UMCommonLog.framework/UMCommonLog[arm64][2](UMCommonLog.o)) built for 'iOS'
说明运行App使用的模拟器和pod库的不兼容导致的。切换到真机调试就不会有问题。如果还是想用模拟器,可以按下面的方式修改
需要调整 Build settings
中的Excluded Architecture
,添加上arm64
同时修改Podfile
文件,添加如下配置,避免pod install
时覆盖了上面的项目配置
# 修复报错
# Xcode building for iOS Simulator, but linking in an object file built for iOS, for architecture 'arm64'
# https://stackoverflow.com/questions/63607158/xcode-building-for-ios-simulator-but-linking-in-an-object-file-built-for-ios-f
post_install do |installer|
installer.pods_project.build_configurations.each do |config|
config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64"
end
end
注意
使用上面的设置后,当在真机上运行App时,又会报错
Build input file cannot be found: '/Users/.../Library/Developer/Xcode/DerivedData/HelloUMeng-andwdcqdjmobxjawstdkqvnkofgt/Build/Products/Debug-iphoneos/HelloUMeng.app/HelloUMeng.debug.dylib'. Did you forget to declare this file as an output of a script phase or custom build rule which produces it?
这时只需要在Build settings
中的Excluded Architecture
,去掉arm64
就没有问题了。
错误二 Sandbox: bash(51846) deny(1) file-write-create
如果编译项目时遇到如下错误
Sandbox: bash(51846) deny(1) file-write-create ../HelloUMeng/Pods/resources-to-copy-HelloUMeng.txt
找到 Build settings
中的User Script Sandboxing
, 将它设置为false
初始化SDK
下载示例代码
- 参考友盟官方的Swift Demo示例, 下载示例代码后,我们可以自己看一看,了解下SDK的基本使用。解压后,将其中
UMAnalyticsSwiftDemo/UMSwift
目录下的UMAnalyticsSwift.swift
,UMCommonLogSwift.swift
,UMCommonSwift.swift
三个文件拷贝到当前项目中,这里我并没有拷贝UMGameAnalyticsSwift.swift
文件,因为里面是封装的是游戏统计相关的操作。我暂时不需要,拷贝后的效果如下:
创建AppDelegate.swift文件
在项目中,新建一个AppDelegate.swift
文件, 设置如下内容:
import UIKit
class AppDelegate: NSObject, UIApplicationDelegate, ObservableObject {
func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
// 根据实际情况替换
initUmeng(appKey: "6718d7bd80464b33f6e30afe")
return true
}
func initUmeng(appKey: String) {
#if DEBUG
UMCommonLogSwift.setUpUMCommonLogManager()
UMCommonSwift.setLogEnabled(bFlag: true)
#endif
// Channel为渠道号,为空时表示App Store
UMCommonSwift.initWithAppkey(appKey: appKey, channel: "")
}
}
上面的appKey
就是第一步中在友盟中创建了App之后生成的appKey
,如果忘记了,可以在应用信息中查看
修改HelloUMengApp.swift文件
使用刚刚创建的AppDelegate
import SwiftUI
@main
struct HelloUMengApp: App {
// 设置使用AppDelegate
@UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
验证
配置成功后,启动项目,在控制台可以看到类型如下输出
集成测试
根据友盟官方文档的描述
集成测试是通过收集和展示已注册测试设备发送的日志,来检验SDK集成有效性和完整性的一个服务。 所有由注册设备发送的应用日志将实时地进行展示,您可以方便地查看包括应用版本、渠道名称、自定义事件、页面访问情况等数据,提升集成与调试的工作效率。
简单来说,集成测试就是让我们可以实时看到设备使用SDK的情况,以确保我们的集成的友盟SDK按照我们预期的想法在上报统计数据和异常信息。
要使用集成测试,需要完成以下步骤
配置 URL Schema
在项目设置 target -> 选项卡 Info - > URL Types,填入的URL Schemes,格式为um.appKey
,如:
um.6718d7bd80464b33f6e30afe
, 6718d7bd80464b33f6e30afe
就是我们第一步中创建的友盟App Key
配置接受URL
这里需要注意,官方文档给的示例是在AppDelegate.swift
文件中,添加下面的方法来配置接受URL的
func application(_: UIApplication, open url: URL, options _: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
print("open url in AppDelegate: \(url.absoluteString)")
if MobClick.handle(url) {
print("handle url: \(url.absoluteString)")
return true
}
// 其它第三方处理
return true
}
但实际测试时发现,在SwiftUI中,上述方法并不会调用,而是通过onOpenURL
修饰符来代替
import SwiftUI
@main
struct HelloUMengApp: App {
// 设置使用AppDelegate
@UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL { url in
// 处理友盟的集成测试调用
print("onOpenURL: \(url)")
MobClick.handle(url)
}
}
}
}
如果这里配置错误,会导致集成测试时无法看到收集的数据
开启实时监控
在登陆了友盟账户的情况下,打开实时日志的页面。选择待验证应用为我们刚刚创建的HelloUMeng
,如下图所示:
首先确保使用最新的代码编译并将App安装在手机上,然后使用手机系统自带的照相App扫描上面的二维码。会打开如下网页
点击”开始测试”按钮,会唤起我们的App. 这时在网页就可以看到SDK自动上报”App启动”的事件信息
说明我们的SDK配置正常,且没有任何问题。
自定义上报事件和App崩溃日志
我们也可以自定义上报的事件,修改ContentView.swift
的代码,添加两个按钮,分别用于触发上报自定义事件,和引起App崩溃
struct ContentView: View {
var body: some View {
VStack {
Button("上报自定义事件") {
UMAnalyticsSwift.event(eventId: "eventA")
}
.buttonStyle(.borderedProminent)
Button("App崩溃上报") {
fatalError("App崩溃上报测试")
}
.buttonStyle(.borderedProminent)
}
.padding()
}
}
修改代码后,把App安装到手机上。
测试自定义上报事件
再次重复前面的步骤,打开实时日志的页面,扫描二维码唤起App,然后点击App中的上报自定义事件
的按钮,则可以在页面看到上报的事件信息。
说明统计功能完全没有问题。
测试App崩溃日志上报
崩溃日志的查看和统计数据不在同一个地方,打开友盟的U-APM,在”应用列表中”,找到我们创建的”HelloUMeng” App, 选择”集成测试“
在打开的页面选择”开始测试”,会看到类似统计上报事件时相同的页面
同样扫描二维码,在打开的网页中,点击”关联应用开始集成测试”的按钮
唤起App后,点击App中的”App崩溃上报”按钮,引起App崩溃,再次打开App,让SDK上报事件(因为崩溃类的日志上报时机是在App崩溃后,下一次打开App时才上报,关于不同类似事件的上报时机,可以参考友盟的官方文档)
在网页上,正常情况下应该是能像数据统计上报一样,实时看到上报的日志,但是我多次测试,并且让App崩溃多次,都没有能看到实时日志。
反而是等待几分钟后,在友盟的U-APM,的”应用列表中”,看到了捕获的App异常情况。集成状态也由开始的”未集成”变成了”已集成”状态。
点击应用名,在实时概览中,可以看到App采集的异常日志情况
在点击”查看详情”,可以看到异常日志的采集情况。
注意,在测试异常上报时,不要使用自定义异常来测试,因为自定义异常上传的功能免费版本的U-APM并不支持。
// APM免费版本不支持
Button("自定义异常上报") {
do {
throw CustomError.NetworkError
} catch {
print("上报自定义错误测试")
UMCrashConfigure.reportException(withName: "崩溃采集测试", reason: "测试", stackTrace: [error])
}
}
至此,整个测试流程完成,说明我们数据统计U-APP和U-APM的SDK集成都没有问题。当然,这里也只是简单使用了SDK,更多关于数据统计和APM采集相关的使用,可以参考友盟的官方文档。
再次强调下
Info
本文章中使用的项目源码,如果有需要的话,可以从Github下载