parent
de45dd6e81
commit
0ad881ca66
21
CHANGELOG.md
21
CHANGELOG.md
|
|
@ -1,3 +1,24 @@
|
|||
## v0.3.8
|
||||
- fix
|
||||
- 隐私文件增加divice id声明
|
||||
|
||||
## v0.3.7
|
||||
- feature
|
||||
- 增加事件
|
||||
- guru_sdk_init_start
|
||||
- guru_sdk_init_complete
|
||||
- session_start
|
||||
- guru_engagement
|
||||
- 增加事件参数
|
||||
- session_number
|
||||
- session_id
|
||||
- deviceInfo.sdkVersion
|
||||
- info.vendorId
|
||||
|
||||
## v0.3.6
|
||||
- fix:
|
||||
- 增加第三方依赖库版本约束
|
||||
|
||||
## v0.3.5
|
||||
- 接口更新:
|
||||
- 日志打包方法eventsLogsArchive废弃,使用eventsLogsDirectory获取文件夹URL
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 51;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
|
|
@ -128,7 +128,7 @@
|
|||
607FACCC1AFB9204008FA782 /* Sources */,
|
||||
607FACCD1AFB9204008FA782 /* Frameworks */,
|
||||
607FACCE1AFB9204008FA782 /* Resources */,
|
||||
86E7176C5034831947DC0310 /* [CP] Embed Pods Frameworks */,
|
||||
65438729BAC744B5EACA2945 /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
|
|
@ -210,7 +210,7 @@
|
|||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
86E7176C5034831947DC0310 /* [CP] Embed Pods Frameworks */ = {
|
||||
65438729BAC744B5EACA2945 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
|
|
@ -368,7 +368,7 @@
|
|||
baseConfigurationReference = D30C5441D6D5CBD750E76657 /* Pods-GuruAnalytics_Example.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
DEVELOPMENT_TEAM = 43U6TB4QK3;
|
||||
DEVELOPMENT_TEAM = 69MW7VVKA9;
|
||||
INFOPLIST_FILE = GuruAnalytics/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
|
|
@ -388,7 +388,7 @@
|
|||
baseConfigurationReference = F992AC1E7C4013032773C33F /* Pods-GuruAnalytics_Example.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
DEVELOPMENT_TEAM = 43U6TB4QK3;
|
||||
DEVELOPMENT_TEAM = 69MW7VVKA9;
|
||||
INFOPLIST_FILE = GuruAnalytics/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ class ViewController: UIViewController {
|
|||
}
|
||||
|
||||
@IBAction func getLogs(_ sender: UIButton) {
|
||||
GuruAnalytics.eventsLogsArchive({ [weak self] url in
|
||||
GuruAnalytics.eventsLogsDirectory({ [weak self] url in
|
||||
guard let `self` = self, let url = url else { return }
|
||||
|
||||
if MFMailComposeViewController.canSendMail() {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ platform :ios, '11.0'
|
|||
|
||||
target 'GuruAnalytics_Example' do
|
||||
pod 'GuruAnalyticsLib', :path => '../'
|
||||
# pod 'GuruAnalyticsLib', '0.3.4'
|
||||
# pod 'GuruAnalyticsLib', '0.3.8'
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
|
|
|
|||
|
|
@ -2,6 +2,21 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyCollectedDataType</key>
|
||||
<string>NSPrivacyCollectedDataTypeDeviceID</string>
|
||||
<key>NSPrivacyCollectedDataTypeLinked</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypeTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypePurposes</key>
|
||||
<array>
|
||||
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
|
|
@ -34,8 +49,6 @@
|
|||
<key>NSPrivacyTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyTrackingDomains</key>
|
||||
<array>
|
||||
<string></string>
|
||||
</array>
|
||||
<array/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -124,8 +124,16 @@ extension Entity {
|
|||
|
||||
static func normalizeParameters(_ parameters: [String : Any]) -> [String : EventValue] {
|
||||
var params = [String : EventValue]()
|
||||
var allParams = parameters;
|
||||
|
||||
GuruAnalytics.BuiltinParametersKeys.allCases.forEach { paramKey in
|
||||
if let value = allParams.removeValue(forKey: paramKey.rawValue) {
|
||||
params[paramKey.rawValue] = normalizeValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
var count = 0
|
||||
parameters.sorted(by: { $0.key < $1.key }).forEach({ key, value in
|
||||
allParams.sorted(by: { $0.key < $1.key }).forEach({ key, value in
|
||||
|
||||
guard count < maxParametersCount else {
|
||||
cdPrint("too many parameters")
|
||||
|
|
@ -140,17 +148,7 @@ extension Entity {
|
|||
return
|
||||
}
|
||||
|
||||
if let value = value as? String {
|
||||
params[normalizedKey] = Entity.EventValue(stringValue: String(value.prefix(maxParameterStringValueLength)))
|
||||
} else if let value = value as? Int {
|
||||
params[normalizedKey] = Entity.EventValue(longValue: Int64(value))
|
||||
} else if let value = value as? Int64 {
|
||||
params[normalizedKey] = Entity.EventValue(longValue: value)
|
||||
} else if let value = value as? Double {
|
||||
params[normalizedKey] = Entity.EventValue(doubleValue: value)
|
||||
} else {
|
||||
params[normalizedKey] = Entity.EventValue(stringValue: String("\(value)".prefix(maxParameterStringValueLength)))
|
||||
}
|
||||
params[normalizedKey] = normalizeValue(value)
|
||||
|
||||
count += 1
|
||||
})
|
||||
|
|
@ -158,6 +156,22 @@ extension Entity {
|
|||
return params
|
||||
}
|
||||
|
||||
static func normalizeValue(_ value: Any) -> EventValue {
|
||||
let eventValue: EventValue
|
||||
if let value = value as? String {
|
||||
eventValue = Entity.EventValue(stringValue: String(value.prefix(maxParameterStringValueLength)))
|
||||
} else if let value = value as? Int {
|
||||
eventValue = Entity.EventValue(longValue: Int64(value))
|
||||
} else if let value = value as? Int64 {
|
||||
eventValue = Entity.EventValue(longValue: value)
|
||||
} else if let value = value as? Double {
|
||||
eventValue = Entity.EventValue(doubleValue: value)
|
||||
} else {
|
||||
eventValue = Entity.EventValue(stringValue: String("\(value)".prefix(maxParameterStringValueLength)))
|
||||
}
|
||||
return eventValue
|
||||
}
|
||||
|
||||
static func normalizeKey(_ key: String) -> String? {
|
||||
var mutableKey = key
|
||||
|
||||
|
|
@ -192,12 +206,16 @@ extension Entity {
|
|||
///用户的pseudo_id
|
||||
let firebaseId: String?
|
||||
|
||||
///IDFV
|
||||
let vendorId: String? = UIDevice().identifierForVendor?.uuidString
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case deviceId
|
||||
case uid
|
||||
case adjustId
|
||||
case adId
|
||||
case firebaseId
|
||||
case vendorId
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -226,3 +244,23 @@ extension Entity {
|
|||
let data: Int64
|
||||
}
|
||||
}
|
||||
|
||||
extension Entity {
|
||||
struct Session {
|
||||
let id: UUID = UUID()
|
||||
|
||||
var sessionId: Int {
|
||||
return id.uuidString.hash
|
||||
}
|
||||
}
|
||||
|
||||
struct SessionNumber: Codable {
|
||||
var number: Int
|
||||
let createdAtMs: Int64
|
||||
|
||||
static func createNumber() -> SessionNumber {
|
||||
return SessionNumber(number: 0, createdAtMs: Date().msSince1970)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -176,6 +176,40 @@ WHERE \(Entity.EventRecord.CodingKeys.timestamp.rawValue) < \(earlierThan)
|
|||
})
|
||||
}
|
||||
|
||||
func fetchOutdatedEventRecords(earlierThan: Int64) -> Single<[Entity.EventRecord]> {
|
||||
return mapTransactionToSingle { (db) in
|
||||
let querySQL: String =
|
||||
"""
|
||||
SELECT * FROM \(TableName.event.rawValue)
|
||||
WHERE \(Entity.EventRecord.CodingKeys.timestamp.rawValue) < \(earlierThan)
|
||||
"""
|
||||
cdPrint(#function + "query sql: \(querySQL)")
|
||||
let results = try db.executeQuery(querySQL, values: nil) //[ASC | DESC]
|
||||
var t: [Entity.EventRecord] = []
|
||||
while results.next() {
|
||||
guard let recordId = results.string(forColumnIndex: 0),
|
||||
let eventName = results.string(forColumnIndex: 1),
|
||||
let eventJson = results.string(forColumnIndex: 2) else {
|
||||
continue
|
||||
}
|
||||
|
||||
let priority: Int = results.columnIsNull(Entity.EventRecord.CodingKeys.priority.rawValue) ?
|
||||
Entity.EventRecord.Priority.DEFAULT.rawValue : Int(results.int(forColumn: Entity.EventRecord.CodingKeys.priority.rawValue))
|
||||
|
||||
let ts: Int = results.columnIsNull(Entity.EventRecord.CodingKeys.transitionStatus.rawValue) ?
|
||||
Entity.EventRecord.TransitionStatus.idle.rawValue : Int(results.int(forColumn: Entity.EventRecord.CodingKeys.transitionStatus.rawValue))
|
||||
|
||||
let record = Entity.EventRecord(recordId: recordId, eventName: eventName, eventJson: eventJson,
|
||||
timestamp: results.longLongInt(forColumn: Entity.EventRecord.CodingKeys.timestamp.rawValue),
|
||||
priority: priority, transitionStatus: ts)
|
||||
t.append(record)
|
||||
}
|
||||
|
||||
results.close()
|
||||
return t
|
||||
}
|
||||
}
|
||||
|
||||
func resetTransitionStatus(for recordIds: [String]) -> Single<Void> {
|
||||
guard !recordIds.isEmpty else {
|
||||
return .just(())
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ internal class Manager {
|
|||
// MARK: - private members
|
||||
|
||||
private typealias PropertyName = GuruAnalytics.PropertyName
|
||||
private typealias BuiltinParametersKeys = GuruAnalytics.BuiltinParametersKeys
|
||||
|
||||
private let bag = DisposeBag()
|
||||
|
||||
|
|
@ -170,8 +171,23 @@ internal class Manager {
|
|||
private typealias InternalEventReporter = ((_ eventCode: Int, _ info: String) -> Void)
|
||||
private var internalEventReporter: InternalEventReporter?
|
||||
|
||||
private lazy var session = Entity.Session()
|
||||
private lazy var sessionNumber: Entity.SessionNumber = {
|
||||
var sessionNumber = UserDefaults.sessionNumber
|
||||
let dayGaps = Calendar.current.dateComponents([.day], from: Date(timeIntervalSince1970: Double(sessionNumber.createdAtMs) / 1000), to: Date()).day ?? 0
|
||||
if (dayGaps > 0) {
|
||||
sessionNumber = Entity.SessionNumber.createNumber()
|
||||
}
|
||||
sessionNumber.number += 1
|
||||
UserDefaults.sessionNumber = sessionNumber
|
||||
return sessionNumber
|
||||
}()
|
||||
|
||||
private init() {
|
||||
|
||||
//
|
||||
logSDKInitStart()
|
||||
|
||||
// first open
|
||||
logFirstOpenIfNeeded()
|
||||
|
||||
|
|
@ -188,6 +204,12 @@ internal class Manager {
|
|||
logFirstFgEvent()
|
||||
|
||||
ntwkMgr.networkErrorReporter = self
|
||||
|
||||
//
|
||||
logSDKInitComplete()
|
||||
|
||||
//
|
||||
logSessionStart()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -229,7 +251,7 @@ internal extension Manager {
|
|||
priority: Entity.EventRecord.Priority) -> Single<Entity.EventRecord> {
|
||||
|
||||
return userProperty.take(1).observe(on: rxWorkScheduler).asSingle().flatMap { p in
|
||||
.create { subscriber in
|
||||
.create { [weak self] subscriber in
|
||||
do {
|
||||
debugPrint("userProperty thread queueName: \(Thread.current.queueName) count: \(p.count)")
|
||||
var userProperty = p
|
||||
|
|
@ -240,6 +262,9 @@ internal extension Manager {
|
|||
eventParam[PropertyName.screen.rawValue] = screen
|
||||
}
|
||||
|
||||
eventParam[BuiltinParametersKeys.sessionId.rawValue] = self?.session.sessionId
|
||||
eventParam[BuiltinParametersKeys.sessionNo.rawValue] = self?.sessionNumber.number
|
||||
|
||||
let userInfo = Entity.UserInfo(
|
||||
uid: userProperty.removeValue(forKey: PropertyName.uid.rawValue),
|
||||
deviceId: userProperty.removeValue(forKey: PropertyName.deviceId.rawValue),
|
||||
|
|
@ -361,16 +386,30 @@ private extension Manager {
|
|||
|
||||
/// 1. 删除过期的数据
|
||||
serverNowMsSingle
|
||||
.flatMap({ [weak self] serverNowMs -> Single<Void> in
|
||||
guard let `self` = self else { return .just(()) }
|
||||
.flatMap({ [weak self] serverNowMs -> Single<[Entity.EventRecord]> in
|
||||
guard let `self` = self else {
|
||||
return .error(NSError(domain: "com.guru.analytics.manager",
|
||||
code: 0,
|
||||
userInfo: [NSLocalizedDescriptionKey : "Manager released"]
|
||||
))
|
||||
}
|
||||
let earlierThan: Int64 = serverNowMs - self.eventExpiredIntervel.int64Ms
|
||||
return self.db.removeOutdatedEventRecords(earlierThan: earlierThan)
|
||||
return self.db.fetchOutdatedEventRecords(earlierThan: earlierThan)
|
||||
})
|
||||
.flatMap({ [weak self] records in
|
||||
return self?.db.deleteEventRecords(records.map { $0.recordId })
|
||||
.map { _ in records.count } ??
|
||||
.error(NSError(domain: "com.guru.analytics.manager",
|
||||
code: 0,
|
||||
userInfo: [NSLocalizedDescriptionKey : "Manager released"]
|
||||
))
|
||||
})
|
||||
.catch({ error in
|
||||
cdPrint("remove outdated records error: \(error)")
|
||||
return .just(())
|
||||
return .just(0)
|
||||
})
|
||||
.subscribe(onSuccess: { [weak self] _ in
|
||||
.subscribe(onSuccess: { [weak self] deletedCount in
|
||||
UserDefaults.deletedEventsCount += deletedCount
|
||||
self?.outdatedEventsCleared.onNext(true)
|
||||
})
|
||||
.disposed(by: bag)
|
||||
|
|
@ -417,6 +456,7 @@ private extension Manager {
|
|||
.flatMap { self.db.addEventRecords($0) }
|
||||
.do(onSuccess: { _ in
|
||||
self.accumulateLoggedEventsCount(1)
|
||||
UserDefaults.totalEventsCount += 1
|
||||
self.eventsLogger.verbose("log event success")
|
||||
}, onError: { error in
|
||||
self.eventsLogger.error("log event error: \(error)")
|
||||
|
|
@ -424,6 +464,28 @@ private extension Manager {
|
|||
}()
|
||||
}
|
||||
|
||||
func logSDKInitStart() {
|
||||
_logEvent(GuruAnalytics.sdkInitStartEvent.name, parameters: [
|
||||
GuruAnalytics.sdkInitStartEvent.paramKeyType.totalEvents.rawValue : UserDefaults.totalEventsCount,
|
||||
GuruAnalytics.sdkInitStartEvent.paramKeyType.deletedEvents.rawValue : UserDefaults.deletedEventsCount,
|
||||
GuruAnalytics.sdkInitStartEvent.paramKeyType.uploadedEvents.rawValue : UserDefaults.uploadedEventsCount,
|
||||
], priority: .HIGH)
|
||||
.subscribe()
|
||||
.disposed(by: bag)
|
||||
}
|
||||
|
||||
func logSDKInitComplete() {
|
||||
_logEvent(GuruAnalytics.sdkInitCompleteEvent.name, parameters: [
|
||||
GuruAnalytics.sdkInitCompleteEvent.paramKeyType.duration.rawValue : Date().msSince1970 - startAt.msSince1970,
|
||||
], priority: .HIGH)
|
||||
.subscribe()
|
||||
.disposed(by: bag)
|
||||
}
|
||||
func logSessionStart() {
|
||||
_logEvent(GuruAnalytics.sessionStartEvent.name, parameters: nil, priority: .HIGH)
|
||||
.subscribe()
|
||||
.disposed(by: bag)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 轮询上传相关
|
||||
|
|
@ -536,6 +598,7 @@ private extension Manager {
|
|||
}
|
||||
.flatMap { recordIDs -> Single<Void> in
|
||||
self.accumulateUploadedEventsCount(recordIDs.count)
|
||||
UserDefaults.uploadedEventsCount += recordIDs.count
|
||||
/// step4: 删除数据库中对应记录
|
||||
return self.db.deleteEventRecords(recordIDs)
|
||||
.catch { error in
|
||||
|
|
|
|||
|
|
@ -39,6 +39,48 @@ internal enum UserDefaults {
|
|||
}
|
||||
}
|
||||
|
||||
static var totalEventsCount: Int {
|
||||
get {
|
||||
return defaults?.value(forKey: totalEventsCountKey) as? Int ?? 0
|
||||
}
|
||||
|
||||
set {
|
||||
defaults?.set(newValue, forKey: totalEventsCountKey)
|
||||
}
|
||||
}
|
||||
|
||||
static var deletedEventsCount: Int {
|
||||
get {
|
||||
return defaults?.value(forKey: deletedEventsCountKey) as? Int ?? 0
|
||||
}
|
||||
|
||||
set {
|
||||
defaults?.set(newValue, forKey: deletedEventsCountKey)
|
||||
}
|
||||
}
|
||||
|
||||
static var uploadedEventsCount: Int {
|
||||
get {
|
||||
return defaults?.value(forKey: uploadedEventsCountKey) as? Int ?? 0
|
||||
}
|
||||
|
||||
set {
|
||||
defaults?.set(newValue, forKey: uploadedEventsCountKey)
|
||||
}
|
||||
}
|
||||
|
||||
static var sessionNumber: Entity.SessionNumber {
|
||||
get {
|
||||
let jsonString = defaults?.value(forKey: sessionNumberKey) as? String ?? ""
|
||||
let sessionNumber = JSONDecoder().decodeObject(Entity.SessionNumber.self, from: jsonString)
|
||||
?? Entity.SessionNumber.createNumber()
|
||||
return sessionNumber
|
||||
}
|
||||
set {
|
||||
let jsonString = newValue.asString
|
||||
defaults?.setValue(jsonString, forKey: sessionNumberKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension UserDefaults {
|
||||
|
|
@ -62,4 +104,21 @@ extension UserDefaults {
|
|||
static var fgDurationKey: String {
|
||||
return "fg.duration.ms"
|
||||
}
|
||||
|
||||
static var totalEventsCountKey: String {
|
||||
return "events.recorded.total.count"
|
||||
}
|
||||
|
||||
static var deletedEventsCountKey: String {
|
||||
return "events.deleted.count"
|
||||
}
|
||||
|
||||
static var uploadedEventsCountKey: String {
|
||||
return "events.uploaded.count"
|
||||
}
|
||||
|
||||
static var sessionNumberKey: String {
|
||||
return "session.number"
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ internal extension GuruAnalytics {
|
|||
|
||||
///built-in events
|
||||
static let fgEvent: EventProto = {
|
||||
var e = EventProto(paramKeyType: FgEventParametersKeys.self, name: "fg")
|
||||
var e = EventProto(paramKeyType: FgEventParametersKeys.self, name: "guru_engagement")
|
||||
return e
|
||||
}()
|
||||
|
||||
|
|
@ -31,6 +31,21 @@ internal extension GuruAnalytics {
|
|||
return e
|
||||
}()
|
||||
|
||||
static let sdkInitStartEvent: EventProto = {
|
||||
var e = EventProto(paramKeyType: SDKEventParametersKeys.self, name: "guru_sdk_init_start")
|
||||
return e
|
||||
}()
|
||||
|
||||
static let sdkInitCompleteEvent: EventProto = {
|
||||
var e = EventProto(paramKeyType: SDKEventParametersKeys.self, name: "guru_sdk_init_complete")
|
||||
return e
|
||||
}()
|
||||
|
||||
static let sessionStartEvent: EventProto = {
|
||||
var e = EventProto(paramKeyType: DefaultEventParametersKeys.self, name: "session_start")
|
||||
return e
|
||||
}()
|
||||
|
||||
class func setUserProperty(_ value: String?, forName name: PropertyName) {
|
||||
setUserProperty(value, forName: name.rawValue)
|
||||
}
|
||||
|
|
@ -48,4 +63,23 @@ internal extension GuruAnalytics {
|
|||
case duration
|
||||
}
|
||||
|
||||
enum SDKEventParametersKeys: String {
|
||||
case totalEvents = "total_events"
|
||||
case deletedEvents = "deleted_events"
|
||||
case uploadedEvents = "uploaded_events"
|
||||
case duration
|
||||
}
|
||||
|
||||
enum DefaultEventParametersKeys {
|
||||
}
|
||||
}
|
||||
|
||||
internal extension GuruAnalytics {
|
||||
|
||||
///built-in event parameters
|
||||
enum BuiltinParametersKeys: String, CaseIterable {
|
||||
case screenName = "screen_name"
|
||||
case sessionNo = "session_number"
|
||||
case sessionId = "session_id"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,14 @@ internal struct Constants {
|
|||
return shortVersion
|
||||
}()
|
||||
|
||||
private static let sdkVersion: String = {
|
||||
guard let infoDict = Bundle(for: Manager.self).infoDictionary,
|
||||
let currentVersion = infoDict["CFBundleShortVersionString"] as? String else {
|
||||
return ""
|
||||
}
|
||||
return currentVersion
|
||||
}()
|
||||
|
||||
private static let preferredLocale: Locale = {
|
||||
guard let preferredIdentifier = Locale.preferredLanguages.first else {
|
||||
return Locale.current
|
||||
|
|
@ -83,7 +91,8 @@ internal struct Constants {
|
|||
"screenH": Int(screenSize.h),
|
||||
"screenW": Int(screenSize.w),
|
||||
"osVersion": systemVersion,
|
||||
"language" : languageCode
|
||||
"language" : languageCode,
|
||||
"sdkVersion" : sdkVersion,
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,4 +25,38 @@ internal extension JSONDecoder {
|
|||
}
|
||||
return try decode(type, from: unwrappedData)
|
||||
}
|
||||
|
||||
private func decodeObject<T>(_ type: T.Type, from data: Data) -> T? where T: Decodable {
|
||||
|
||||
guard data.count > 0 else { return nil }
|
||||
|
||||
var object: T? = nil
|
||||
|
||||
do {
|
||||
object = try decode(type, from: data)
|
||||
} catch {
|
||||
cdPrint("JSONDecoder decode error: \(error)")
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func decodeObject<T>(_ type: T.Type, from jsonString: String) -> T? where T: Decodable {
|
||||
guard let jsonData = jsonString.data(using: .utf8) else { return nil }
|
||||
return decodeObject(type, from: jsonData)
|
||||
}
|
||||
|
||||
func decodeObject<T>(_ type: T.Type, from dictionary: [String : Any]) -> T? where T: Decodable {
|
||||
|
||||
var data: Data?
|
||||
|
||||
do {
|
||||
data = try JSONSerialization.data(withJSONObject: dictionary, options: .prettyPrinted)
|
||||
} catch let error {
|
||||
cdPrint(error)
|
||||
}
|
||||
|
||||
guard let jsonData = data else { return nil }
|
||||
return decodeObject(type, from: jsonData)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'GuruAnalyticsLib'
|
||||
s.version = '0.3.5'
|
||||
s.version = '0.3.8'
|
||||
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@git.chengdu.pundit.company:castbox/GuruAnalytics_iOS.git', :tag => s.version.to_s }
|
||||
s.source = { :git => 'git@github.com:castbox/GuruAnalytics_iOS.git', :tag => s.version.to_s }
|
||||
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
|
||||
|
||||
s.ios.deployment_target = '11.0'
|
||||
|
|
@ -38,12 +38,12 @@ TODO: Add long description of the pod here.
|
|||
# s.public_header_files = 'Pod/Classes/**/*.h'
|
||||
# s.frameworks = 'UIKit', 'MapKit'
|
||||
# s.dependency 'AFNetworking', '~> 2.3'
|
||||
s.dependency 'RxCocoa', '~> 6'
|
||||
s.dependency 'Alamofire', '~> 5.0'
|
||||
s.dependency 'FMDB'
|
||||
s.dependency 'GzipSwift'
|
||||
s.dependency 'CryptoSwift'
|
||||
s.dependency 'SwiftyBeaver'
|
||||
s.dependency 'RxCocoa', '~> 6.7.0'
|
||||
s.dependency 'Alamofire', '~> 5.9'
|
||||
s.dependency 'FMDB', '~> 2.0'
|
||||
s.dependency 'GzipSwift', '~> 5.0'
|
||||
s.dependency 'CryptoSwift', '~> 1.0'
|
||||
s.dependency 'SwiftyBeaver', '~> 1.0'
|
||||
|
||||
s.subspec 'Privacy' do |ss|
|
||||
ss.resource_bundles = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue