// // 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 } }