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