接入友盟统计与 U-APM,并优化宣传页打卡点标签交互。

友盟 SDK 改为隐私同意后再初始化;宣传页标签新增滑块位移动画,超过 5 个时选中项自动滚动居中。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-03 09:23:15 +08:00
parent c7f557f361
commit 00c3cd0a93
134 changed files with 6555 additions and 590 deletions

View File

@ -0,0 +1,96 @@
//
// UMengSDKBootstrap.swift
// suixinkan
//
// Created by Codex on 2026/7/2.
//
import Foundation
/// U-APM 使
struct UMengAPMOptions: Equatable {
let crashAndBlockMonitorEnable: Bool
let launchMonitorEnable: Bool
let memMonitorEnable: Bool
let oomMonitorEnable: Bool
let networkEnable: Bool
let logCollectEnable: Bool
/// OOM
static let minimumCompliance = UMengAPMOptions(
crashAndBlockMonitorEnable: true,
launchMonitorEnable: true,
memMonitorEnable: false,
oomMonitorEnable: false,
networkEnable: false,
logCollectEnable: false
)
}
/// SDK 便
@MainActor
protocol UMengSDKConfiguring {
/// 使 AppKey APM SDK
func configure(appKey: String, channel: String?, options: UMengAPMOptions)
}
/// SDK
@MainActor
struct UMengSDKProductionConfigurator: UMengSDKConfiguring {
/// 使 ObjC SDK
func configure(appKey: String, channel: String?, options: UMengAPMOptions) {
UMengSDKAdapter.configure(
appKey: appKey,
channel: channel,
crashAndBlockMonitorEnable: options.crashAndBlockMonitorEnable,
launchMonitorEnable: options.launchMonitorEnable,
memMonitorEnable: options.memMonitorEnable,
oomMonitorEnable: options.oomMonitorEnable,
networkEnable: options.networkEnable,
logCollectEnable: options.logCollectEnable
)
}
}
@MainActor
/// SDK
final class UMengSDKBootstrap {
static let shared = UMengSDKBootstrap(configurator: UMengSDKProductionConfigurator())
private let configurator: UMengSDKConfiguring
private(set) var didConfigure = false
///
init(configurator: UMengSDKConfiguring) {
self.configurator = configurator
}
/// AppKey SDK
@discardableResult
func configureIfAllowed(
preferencesStore: AppPreferencesStore,
appKey: String = UMengConfig.appKey,
channel: String? = UMengConfig.channel,
options: UMengAPMOptions = .minimumCompliance
) -> Bool {
guard preferencesStore.loadPrivacyAgreementAccepted() else { return false }
guard !didConfigure else { return false }
let normalizedAppKey = appKey.trimmingCharacters(in: .whitespacesAndNewlines)
guard !normalizedAppKey.isEmpty else { return false }
configurator.configure(
appKey: normalizedAppKey,
channel: normalizedChannel(channel),
options: options
)
didConfigure = true
return true
}
/// nil
private func normalizedChannel(_ channel: String?) -> String? {
let value = channel?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
return value.isEmpty ? nil : value
}
}