QSLDeviceTool.swift 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. //
  2. // QSLDeviceTool.swift
  3. // QuickSearchLocation
  4. //
  5. // Created by mac on 2024/4/10.
  6. //
  7. import Foundation
  8. import UIKit
  9. import AdSupport
  10. import Security
  11. import AppTrackingTransparency
  12. /// 设备各种信息
  13. class QSLDeviceTool: NSObject {
  14. static let shared = QSLDeviceTool()
  15. private override init() {}
  16. // 在 QSLDeviceTool 中添加
  17. public func configure(autoRefresh: Bool = true) {
  18. enableAutoRefresh = autoRefresh
  19. }
  20. private let configQueue = DispatchQueue(label: "com.qsl.deviceTool.config")
  21. public var enableAutoRefresh: Bool = true {
  22. didSet {
  23. configQueue.async {
  24. if self.enableAutoRefresh {
  25. self.setupObservers()
  26. } else {
  27. self.removeObservers()
  28. }
  29. }
  30. }
  31. }
  32. private func removeObservers() {
  33. if #available(iOS 13.0, *) {
  34. NotificationCenter.default.removeObserver(
  35. self,
  36. name: UIScene.didEnterBackgroundNotification,
  37. object: nil
  38. )
  39. }else{
  40. NotificationCenter.default.removeObserver(
  41. self,
  42. name: UIApplication.didEnterBackgroundNotification,
  43. object: nil
  44. )
  45. }
  46. }
  47. // MARK: - 监听 App 生命周期
  48. private func setupObservers() {
  49. removeObservers() // 先移除旧监听
  50. if #available(iOS 13.0, *) {
  51. NotificationCenter.default.addObserver(
  52. self,
  53. selector: #selector(sceneDidEnterBackground(_:)),
  54. name: UIScene.didEnterBackgroundNotification,
  55. object: nil
  56. )
  57. } else {
  58. // Fallback 到 App 生命周期监听(iOS 12 及以下)
  59. NotificationCenter.default.addObserver(
  60. self,
  61. selector: #selector(appDidEnterBackground),
  62. name: UIApplication.didEnterBackgroundNotification,
  63. object: nil
  64. )
  65. }
  66. }
  67. func refreshSessionId() {
  68. sessionId = UUID().uuidString.replacingOccurrences(of: "-", with: "")
  69. print("SessionID 已更新: \(sessionId)")
  70. }
  71. @objc private func appDidEnterBackground() {
  72. // App 退到后台时更新 SessionID
  73. refreshSessionId()
  74. }
  75. @objc private func sceneDidEnterBackground(_ notification: Notification) {
  76. // App 退到后台时更新 SessionID
  77. refreshSessionId()
  78. }
  79. // MARK: 1、App 名称
  80. /// App 名称
  81. lazy var appDisplayName: String = {
  82. return (Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String) ?? ""
  83. }()
  84. // MARK: 2、App 平台 安卓为1 iOS为2
  85. /// App 平台
  86. lazy var platform: Int = {
  87. return 2;
  88. }()
  89. // MARK: 3、渠道名称
  90. /// 渠道名称
  91. lazy var channelName: String = {
  92. return "appstore";
  93. }()
  94. // MARK: 4、版本
  95. /// 版本
  96. lazy var OSVersion: String = {
  97. return String.init(format: "%@%@", UIDevice.current.systemName, UIDevice.current.systemVersion)
  98. }()
  99. // MARK: 5、包名
  100. /// 包名
  101. lazy var packageName: String = {
  102. return Bundle.main.bundleIdentifier ?? ""
  103. }()
  104. // MARK: 6、版本号 1.0.0
  105. /// 版本号
  106. lazy var appVersionName: String = {
  107. return (Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String) ?? "";
  108. }()
  109. // MARK: 6、版本号 100
  110. /// 版本号
  111. lazy var appVersionCode: String = {
  112. let versionSubStrings = self.appVersionName.components(separatedBy: ".")
  113. var versionCode = ""
  114. for subString in versionSubStrings {
  115. versionCode.append(subString)
  116. }
  117. if versionSubStrings.count < 3 {
  118. versionCode.append("0")
  119. }
  120. return versionCode
  121. }()
  122. // MARK: 7、品牌
  123. /// 品牌
  124. lazy var brand: String = {
  125. return "Apple"
  126. }()
  127. // MARK: 8、idfa
  128. /// idfa
  129. lazy var idfa: String = {
  130. if #available(iOS 14.0, *) {
  131. return ""
  132. } else {
  133. // 检查用户是否授权应用使用广告标识符
  134. if ASIdentifierManager.shared().isAdvertisingTrackingEnabled {
  135. let idfa = ASIdentifierManager.shared().advertisingIdentifier
  136. return idfa.uuidString
  137. } else {
  138. return ""
  139. }
  140. }
  141. }()
  142. // MARK: 8、idfv
  143. /// idfv
  144. lazy var idfv: String = {
  145. return UIDevice.current.identifierForVendor?.uuidString ?? "";
  146. }()
  147. //设备唯一码 卸载删除还是一致
  148. lazy var deviceID: String = {
  149. return QSLDeviceUniqueIDTool.shared.deviceUniqueID
  150. }()
  151. // MARK: 9、型号
  152. /// 型号
  153. lazy var phoneModel: String = {
  154. var systemInfo = utsname()
  155. uname(&systemInfo)
  156. let machineMirror = Mirror(reflecting: systemInfo.machine)
  157. let identifier = machineMirror.children.reduce("") { identifier, element in
  158. guard let value = element.value as? Int8, value != 0 else { return identifier }
  159. return identifier + String(UnicodeScalar(UInt8(value)))
  160. }
  161. return identifier
  162. }()
  163. lazy var sessionId: String = {
  164. let uuidString = UUID().uuidString.replacingOccurrences(of: "-", with: "")
  165. return uuidString
  166. }()
  167. }
  168. class QSLDeviceUniqueIDTool {
  169. static let shared = QSLDeviceUniqueIDTool()
  170. // Keychain 存储的 key
  171. private let keychainService = QSLCentralEventManager.bundleId + ".deviceUniqueID"
  172. private let keychainAccount = "deviceUniqueID"
  173. // 获取或生成设备唯一码
  174. var deviceUniqueID: String {
  175. // 1. 尝试从 Keychain 读取
  176. if let existingID = retrieveDeviceUniqueIDFromKeychain() {
  177. return existingID
  178. }
  179. // 2. 如果没有,生成新的 UUID 并存储到 Keychain
  180. else {
  181. let newID = UUID().uuidString
  182. saveDeviceUniqueIDToKeychain(newID)
  183. return newID
  184. }
  185. }
  186. // MARK: - Keychain 操作
  187. /// 从 Keychain 读取 UUID
  188. private func retrieveDeviceUniqueIDFromKeychain() -> String? {
  189. let query: [CFString: Any] = [
  190. kSecClass: kSecClassGenericPassword,
  191. kSecAttrService: keychainService,
  192. kSecAttrAccount: keychainAccount,
  193. kSecReturnData: true,
  194. kSecMatchLimit: kSecMatchLimitOne
  195. ]
  196. var dataTypeRef: AnyObject?
  197. let status = SecItemCopyMatching(query as CFDictionary, &dataTypeRef)
  198. if status == errSecSuccess, let data = dataTypeRef as? Data {
  199. return String(data: data, encoding: .utf8)
  200. }
  201. return nil
  202. }
  203. /// 存储 UUID 到 Keychain
  204. private func saveDeviceUniqueIDToKeychain(_ id: String) {
  205. let query: [CFString: Any] = [
  206. kSecClass: kSecClassGenericPassword,
  207. kSecAttrService: keychainService,
  208. kSecAttrAccount: keychainAccount,
  209. kSecValueData: id.data(using: .utf8)!
  210. ]
  211. // 先删除旧的(如果有)
  212. SecItemDelete(query as CFDictionary)
  213. // 存储新的
  214. SecItemAdd(query as CFDictionary, nil)
  215. }
  216. }