Signed-off-by: Haoyi <haoyi.zhang@castbox.fm>
main 0.4.3
Haoyi 2025-08-08 18:12:14 +08:00
parent 0ad881ca66
commit 8163f171d2
14 changed files with 244 additions and 18 deletions

View File

@ -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 ## v0.3.8
- fix - fix
- 隐私文件增加divice id声明 - 隐私文件增加divice id声明
@ -15,6 +60,10 @@
- deviceInfo.sdkVersion - deviceInfo.sdkVersion
- info.vendorId - info.vendorId
## v0.3.6.1
- fix:
- 更新privacy manifest文件移除tracking domains下的空条目。
## v0.3.6 ## v0.3.6
- fix: - fix:
- 增加第三方依赖库版本约束 - 增加第三方依赖库版本约束

View File

@ -370,6 +370,8 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = 69MW7VVKA9; DEVELOPMENT_TEAM = 69MW7VVKA9;
INFOPLIST_FILE = GuruAnalytics/Info.plist; INFOPLIST_FILE = GuruAnalytics/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = GuruAnalytics;
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
IPHONEOS_DEPLOYMENT_TARGET = 11.0; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@ -390,6 +392,8 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = 69MW7VVKA9; DEVELOPMENT_TEAM = 69MW7VVKA9;
INFOPLIST_FILE = GuruAnalytics/Info.plist; INFOPLIST_FILE = GuruAnalytics/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = GuruAnalytics;
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
IPHONEOS_DEPLOYMENT_TARGET = 11.0; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",

View File

@ -27,7 +27,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
batchLimit: 25, batchLimit: 25,
initializeTimeout: 5, initializeTimeout: 5,
saasXAPPID: "test_app_id", 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.setUserID("100004")
GuruAnalytics.setAdjustId("xsdfal021sxasdfl") GuruAnalytics.setAdjustId("xsdfal021sxasdfl")
GuruAnalytics.setDeviceId(UUID().uuidString) GuruAnalytics.setDeviceId(UUID().uuidString)

View File

