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,19 +386,33 @@ private extension Manager { | |||
|          | ||||
|         /// 1. 删除过期的数据 | ||||
|         serverNowMsSingle | ||||
|             .flatMap({ [weak self] serverNowMs -> Single<Void> in | ||||
|             guard let `self` = self else { return .just(()) } | ||||
|             let earlierThan: Int64 = serverNowMs - self.eventExpiredIntervel.int64Ms | ||||
|             return self.db.removeOutdatedEventRecords(earlierThan: earlierThan) | ||||
|         }) | ||||
|         .catch({ error in | ||||
|             cdPrint("remove outdated records error: \(error)") | ||||
|             return .just(()) | ||||
|         }) | ||||
|         .subscribe(onSuccess: { [weak self] _ in | ||||
|             self?.outdatedEventsCleared.onNext(true) | ||||
|         }) | ||||
|         .disposed(by: bag) | ||||
|             .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.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(0) | ||||
|             }) | ||||
|             .subscribe(onSuccess: { [weak self] deletedCount in | ||||
|                 UserDefaults.deletedEventsCount += deletedCount | ||||
|                 self?.outdatedEventsCleared.onNext(true) | ||||
|             }) | ||||
|             .disposed(by: bag) | ||||
|     } | ||||
|      | ||||
|     func logFirstOpenIfNeeded() { | ||||
|  | @ -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