Compare commits
	
		
			1 Commits 
		
	
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 8163f171d2 | 
							
								
								
									
										49
									
								
								CHANGELOG.md
								
								
								
								
							
							
						
						
									
										49
									
								
								CHANGELOG.md
								
								
								
								
							|  | @ -1,3 +1,48 @@ | |||
| ## v0.4.3 | ||||
| - fix | ||||
|   - 事件参数值字符串中包含“‘”单引号sql报错 | ||||
| - feature | ||||
|   - 增加事件参数 | ||||
|     - deviceInfo.appsflyerId | ||||
|   - 增加设置appsflyerId接口 | ||||
|     - setAppFlyersId(_ appFlyersId: String?) -> Void | ||||
| 
 | ||||
| ## v0.4.2 | ||||
| - fix | ||||
|   - 临时回滚 0.4.0,排查solitare collection ATP DAU下降问题。 | ||||
| 
 | ||||
| ## v0.4.1 | ||||
| - fix | ||||
|   - x.0的数值类型转换成整型问题 | ||||
| 
 | ||||
| ## v0.4.0 | ||||
| - feature | ||||
|   - 支持 AppExtension 上报打点 | ||||
| - 如在AppExtension 中使用,需在Podfile 中添加以下代码: | ||||
| ```swift | ||||
| installer.pods_project.targets.each do |target| | ||||
|   flutter_additional_ios_build_settings(target) | ||||
|   target.build_configurations.each do |config| | ||||
|     if target.name == 'GuruAnalyticsLib' | ||||
|       config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'NO' | ||||
|     end | ||||
|   end | ||||
| end | ||||
| ``` | ||||
| 
 | ||||
| ## v0.3.9 | ||||
| - feature | ||||
|   - 增加事件参数 | ||||
|     - deviceInfo.guruAnalyticsVersion | ||||
|     - deviceInfo.gurusdkVersion | ||||
|   - 增加设置中台版本接口 | ||||
|     - 初始化方法增加guruSDKVersion参数 initializeLib(..., guruSDKVersion: String)  | ||||
|     - setGuruSDKVersion(_ version: String) -> Void | ||||
| 
 | ||||
| ## v0.3.8.1 | ||||
| - fix | ||||
|   - x.0的数值类型转换成整型问题 | ||||
| 
 | ||||
| ## v0.3.8 | ||||
| - fix | ||||
|   - 隐私文件增加divice id声明 | ||||
|  | @ -15,6 +60,10 @@ | |||
|     - deviceInfo.sdkVersion | ||||
|     - info.vendorId | ||||
| 
 | ||||
| ## v0.3.6.1 | ||||
| - fix: | ||||
|   - 更新privacy manifest文件,移除tracking domains下的空条目。 | ||||
| 
 | ||||
