diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2b6ac41..d0930b8 100644
--- a/CHANGELOG.md
+++ b/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
diff --git a/Example/GuruAnalytics.xcodeproj/project.pbxproj b/Example/GuruAnalytics.xcodeproj/project.pbxproj
index 3338c7a..ce1e7e9 100644
--- a/Example/GuruAnalytics.xcodeproj/project.pbxproj
+++ b/Example/GuruAnalytics.xcodeproj/project.pbxproj
@@ -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 = (
diff --git a/Example/GuruAnalytics/ViewController.swift b/Example/GuruAnalytics/ViewController.swift
index 5921954..c6d3892 100644
--- a/Example/GuruAnalytics/ViewController.swift
+++ b/Example/GuruAnalytics/ViewController.swift
@@ -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() {
diff --git a/Example/Podfile b/Example/Podfile
index d3be955..eb06f26 100644
--- a/Example/Podfile
+++ b/Example/Podfile
@@ -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|
diff --git a/GuruAnalytics/Assets/PrivacyInfo.xcprivacy b/GuruAnalytics/Assets/PrivacyInfo.xcprivacy
index 458bfd4..11d3be4 100644
--- a/GuruAnalytics/Assets/PrivacyInfo.xcprivacy
+++ b/GuruAnalytics/Assets/PrivacyInfo.xcprivacy
@@ -2,6 +2,21 @@
 
 
 
+	NSPrivacyCollectedDataTypes
+	
+		
+			NSPrivacyCollectedDataType
+			NSPrivacyCollectedDataTypeDeviceID
+			NSPrivacyCollectedDataTypeLinked
+			
+			NSPrivacyCollectedDataTypeTracking
+			
+			NSPrivacyCollectedDataTypePurposes
+			
+				NSPrivacyCollectedDataTypePurposeAnalytics
+			
+		
+	
 	NSPrivacyAccessedAPITypes
 	
 		
@@ -34,8 +49,6 @@
 	NSPrivacyTracking
 	
 	NSPrivacyTrackingDomains
-	
-		
-	
+	
 
 
diff --git a/GuruAnalytics/Classes/Internal/DataModel/Models.swift b/GuruAnalytics/Classes/Internal/DataModel/Models.swift
index e97322f..805b660 100644
--- a/GuruAnalytics/Classes/Internal/DataModel/Models.swift
+++ b/GuruAnalytics/Classes/Internal/DataModel/Models.swift
@@ -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)
+        }
+    }
+}
+
diff --git a/GuruAnalytics/Classes/Internal/Database/Database.swift b/GuruAnalytics/Classes/Internal/Database/Database.swift
index 0ef7c10..41006b9 100644
--- a/GuruAnalytics/Classes/Internal/Database/Database.swift
+++ b/GuruAnalytics/Classes/Internal/Database/Database.swift
@@ -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 {
         guard !recordIds.isEmpty else {
             return .just(())
diff --git a/GuruAnalytics/Classes/Internal/Database/Manager.swift b/GuruAnalytics/Classes/Internal/Database/Manager.swift
index ef35122..e16f3da 100644
--- a/GuruAnalytics/Classes/Internal/Database/Manager.swift
+++ b/GuruAnalytics/Classes/Internal/Database/Manager.swift
@@ -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 {
         
         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 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 in
                     self.accumulateUploadedEventsCount(recordIDs.count)
+                    UserDefaults.uploadedEventsCount += recordIDs.count
                     /// step4:  删除数据库中对应记录
                     return self.db.deleteEventRecords(recordIDs)
                         .catch { error in
diff --git a/GuruAnalytics/Classes/Internal/Database/UserDefaults.swift b/GuruAnalytics/Classes/Internal/Database/UserDefaults.swift
index dd9fb51..3fa4c7c 100644
--- a/GuruAnalytics/Classes/Internal/Database/UserDefaults.swift
+++ b/GuruAnalytics/Classes/Internal/Database/UserDefaults.swift
@@ -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"
+    }
+    
 }
diff --git a/GuruAnalytics/Classes/Internal/GuruAnalytics+Internal.swift b/GuruAnalytics/Classes/Internal/GuruAnalytics+Internal.swift
index f62e47e..598cf3c 100644
--- a/GuruAnalytics/Classes/Internal/GuruAnalytics+Internal.swift
+++ b/GuruAnalytics/Classes/Internal/GuruAnalytics+Internal.swift
@@ -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"
+    }
 }
diff --git a/GuruAnalytics/Classes/Internal/Utility/Constants.swift b/GuruAnalytics/Classes/Internal/Utility/Constants.swift
index c4d8c78..84152a0 100755
--- a/GuruAnalytics/Classes/Internal/Utility/Constants.swift
+++ b/GuruAnalytics/Classes/Internal/Utility/Constants.swift
@@ -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,
         ]
     }
     
diff --git a/GuruAnalytics/Classes/Internal/Utility/JSONDecoder.Extension.swift b/GuruAnalytics/Classes/Internal/Utility/JSONDecoder.Extension.swift
index 13388c3..4e63ac8 100644
--- a/GuruAnalytics/Classes/Internal/Utility/JSONDecoder.Extension.swift
+++ b/GuruAnalytics/Classes/Internal/Utility/JSONDecoder.Extension.swift
@@ -25,4 +25,38 @@ internal extension JSONDecoder {
         }
         return try decode(type, from: unwrappedData)
     }
+    
+    private func decodeObject(_ 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(_ 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(_ 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)
+    }
 }
diff --git a/GuruAnalyticsLib.podspec b/GuruAnalyticsLib.podspec
index 794f097..d1c3c63 100644
--- a/GuruAnalyticsLib.podspec
+++ b/GuruAnalyticsLib.podspec
@@ -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/'
 
   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 = {