@ -1,7 +1,31 @@
import UIKit import Foundation
var greeting = "Hello, playground" var greeting = "Hello, playground"
let sss = ["1", "2", "3"].map({ "'\($0)'" let sss = ["1", "2", "3"].map({ "'\($0)'"
}).joined(separator: ",") }).joined(separator: ",")
print("value in (\(sss))") 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)")

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -23,15 +23,31 @@ class ViewController: UIViewController {
@IBAction func setFirebaseId(_ sender: Any) { @IBAction func setFirebaseId(_ sender: Any) {
GuruAnalytics.setFirebaseId("2312:3XSFA0211231") GuruAnalytics.setFirebaseId("2312:3XSFA0211231")
GuruAnalytics.setAppFlyersId("app_flyers_id:133323")
} }
@IBAction func create(_ sender: Any) { @IBAction func create(_ sender: Any) {
GuruAnalytics.setScreen("home") GuruAnalytics.setScreen("home")
GuruAnalytics.logEvent("crate_clk_" + String(Int(Date().timeIntervalSince1970)), GuruAnalytics.logEvent("crate_clk_" + String(Int(Date().timeIntervalSince1970)),
parameters: ["category": "category_\(Int.random(in: 0...100000))", 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", "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) { @IBAction func deleteItem(_ sender: Any) {

View File

@ -7,7 +7,7 @@ platform :ios, '11.0'
target 'GuruAnalytics_Example' do target 'GuruAnalytics_Example' do
pod 'GuruAnalyticsLib', :path => '../' pod 'GuruAnalyticsLib', :path => '../'
# pod 'GuruAnalyticsLib', '0.3.8' # pod 'GuruAnalyticsLib', '0.4.2'
end end
post_install do |installer| post_install do |installer|

View File

@ -34,7 +34,8 @@ public class GuruAnalytics: NSObject {
initializeTimeout: Double = 5, initializeTimeout: Double = 5,
saasXAPPID: String, saasXAPPID: String,
saasXDEVICEINFO: String, saasXDEVICEINFO: String,
loggerDebug: Bool = true) { loggerDebug: Bool = true,
guruSDKVersion: String) {
Self.uploadPeriodInSecond = uploadPeriodInSecond Self.uploadPeriodInSecond = uploadPeriodInSecond
Self.batchLimit = batchLimit Self.batchLimit = batchLimit
Self.eventExpiredSeconds = eventExpiredSeconds Self.eventExpiredSeconds = eventExpiredSeconds
@ -42,6 +43,7 @@ public class GuruAnalytics: NSObject {
Self.saasXAPPID = saasXAPPID Self.saasXAPPID = saasXAPPID
Self.saasXDEVICEINFO = saasXDEVICEINFO Self.saasXDEVICEINFO = saasXDEVICEINFO
Self.loggerDebug = loggerDebug Self.loggerDebug = loggerDebug
Constants.guruSDKVersion = guruSDKVersion
_ = Manager.shared _ = Manager.shared
} }
@ -81,6 +83,12 @@ public class GuruAnalytics: NSObject {
setUserProperty(firebaseId, forName: .firebaseId) setUserProperty(firebaseId, forName: .firebaseId)
} }
/// appsflyerId
@objc
public class func setAppFlyersId(_ appFlyersId: String?) {
setUserProperty(appFlyersId, forName: .appsflyerId)
}
/// screen name /// screen name
@objc @objc
public class func setScreen(_ name: String) { public class func setScreen(_ name: String) {
@ -155,4 +163,9 @@ public class GuruAnalytics: NSObject {
enableUpload = isOn enableUpload = isOn
} }
///
@objc
public class func setGuruSDKVersion(_ version: String) -> Void {
Constants.guruSDKVersion = version
}
} }

View File

@ -157,21 +157,35 @@ extension Entity {
} }
static func normalizeValue(_ value: Any) -> EventValue { static func normalizeValue(_ value: Any) -> EventValue {
var preprocessedValue = value
if let val = preprocessedValue as? NSNumber {
preprocessedValue = val.numricValue
}
let eventValue: EventValue let eventValue: EventValue
if let value = value as? String { if let value = preprocessedValue as? String {
eventValue = Entity.EventValue(stringValue: String(value.prefix(maxParameterStringValueLength))) eventValue = Entity.EventValue(stringValue: normalizeStringValue(String(value.prefix(maxParameterStringValueLength))))
} else if let value = value as? Int { } else if let value = preprocessedValue as? Int {
eventValue = Entity.EventValue(longValue: Int64(value)) 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) eventValue = Entity.EventValue(longValue: value)
} else if let value = value as? Double { } else if let value = preprocessedValue as? Double {
eventValue = Entity.EventValue(doubleValue: value) eventValue = Entity.EventValue(doubleValue: value)
} else { } else {
eventValue = Entity.EventValue(stringValue: String("\(value)".prefix(maxParameterStringValueLength))) eventValue = Entity.EventValue(stringValue: normalizeStringValue(String("\(value)".prefix(maxParameterStringValueLength))))
} }
return eventValue 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? { static func normalizeKey(_ key: String) -> String? {
var mutableKey = key var mutableKey = key
@ -205,6 +219,8 @@ extension Entity {
let adId: String? let adId: String?
///pseudo_id ///pseudo_id
let firebaseId: String? let firebaseId: String?
///appsFlyerId
let appsflyerId: String?
///IDFV ///IDFV
let vendorId: String? = UIDevice().identifierForVendor?.uuidString let vendorId: String? = UIDevice().identifierForVendor?.uuidString
@ -216,6 +232,7 @@ extension Entity {
case adId case adId
case firebaseId case firebaseId
case vendorId case vendorId
case appsflyerId
} }
} }

View File

@ -270,7 +270,8 @@ internal extension Manager {
deviceId: userProperty.removeValue(forKey: PropertyName.deviceId.rawValue), deviceId: userProperty.removeValue(forKey: PropertyName.deviceId.rawValue),
adjustId: userProperty.removeValue(forKey: PropertyName.adjustId.rawValue), adjustId: userProperty.removeValue(forKey: PropertyName.adjustId.rawValue),
adId: userProperty.removeValue(forKey: PropertyName.adId.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, let event = try Entity.Event(timestamp: timestamp,

View File

@ -18,6 +18,7 @@ internal extension GuruAnalytics {
case firebaseId case firebaseId
case screen = "screen_name" case screen = "screen_name"
case firstOpenTime = "first_open_time" case firstOpenTime = "first_open_time"
case appsflyerId
} }
///built-in events ///built-in events

View File

@ -26,7 +26,7 @@ internal struct Constants {
return shortVersion return shortVersion
}() }()
private static let sdkVersion: String = { private static let guruAnalyticsSDKVersion: String = {
guard let infoDict = Bundle(for: Manager.self).infoDictionary, guard let infoDict = Bundle(for: Manager.self).infoDictionary,
let currentVersion = infoDict["CFBundleShortVersionString"] as? String else { let currentVersion = infoDict["CFBundleShortVersionString"] as? String else {
return "" return ""
@ -34,6 +34,9 @@ internal struct Constants {
return currentVersion return currentVersion
}() }()
///
static var guruSDKVersion: String = "";
private static let preferredLocale: Locale = { private static let preferredLocale: Locale = {
guard let preferredIdentifier = Locale.preferredLanguages.first else { guard let preferredIdentifier = Locale.preferredLanguages.first else {
return Locale.current return Locale.current
@ -92,7 +95,8 @@ internal struct Constants {
"screenW": Int(screenSize.w), "screenW": Int(screenSize.w),
"osVersion": systemVersion, "osVersion": systemVersion,
"language" : languageCode, "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 /// - returns: raw `String` of device type, e.g. iPhone5,1
/// ///
private static func hardwareString() -> String { 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" //
}
// 🛡 4null
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 name: [Int32] = [CTL_HW, HW_MACHINE]
var size: Int = 2 var size: Int = 2
sysctl(&name, 2, nil, &size, nil, 0) sysctl(&name, 2, nil, &size, nil, 0)
@ -116,10 +164,12 @@ internal struct Constants {
hardware = deviceID hardware = deviceID
} }
} }
return hardware return hardware
} }
/// This method returns the Platform enum depending upon harware string /// This method returns the Platform enum depending upon harware string
/// ///
/// ///

View File

@ -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;
}
}
}

View File

@ -8,7 +8,7 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = 'GuruAnalyticsLib' s.name = 'GuruAnalyticsLib'
s.version = '0.3.8' s.version = '0.4.4'
s.summary = 'A short description of GuruAnalytics.' s.summary = 'A short description of GuruAnalytics.'
# This description is used to generate tags and improve search results. # 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.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
s.license = { :type => 'MIT', :file => 'LICENSE' } s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'devSC' => 'xiaochong2154@163.com' } 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.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
s.ios.deployment_target = '11.0' s.ios.deployment_target = '11.0'