| ## v0.3.6 | ||||
| - fix: | ||||
|   - 增加第三方依赖库版本约束 | ||||
|  |  | |||
|  | @ -370,6 +370,8 @@ | |||
| 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||||
| 				DEVELOPMENT_TEAM = 69MW7VVKA9; | ||||
| 				INFOPLIST_FILE = GuruAnalytics/Info.plist; | ||||
| 				INFOPLIST_KEY_CFBundleDisplayName = GuruAnalytics; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 15.6; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 11.0; | ||||
| 				LD_RUNPATH_SEARCH_PATHS = ( | ||||
| 					"$(inherited)", | ||||
|  | @ -390,6 +392,8 @@ | |||
| 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||||
| 				DEVELOPMENT_TEAM = 69MW7VVKA9; | ||||
| 				INFOPLIST_FILE = GuruAnalytics/Info.plist; | ||||
| 				INFOPLIST_KEY_CFBundleDisplayName = GuruAnalytics; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 15.6; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 11.0; | ||||
| 				LD_RUNPATH_SEARCH_PATHS = ( | ||||
| 					"$(inherited)", | ||||
|  |  | |||
|  | @ -27,7 +27,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { | |||
|                                             batchLimit: 25, | ||||
|                                             initializeTimeout: 5, | ||||
|                                             saasXAPPID: "test_app_id", | ||||
|                                             saasXDEVICEINFO: "appIdentifier=test.app.example;appVersion=1.0;deviceType=apple;deviceCountry=CN") | ||||
|                                             saasXDEVICEINFO: "appIdentifier=test.app.example;appVersion=1.0;deviceType=apple;deviceCountry=CN", | ||||
|                                             guruSDKVersion: "1.0.0") | ||||
|                 GuruAnalytics.setUserID("100004") | ||||
|                 GuruAnalytics.setAdjustId("xsdfal021sxasdfl") | ||||
|                 GuruAnalytics.setDeviceId(UUID().uuidString) | ||||
|  |  | |||
|  | @ -1,7 +1,31 @@ | |||
| import UIKit | ||||
| import Foundation | ||||
| 
 | ||||
| var greeting = "Hello, playground" | ||||
| 
 | ||||
| let sss = ["1", "2", "3"].map({ "'\($0)'" | ||||
| }).joined(separator: ",") | ||||
| print("value in (\(sss))") | ||||
| 
 | ||||
| let number = NSNumber(7.0) | ||||
| let number2 = NSNumber(7) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| print("number is double: \(number is Double)") | ||||
| print("number is int: \(number is Int)") | ||||
| print("number value: \(number)") | ||||
| print("number type: \(number.objCType)") | ||||
|           extension NSNumber { | ||||
|               var type: CFNumberType { | ||||
|                   return CFNumberGetType(self as CFNumber) | ||||
|               } | ||||
|           } | ||||
| 
 | ||||
| print("number type: \(number.type)") | ||||
| print("number2 type: \(number2.type)") | ||||
| 
 | ||||
| let number3 = 7.0 | ||||
| 
 | ||||
| print("number3 is Double: \(number3 is Double)") | ||||
| print("number3 is Int: \(number3 is Int)") | ||||
| print("number3 is NSNumber: \(number3 is NSNumber)") | ||||
|  |  | |||
							
								
								
									
										7
									
								
								Example/GuruAnalytics/MyPlayground.playground/playground.xcworkspace/contents.xcworkspacedata
								
								
									generated
								
								
								
									Normal file
								
							
							
						
						
									
										7
									
								
								Example/GuruAnalytics/MyPlayground.playground/playground.xcworkspace/contents.xcworkspacedata
								
								
									generated
								
								
								
									Normal file
								
							|  | @ -0,0 +1,7 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <Workspace | ||||
|    version = "1.0"> | ||||
|    <FileRef | ||||
|       location = "self:"> | ||||
|    </FileRef> | ||||
| </Workspace> | ||||
|  | @ -23,15 +23,31 @@ class ViewController: UIViewController { | |||
|      | ||||
|     @IBAction func setFirebaseId(_ sender: Any) { | ||||
|         GuruAnalytics.setFirebaseId("2312:3XSFA0211231") | ||||
|         GuruAnalytics.setAppFlyersId("app_flyers_id:133323") | ||||
|     } | ||||
|      | ||||
|     @IBAction func create(_ sender: Any) { | ||||
|         GuruAnalytics.setScreen("home") | ||||
|         GuruAnalytics.logEvent("crate_clk_" + String(Int(Date().timeIntervalSince1970)), | ||||
|                                parameters: ["category": "category_\(Int.random(in: 0...100000))", | ||||
|                                             "int_v_test": 2147483647, "double_v_test": 200.1, | ||||
|                                             "int_v_test": 2147483647, "double_v_test_1": 200.1, | ||||
|                                             "double_v_test_2": NSNumber(7.00), | ||||
|                                             "double_v_test_3": 7.00, | ||||
|                                             "string_v_test": "400", | ||||
|                                             "long_v_test":  Int64(1)]) | ||||
|                                             "long_v_test":  Int64(1), | ||||
|                                             "long_v_test2": Int64.max, | ||||
|                                             "long_v_test3": Int64.min, | ||||
|                                             "value": 0.0]) | ||||
|         GuruAnalytics.logEvent("spend_virtual_currency", | ||||
|                                parameters: ["theme_name": "theme_bg_0029", | ||||
|                                             "theme_category": "theme", | ||||
|                                             "virtural_currency_name": "coins", | ||||
|                                             "value": 100, | ||||
|                                             "balance": 33810, | ||||
|                                             "scene": "theme", | ||||
|                                             "level_name": "Instance of I'm 'SettingIntDat'", | ||||
|                                            ]) | ||||
| 
 | ||||
|     } | ||||
|      | ||||
|     @IBAction func deleteItem(_ sender: Any) { | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ platform :ios, '11.0' | |||
| 
 | ||||
| target 'GuruAnalytics_Example' do | ||||
|   pod 'GuruAnalyticsLib', :path => '../' | ||||
| #  pod 'GuruAnalyticsLib', '0.3.8' | ||||
| #  pod 'GuruAnalyticsLib', '0.4.2' | ||||
| end | ||||
| 
 | ||||
| post_install do |installer| | ||||
|  |  | |||
|  | @ -34,7 +34,8 @@ public class GuruAnalytics: NSObject { | |||
|                                     initializeTimeout: Double = 5, | ||||
|                                     saasXAPPID: String, | ||||
|                                     saasXDEVICEINFO: String, | ||||
|                                     loggerDebug: Bool = true) { | ||||
|                                     loggerDebug: Bool = true, | ||||
|                                     guruSDKVersion: String) { | ||||
|         Self.uploadPeriodInSecond = uploadPeriodInSecond | ||||
|         Self.batchLimit = batchLimit | ||||
|         Self.eventExpiredSeconds = eventExpiredSeconds | ||||
|  | @ -42,6 +43,7 @@ public class GuruAnalytics: NSObject { | |||
|         Self.saasXAPPID = saasXAPPID | ||||
|         Self.saasXDEVICEINFO = saasXDEVICEINFO | ||||
|         Self.loggerDebug = loggerDebug | ||||
|         Constants.guruSDKVersion = guruSDKVersion | ||||
|         _ = Manager.shared | ||||
|     } | ||||
|      | ||||
|  | @ -81,6 +83,12 @@ public class GuruAnalytics: NSObject { | |||
|         setUserProperty(firebaseId, forName: .firebaseId) | ||||
|     } | ||||
|      | ||||
|     /// 设置appsflyerId | ||||
|     @objc | ||||
|     public class func setAppFlyersId(_ appFlyersId: String?) { | ||||
|         setUserProperty(appFlyersId, forName: .appsflyerId) | ||||
|     } | ||||
|      | ||||
|     /// screen name | ||||
|     @objc | ||||
|     public class func setScreen(_ name: String) { | ||||
|  | @ -155,4 +163,9 @@ public class GuruAnalytics: NSObject { | |||
|         enableUpload = isOn | ||||
|     } | ||||
| 
 | ||||
|     /// 设置中台库版本 | ||||
|     @objc | ||||
|     public class func setGuruSDKVersion(_ version: String) -> Void { | ||||
|         Constants.guruSDKVersion = version | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -157,21 +157,35 @@ extension Entity { | |||
|         } | ||||
|          | ||||
|         static func normalizeValue(_ value: Any) -> EventValue { | ||||
|              | ||||
|             var preprocessedValue = value | ||||
|              | ||||
|             if let val = preprocessedValue as? NSNumber { | ||||
|                 preprocessedValue = val.numricValue | ||||
|             } | ||||
|              | ||||
|             let eventValue: EventValue | ||||
|             if let value = value as? String { | ||||
|                 eventValue = Entity.EventValue(stringValue: String(value.prefix(maxParameterStringValueLength))) | ||||
|             } else if let value = value as? Int { | ||||
|             if let value = preprocessedValue as? String { | ||||
|                 eventValue = Entity.EventValue(stringValue: normalizeStringValue(String(value.prefix(maxParameterStringValueLength)))) | ||||
|             } else if let value = preprocessedValue as? Int { | ||||
|                 eventValue = Entity.EventValue(longValue: Int64(value)) | ||||
|             } else if let value = value as? Int64 { | ||||
|             } else if let value = preprocessedValue as? Int64 { | ||||
|                 eventValue = Entity.EventValue(longValue: value) | ||||
|             } else if let value = value as? Double { | ||||
|             } else if let value = preprocessedValue as? Double { | ||||
|                 eventValue = Entity.EventValue(doubleValue: value) | ||||
|             } else { | ||||
|                 eventValue = Entity.EventValue(stringValue: String("\(value)".prefix(maxParameterStringValueLength))) | ||||
|                 eventValue = Entity.EventValue(stringValue: normalizeStringValue(String("\(value)".prefix(maxParameterStringValueLength)))) | ||||
|             } | ||||
|             return eventValue | ||||
|         } | ||||
|          | ||||
|         static func normalizeStringValue(_ value: String) -> String { | ||||
|             let normalizedString = value.replacingOccurrences(of: "'", with: "''") | ||||
|             cdPrint("original string value: \(value)") | ||||
|             cdPrint("normalized string value: \(normalizedString)") | ||||
|             return normalizedString | ||||
|         } | ||||
|          | ||||
|         static func normalizeKey(_ key: String) -> String? { | ||||
|             var mutableKey = key | ||||
|              | ||||
|  | @ -205,6 +219,8 @@ extension Entity { | |||
|         let adId: String? | ||||
|         ///用户的pseudo_id | ||||
|         let firebaseId: String? | ||||
|         ///appsFlyerId | ||||
|         let appsflyerId: String? | ||||
|          | ||||
|         ///IDFV | ||||
|         let vendorId: String? = UIDevice().identifierForVendor?.uuidString | ||||
|  | @ -216,6 +232,7 @@ extension Entity { | |||
|             case adId | ||||
|             case firebaseId | ||||
|             case vendorId | ||||
|             case appsflyerId | ||||
|         } | ||||
|     } | ||||
|      | ||||
|  |  | |||
|  | @ -270,7 +270,8 @@ internal extension Manager { | |||
|                             deviceId: userProperty.removeValue(forKey: PropertyName.deviceId.rawValue), | ||||
|                             adjustId: userProperty.removeValue(forKey: PropertyName.adjustId.rawValue), | ||||
|                             adId: userProperty.removeValue(forKey: PropertyName.adId.rawValue), | ||||
|                             firebaseId: userProperty.removeValue(forKey: PropertyName.firebaseId.rawValue) | ||||
|                             firebaseId: userProperty.removeValue(forKey: PropertyName.firebaseId.rawValue), | ||||
|                             appsflyerId: userProperty.removeValue(forKey: PropertyName.appsflyerId.rawValue) | ||||
|                         ) | ||||
|                          | ||||
|                         let event = try Entity.Event(timestamp: timestamp, | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ internal extension GuruAnalytics { | |||
|         case firebaseId | ||||
|         case screen = "screen_name" | ||||
|         case firstOpenTime = "first_open_time" | ||||
|         case appsflyerId | ||||
|     } | ||||
|      | ||||
|     ///built-in events | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ internal struct Constants { | |||
|         return shortVersion | ||||
|     }() | ||||
|      | ||||
|     private static let sdkVersion: String = { | ||||
|     private static let guruAnalyticsSDKVersion: String = { | ||||
|         guard let infoDict = Bundle(for: Manager.self).infoDictionary, | ||||
|               let currentVersion = infoDict["CFBundleShortVersionString"] as? String else { | ||||
|             return "" | ||||
|  | @ -34,6 +34,9 @@ internal struct Constants { | |||
|         return currentVersion | ||||
|     }() | ||||
|      | ||||
|     /// 中台库版本,由外部更新 | ||||
|     static var guruSDKVersion: String = ""; | ||||
|      | ||||
|     private static let preferredLocale: Locale = { | ||||
|         guard let preferredIdentifier = Locale.preferredLanguages.first else { | ||||
|             return Locale.current | ||||
|  | @ -92,7 +95,8 @@ internal struct Constants { | |||
|             "screenW": Int(screenSize.w), | ||||
|             "osVersion": systemVersion, | ||||
|             "language" : languageCode, | ||||
|             "sdkVersion" : sdkVersion, | ||||
|             "guruAnalyticsVersion" : guruAnalyticsSDKVersion, | ||||
|             "gurusdkVersion" : guruSDKVersion, | ||||
|         ] | ||||
|     } | ||||
|      | ||||
|  | @ -102,6 +106,50 @@ internal struct Constants { | |||
|     /// - returns: raw `String` of device type, e.g. iPhone5,1 | ||||
|     /// | ||||
|     private static func hardwareString() -> String { | ||||
|         var name: [Int32] = [CTL_HW, HW_MACHINE] | ||||
|         var size: size_t = 0 | ||||
|          | ||||
|         // 🛡️ 安全检查1:获取缓冲区大小 | ||||
|         guard sysctl(&name, 2, nil, &size, nil, 0) == 0, | ||||
|               size > 0 && size < 256 else { | ||||
|             return "iPhone14,1" // 安全默认值 | ||||
|         } | ||||
|          | ||||
|         // 🛡️ 安全检查2:创建缓冲区 | ||||
|         let bufferSize = Int(size) + 1 | ||||
|         var hw_machine = [CChar](repeating: 0, count: bufferSize) | ||||
|         var actualSize = size | ||||
|          | ||||
|         // 🛡️ 安全检查3:获取数据 | ||||
|         guard sysctl(&name, 2, &hw_machine, &actualSize, nil, 0) == 0 else { | ||||
|             return "iPhone14,1" // 安全默认值 | ||||
|         } | ||||
|          | ||||
|         // 🛡️ 安全检查4:确保null终止(防止越界) | ||||
|         let safeIndex = min(Int(actualSize), bufferSize - 1) | ||||
|         hw_machine[safeIndex] = 0 | ||||
|          | ||||
|         var hardware: String = String(cString: hw_machine) | ||||
|          | ||||
|         // 🛡️ 安全检查5:验证结果 | ||||
|         if hardware.isEmpty { | ||||
|             return "iPhone14,1" // 安全默认值 | ||||
|         } | ||||
|          | ||||
|         // Check for simulator | ||||
|         if hardware == "x86_64" || hardware == "i386" || hardware == "arm64" { | ||||
|             if let deviceID = ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIFIER"] { | ||||
|                 hardware = deviceID | ||||
|             } else { | ||||
|                 hardware = "Simulator" | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         return hardware | ||||
|     } | ||||
|      | ||||
|     //这里不可用,有崩溃!!!用上边的方法!! | ||||
|     private static func hardwareStringError() -> String { | ||||
|         var name: [Int32] = [CTL_HW, HW_MACHINE] | ||||
|         var size: Int = 2 | ||||
|         sysctl(&name, 2, nil, &size, nil, 0) | ||||
|  | @ -116,10 +164,12 @@ internal struct Constants { | |||
|                 hardware = deviceID | ||||
|             } | ||||
|         } | ||||
|          | ||||
|      | ||||
|         return hardware | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|      | ||||
|     /// This method returns the Platform enum depending upon harware string | ||||
|     /// | ||||
|     /// | ||||
|  |  | |||
|  | @ -0,0 +1,43 @@ | |||
| // | ||||
| //  Untitled.swift | ||||
| //  Pods | ||||
| // | ||||
| //  Created by mayue on 2025/1/14. | ||||
| // | ||||
| 
 | ||||
| extension NSNumber { | ||||
|     var valueType: CFNumberType { | ||||
|         return CFNumberGetType(self as CFNumber) | ||||
|     } | ||||
|      | ||||
|     var numricValue: Any { | ||||
|         switch valueType { | ||||
|         case .sInt8Type, | ||||
|                 .sInt16Type, | ||||
|                 .sInt32Type, | ||||
|                 .charType, | ||||
|                 .shortType, | ||||
|                 .intType, | ||||
|                 .longType, | ||||
|                 .cfIndexType, | ||||
|                 .nsIntegerType: | ||||
|             return intValue; | ||||
|              | ||||
|         case | ||||
|                 .sInt64Type, | ||||
|                 .longLongType: | ||||
|             return int64Value; | ||||
|              | ||||
|         case .float32Type, | ||||
|                 .float64Type, | ||||
|                 .floatType, | ||||
|                 .doubleType, | ||||
|                 .cgFloatType, | ||||
|                 .maxType: | ||||
|             return doubleValue; | ||||
|              | ||||
|         @unknown default: | ||||
|             return doubleValue; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -8,7 +8,7 @@ | |||
| 
 | ||||
| Pod::Spec.new do |s| | ||||
|   s.name             = 'GuruAnalyticsLib' | ||||
|   s.version          = '0.3.8' | ||||
|   s.version          = '0.4.4' | ||||
|   s.summary          = 'A short description of GuruAnalytics.' | ||||
| 
 | ||||
| # This description is used to generate tags and improve search results. | ||||
|  | @ -25,7 +25,7 @@ TODO: Add long description of the pod here. | |||
|   # s.screenshots     = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' | ||||
|   s.license          = { :type => 'MIT', :file => 'LICENSE' } | ||||
|   s.author           = { 'devSC' => 'xiaochong2154@163.com' } | ||||
|   s.source           = { :git => 'git@github.com:castbox/GuruAnalytics_iOS.git', :tag => s.version.to_s } | ||||
|   s.source           = { :git => 'git@git.chengdu.pundit.company:castbox/GuruAnalytics_iOS.git', :tag => s.version.to_s } | ||||
|   # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>' | ||||
| 
 | ||||
|   s.ios.deployment_target = '11.0' | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue