// // QSLDeviceTool.swift // QuickSearchLocation // // Created by mac on 2024/4/10. // import Foundation import UIKit import AdSupport import Security import AppTrackingTransparency /// 设备各种信息 class QSLDeviceTool: NSObject { static let shared = QSLDeviceTool() private override init() {} // 在 QSLDeviceTool 中添加 public func configure(autoRefresh: Bool = true) { enableAutoRefresh = autoRefresh } private let configQueue = DispatchQueue(label: "com.qsl.deviceTool.config") public var enableAutoRefresh: Bool = true { didSet { configQueue.async { if self.enableAutoRefresh { self.setupObservers() } else { self.removeObservers() } } } } private func removeObservers() { if #available(iOS 13.0, *) { NotificationCenter.default.removeObserver( self, name: UIScene.didEnterBackgroundNotification, object: nil ) }else{ NotificationCenter.default.removeObserver( self, name: UIApplication.didEnterBackgroundNotification, object: nil ) } } // MARK: - 监听 App 生命周期 private func setupObservers() { removeObservers() // 先移除旧监听 if #available(iOS 13.0, *) { NotificationCenter.default.addObserver( self, selector: #selector(sceneDidEnterBackground(_:)), name: UIScene.didEnterBackgroundNotification, object: nil ) } else { // Fallback 到 App 生命周期监听(iOS 12 及以下) NotificationCenter.default.addObserver( self, selector: #selector(appDidEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil ) } } func refreshSessionId() { sessionId = UUID().uuidString.replacingOccurrences(of: "-", with: "") print("SessionID 已更新: \(sessionId)") } @objc private func appDidEnterBackground() { // App 退到后台时更新 SessionID refreshSessionId() } @objc private func sceneDidEnterBackground(_ notification: Notification) { // App 退到后台时更新 SessionID refreshSessionId() } // MARK: 1、App 名称 /// App 名称 lazy var appDisplayName: String = { return (Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String) ?? "" }() // MARK: 2、App 平台 安卓为1 iOS为2 /// App 平台 lazy var platform: Int = { return 2; }() // MARK: 3、渠道名称 /// 渠道名称 lazy var channelName: String = { return "appstore"; }() // MARK: 4、版本 /// 版本 lazy var OSVersion: String = { return String.init(format: "%@%@", UIDevice.current.systemName, UIDevice.current.systemVersion) }() // MARK: 5、包名 /// 包名 lazy var packageName: String = { return Bundle.main.bundleIdentifier ?? "" }() // MARK: 6、版本号 1.0.0 /// 版本号 lazy var appVersionName: String = { return (Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String) ?? ""; }() // MARK: 6、版本号 100 /// 版本号 lazy var appVersionCode: String = { let versionSubStrings = self.appVersionName.components(separatedBy: ".") var versionCode = "" for subString in versionSubStrings { versionCode.append(subString) } if versionSubStrings.count < 3 { versionCode.append("0") } return versionCode }() // MARK: 7、品牌 /// 品牌 lazy var brand: String = { return "Apple" }() // MARK: 8、idfa /// idfa lazy var idfa: String = { if #available(iOS 14.0, *) { return "" } else { // 检查用户是否授权应用使用广告标识符 if ASIdentifierManager.shared().isAdvertisingTrackingEnabled { let idfa = ASIdentifierManager.shared().advertisingIdentifier return idfa.uuidString } else { return "" } } }() // MARK: 8、idfv /// idfv lazy var idfv: String = { return UIDevice.current.identifierForVendor?.uuidString ?? ""; }() //设备唯一码 卸载删除还是一致 lazy var deviceID: String = { return QSLDeviceUniqueIDTool.shared.deviceUniqueID }() // MARK: 9、型号 /// 型号 lazy var phoneModel: String = { var systemInfo = utsname() uname(&systemInfo) let machineMirror = Mirror(reflecting: systemInfo.machine) let identifier = machineMirror.children.reduce("") { identifier, element in guard let value = element.value as? Int8, value != 0 else { return identifier } return identifier + String(UnicodeScalar(UInt8(value))) } return identifier }() lazy var sessionId: String = { let uuidString = UUID().uuidString.replacingOccurrences(of: "-", with: "") return uuidString }() } class QSLDeviceUniqueIDTool { static let shared = QSLDeviceUniqueIDTool() // Keychain 存储的 key private let keychainService = QSLCentralEventManager.bundleId + ".deviceUniqueID" private let keychainAccount = "deviceUniqueID" // 获取或生成设备唯一码 var deviceUniqueID: String { // 1. 尝试从 Keychain 读取 if let existingID = retrieveDeviceUniqueIDFromKeychain() { return existingID } // 2. 如果没有,生成新的 UUID 并存储到 Keychain else { let newID = UUID().uuidString saveDeviceUniqueIDToKeychain(newID) return newID } } // MARK: - Keychain 操作 /// 从 Keychain 读取 UUID private func retrieveDeviceUniqueIDFromKeychain() -> String? { let query: [CFString: Any] = [ kSecClass: kSecClassGenericPassword, kSecAttrService: keychainService, kSecAttrAccount: keychainAccount, kSecReturnData: true, kSecMatchLimit: kSecMatchLimitOne ] var dataTypeRef: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &dataTypeRef) if status == errSecSuccess, let data = dataTypeRef as? Data { return String(data: data, encoding: .utf8) } return nil } /// 存储 UUID 到 Keychain private func saveDeviceUniqueIDToKeychain(_ id: String) { let query: [CFString: Any] = [ kSecClass: kSecClassGenericPassword, kSecAttrService: keychainService, kSecAttrAccount: keychainAccount, kSecValueData: id.data(using: .utf8)! ] // 先删除旧的(如果有) SecItemDelete(query as CFDictionary) // 存储新的 SecItemAdd(query as CFDictionary, nil) } }