#import "ThinkingAnalyticsSDKPrivate.h" #if TARGET_OS_IOS #import "TDAutoTrackManager.h" #import "TDAppLaunchReason.h" #import "TDPushClickEvent.h" #endif #if __has_include() #import #else #import "TDJSONUtil.h" #endif #if __has_include() #import #else #import "TDNotificationManager+Analytics.h" #endif #if __has_include() #import #else #import "TDCalibratedTime.h" #endif #if __has_include() #import #else #import "TDCoreDeviceInfo.h" #endif #if __has_include() #import #else #import "NSString+TDCore.h" #endif #if __has_include() #import #else #import "TDNotificationManager+Networking.h" #endif #if __has_include() #import #else #import "TDMediator+Analytics.h" #endif #import "TDConfig.h" #import "TDPublicConfig.h" #import "TDFile.h" #import "TDCheck.h" #import "TDAppState.h" #import "TDEventRecord.h" #import "TDAppLifeCycle.h" #import "TDAnalytics+Public.h" #import "TDConfigPrivate.h" #if !__has_feature(objc_arc) #error The ThinkingSDK library must be compiled with ARC enabled #endif @interface TDPresetProperties (ThinkingAnalytics) - (instancetype)initWithDictionary:(NSDictionary *)dict; - (void)updateValuesWithDictionary:(NSDictionary *)dict; @end @interface ThinkingAnalyticsSDK () @property (nonatomic, strong) TDEventTracker *eventTracker; @property (nonatomic, strong) TDFile *file; @property (nonatomic, strong) TDSuperProperty *superProperty; @property (nonatomic, strong) TDPropertyPluginManager *propertyPluginManager; @property (nonatomic, strong) TDAppLifeCycle *appLifeCycle; @property (atomic, assign) BOOL isOptOut; @property (nonatomic, strong, nullable) NSTimer *timer; @property (nonatomic, strong) TDTrackTimer *trackTimer; @property (atomic, strong) TDSqliteDataQueue *dataQueue; @end @implementation ThinkingAnalyticsSDK static NSLock *g_lock; static NSMutableDictionary *g_instances; static NSString *defaultProjectAppid; static dispatch_queue_t td_trackQueue; + (NSString *)defaultAppId { return defaultProjectAppid; } + (void)initialize { static dispatch_once_t ThinkingOnceToken; dispatch_once(&ThinkingOnceToken, ^{ td_trackQueue = dispatch_queue_create("cn.thinkingdata.analytics.track", DISPATCH_QUEUE_SERIAL); g_lock = [[NSLock alloc] init]; }); } + (dispatch_queue_t)sharedTrackQueue { return td_trackQueue; } + (dispatch_queue_t)sharedNetworkQueue { return [TDEventTracker td_networkQueue]; } - (ThinkingAnalyticsSDK *)innerCreateLightInstance { ThinkingAnalyticsSDK *lightInstance = [[LightThinkingAnalyticsSDK alloc] initWithAPPID:self.config.appid withServerURL:self.config.serverUrl withConfig:self.config]; lightInstance.identifyId = [TDDeviceInfo sharedManager].uniqueId; lightInstance.propertyPluginManager = self.propertyPluginManager; return lightInstance; } - (instancetype)initLight:(NSString *)appid withServerURL:(NSString *)serverURL withConfig:(TDConfig *)config { if (self = [self init]) { self.isEnabled = YES; self.config = [config copy]; // random instance name NSString *instanceName = [NSUUID UUID].UUIDString; self.config.name = instanceName; self.config.appid = appid; self.config.serverUrl = serverURL; NSString *instanceIdentify = [self instanceAliasNameOrAppId]; if (!instanceIdentify) { return nil; } [g_lock lock]; g_instances[instanceIdentify] = self; [g_lock unlock]; self.superProperty = [[TDSuperProperty alloc] initWithToken:instanceIdentify isLight:YES]; self.trackTimer = [[TDTrackTimer alloc] init]; self.dataQueue = [TDSqliteDataQueue sharedInstanceWithAppid:appid]; if (self.dataQueue == nil) { TDLogError(@"SqliteException: init SqliteDataQueue failed"); } self.eventTracker = [[TDEventTracker alloc] initWithQueue:td_trackQueue instanceToken:instanceIdentify]; } return self; } - (instancetype)initWithConfig:(TDConfig *)config { if (self = [super init]) { if (!config) { return nil; } self.config = config; NSString *instanceAliasName = [self instanceAliasNameOrAppId]; if (!instanceAliasName) { return nil; } static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ g_instances = [NSMutableDictionary dictionary]; defaultProjectAppid = instanceAliasName; }); [g_lock lock]; g_instances[instanceAliasName] = self; [g_lock unlock]; self.file = [[TDFile alloc] initWithAppid:instanceAliasName]; [self retrievePersistedData]; self.superProperty = [[TDSuperProperty alloc] initWithToken:instanceAliasName isLight:NO]; dispatch_async(td_trackQueue, ^{ self.dataQueue = [TDSqliteDataQueue sharedInstanceWithAppid:instanceAliasName]; if (self.dataQueue == nil) { TDLogError(@"SqliteException: init SqliteDataQueue failed"); } self.propertyPluginManager = [[TDPropertyPluginManager alloc] init]; TDPresetPropertyPlugin *presetPlugin = [[TDPresetPropertyPlugin alloc] init]; presetPlugin.instanceToken = [self instanceAliasNameOrAppId]; [self.propertyPluginManager registerPropertyPlugin:presetPlugin]; }); self.config.getInstanceName = ^NSString * _Nonnull{ return instanceAliasName; }; #if TARGET_OS_IOS dispatch_async(td_trackQueue, ^{ if (self.config.innerEnableEncrypt) { self.encryptManager = [[TDEncryptManager alloc] initWithSecretKey:self.config.innerSecretKey]; } __weak __typeof(self)weakSelf = self; [self.config innerUpdateConfig:^(NSDictionary * _Nonnull secretKey) { if (weakSelf.config.innerEnableEncrypt && secretKey) { [weakSelf.encryptManager handleEncryptWithConfig:secretKey]; } }]; [self.config innerUpdateIPMap]; }); #elif TARGET_OS_OSX [self.config innerUpdateConfig:^(NSDictionary * _Nonnull secretKey) {}]; #endif self.trackTimer = [[TDTrackTimer alloc] init]; self.ignoredViewControllers = [[NSMutableSet alloc] init]; self.ignoredViewTypeList = [[NSMutableSet alloc] init]; self.eventTracker = [[TDEventTracker alloc] initWithQueue:td_trackQueue instanceToken:instanceAliasName]; [self startFlushTimer]; [TDAppLifeCycle startMonitor]; [self registerAppLifeCycleListener]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkStatusChangedNotification:) name:kNetworkNotificationNameStatusChange object:nil]; #if TARGET_OS_IOS NSDictionary *ops = [TDAppLaunchReason getAppPushDic]; if(ops != nil){ TDPushClickEvent *pushEvent = [[TDPushClickEvent alloc]initWithName: @"te_ops_push_click"]; pushEvent.ops = ops; [self autoTrackWithEvent:pushEvent properties:@{}]; [self innerFlush]; } [TDAppLaunchReason clearAppPushParams]; #endif TDLogInfo(@"initialize success. Mode: %@\n AppID: %@\n ServerUrl: %@\n TimeZone: %@\n DeviceID: %@\n Lib: %@\n LibVersion: %@", [self modeEnumToString:self.config.mode], self.config.appid, self.config.serverUrl, self.config.defaultTimeZone ?: [NSTimeZone localTimeZone], [TDAnalytics getDeviceId], [[TDDeviceInfo sharedManager] libName] ,[[TDDeviceInfo sharedManager] libVersion]); [TDNotificationManager postAnalyticsInitEventWithAppId:instanceAliasName serverUrl:self.config.serverUrl]; [[TDMediator sharedInstance] registerSuccessWithTargetName:kTDMediatorTargetAnalytics]; } return self; } - (void)registerAppLifeCycleListener { NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter addObserver:self selector:@selector(appStateWillChangeNotification:) name:kTDAppLifeCycleStateWillChangeNotification object:nil]; [notificationCenter addObserver:self selector:@selector(appStateDidChangeNotification:) name:kTDAppLifeCycleStateDidChangeNotification object:nil]; } - (void)networkStatusChangedNotification:(NSNotification *)notification { NSString *status = [notification.userInfo objectForKey:kNetworkNotificationParamsNetworkType]; if (![status isEqualToString:@"NULL"]) { [self innerFlush]; } } - (NSString*)modeEnumToString:(TDMode)enumVal { NSArray *modeEnumArray = [[NSArray alloc] initWithObjects:@"Normal", @"DebugOnly", @"Debug", nil]; return [modeEnumArray objectAtIndex:enumVal]; } - (NSString *)instanceAliasNameOrAppId { return [self.config innerGetMapInstanceToken]; } - (NSString *)description { return [NSString stringWithFormat:@"[ThinkingAnalyticsSDK] AppID: %@, ServerUrl: %@, Mode: %@, TimeZone: %@, DeviceID: %@, Lib: %@, LibVersion: %@", self.config.appid, self.config.serverUrl, [self modeEnumToString:self.config.mode], self.config.defaultTimeZone, [TDAnalytics getDeviceId], [[TDDeviceInfo sharedManager] libName] ,[[TDDeviceInfo sharedManager] libVersion]]; } - (BOOL)hasDisabled { return !self.isEnabled || self.isOptOut; } - (void)doOptOutTracking { self.isOptOut = YES; #if TARGET_OS_IOS @synchronized (self.autoTrackSuperProperty) { [self.autoTrackSuperProperty clearSuperProperties]; } #endif [self.superProperty registerDynamicSuperProperties:nil]; void(^block)(void) = ^{ @synchronized (TDSqliteDataQueue.class) { [self.dataQueue deleteAll:[self instanceAliasNameOrAppId]]; } [self.trackTimer clear]; [self.superProperty clearSuperProperties]; self.identifyId = [TDDeviceInfo sharedManager].uniqueId; self.accountId = nil; [self.file archiveAccountID:nil]; [self.file archiveIdentifyId:nil]; [self.file archiveSuperProperties:nil]; [self.file archiveOptOut:YES]; }; if (dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) == dispatch_queue_get_label(td_trackQueue)) { block(); } else { dispatch_async(td_trackQueue, block); } } #pragma mark - Persistence - (void)retrievePersistedData { self.accountId = [self.file unarchiveAccountID]; self.identifyId = [self.file unarchiveIdentifyID]; self.trackPause = [self.file unarchiveTrackPause]; self.isEnabled = [self.file unarchiveEnabled]; self.isOptOut = [self.file unarchiveOptOut]; self.config.uploadSize = [self.file unarchiveUploadSize]; self.config.uploadInterval = [self.file unarchiveUploadInterval]; if (self.identifyId.length == 0) { self.identifyId = [TDDeviceInfo sharedManager].uniqueId; } if (self.accountId.length == 0) { [self.file deleteOldLoginId]; } } - (void)deleteAll { dispatch_async(td_trackQueue, ^{ @synchronized (TDSqliteDataQueue.class) { [self.dataQueue deleteAll:[self instanceAliasNameOrAppId]]; } }); } //MARK: - AppLifeCycle - (void)appStateWillChangeNotification:(NSNotification *)notification { TDAppLifeCycleState newState = [[notification.userInfo objectForKey:kTDAppLifeCycleNewStateKey] integerValue]; if (newState == TDAppLifeCycleStateEnd) { [self stopFlushTimer]; } } - (void)appStateDidChangeNotification:(NSNotification *)notification { TDAppLifeCycleState newState = [[notification.userInfo objectForKey:kTDAppLifeCycleNewStateKey] integerValue]; if (newState == TDAppLifeCycleStateStart) { [self startFlushTimer]; NSTimeInterval systemUpTime = [TDCoreDeviceInfo bootTime]; [self.trackTimer enterForegroundWithSystemUptime:systemUpTime]; } else if (newState == TDAppLifeCycleStateEnd) { NSTimeInterval systemUpTime = [TDCoreDeviceInfo bootTime]; [self.trackTimer enterBackgroundWithSystemUptime:systemUpTime]; #if TARGET_OS_IOS UIApplication *application = [TDAppState sharedApplication];; __block UIBackgroundTaskIdentifier backgroundTaskIdentifier = UIBackgroundTaskInvalid; void (^endBackgroundTask)(void) = ^() { [application endBackgroundTask:backgroundTaskIdentifier]; backgroundTaskIdentifier = UIBackgroundTaskInvalid; }; backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:endBackgroundTask]; [self.eventTracker _asyncWithCompletion:endBackgroundTask]; #else [self.eventTracker flush]; #endif } else if (newState == TDAppLifeCycleStateTerminate) { dispatch_sync(td_trackQueue, ^{}); [self.eventTracker flush]; dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); dispatch_queue_t networkQueue = [TDEventTracker td_networkQueue]; dispatch_async(networkQueue, ^{ dispatch_semaphore_signal(semaphore); }); dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC))); } } // MARK: - + (NSString *)getNetWorkStates { #if TARGET_OS_IOS return [TDCoreDeviceInfo networkType]; #else return @"--"; #endif } #pragma mark - Private - (void)asyncTrackEventObject:(TDTrackEvent *)event properties:(NSDictionary * _Nullable)properties isH5:(BOOL)isH5 { event.isEnabled = self.isEnabled; event.trackPause = self.isTrackPause; event.isOptOut = self.isOptOut; event.accountId = self.accountId; event.distinctId = self.identifyId; [self handleTimeEvent:event]; [self calibratedTimeWithEvent:event]; dispatch_async(td_trackQueue, ^{ @autoreleasepool { event.dynamicSuperProperties = [self.superProperty obtainDynamicSuperProperties]; [self trackEvent:event properties:[properties copy] isH5:isH5]; } }); } - (void)asyncUserEventObject:(TDUserEvent *)event properties:(NSDictionary * _Nullable)properties isH5:(BOOL)isH5 { event.isEnabled = self.isEnabled; event.trackPause = self.isTrackPause; event.isOptOut = self.isOptOut; event.accountId = self.accountId; event.distinctId = self.identifyId; [self calibratedTimeWithEvent:event]; dispatch_async(td_trackQueue, ^{ @autoreleasepool { [self trackUserEvent:event properties:[properties copy] isH5:NO]; } }); } - (void)calibratedTimeWithEvent:(TDBaseEvent *)event { if (event.timeValueType == TDEventTimeValueTypeNone) { event.time = [TDCalibratedTime now]; } } + (BOOL)isTrackEvent:(NSString *)eventType { return [TD_EVENT_TYPE_TRACK isEqualToString:eventType] || [TD_EVENT_TYPE_TRACK_FIRST isEqualToString:eventType] || [TD_EVENT_TYPE_TRACK_UPDATE isEqualToString:eventType] || [TD_EVENT_TYPE_TRACK_OVERWRITE isEqualToString:eventType] ; } //MARK: - - (void)trackUserEvent:(TDUserEvent *)event properties:(NSDictionary *)properties isH5:(BOOL)isH5 { if (!event.isEnabled || event.isOptOut) { return; } if ([TDAppState shareInstance].relaunchInBackground && !self.config.trackRelaunchedInBackgroundEvents) { return; } [event.properties addEntriesFromDictionary:[TDPropertyValidator validateProperties:properties validator:event]]; if (event.timeZone == nil) { event.timeZone = self.config.defaultTimeZone ?: [NSTimeZone localTimeZone]; } NSDictionary *jsonObj = [event formatDateWithDict:event.jsonObject]; [self.eventTracker track:jsonObj immediately:event.immediately saveOnly:event.isTrackPause]; TDLogInfo(@"user event success"); } - (void)trackEvent:(TDTrackEvent *)event properties:(NSDictionary *)properties isH5:(BOOL)isH5 { if (!event.isEnabled || event.isOptOut) { return; } if ([TDAppState shareInstance].relaunchInBackground && !self.config.trackRelaunchedInBackgroundEvents && [event.eventName isEqualToString:TD_APP_START_BACKGROUND_EVENT]) { return; } NSError *error = nil; [event validateWithError:&error]; if (error) { return; } if ([self.config.disableEvents containsObject:event.eventName]) { return; } if ([TDAppState shareInstance].relaunchInBackground) { event.properties[@"#relaunched_in_background"] = @YES; } NSMutableDictionary *pluginProperties = [self.propertyPluginManager propertiesWithEventType:event.eventType]; NSDictionary *superProperties = [TDPropertyValidator validateProperties:self.superProperty.currentSuperProperties validator:event]; NSDictionary *dynamicSuperProperties = [TDPropertyValidator validateProperties:event.dynamicSuperProperties validator:event]; if (event.timeZone == nil) { event.timeZone = self.config.defaultTimeZone ?: [NSTimeZone localTimeZone]; } NSMutableDictionary *jsonObj = [NSMutableDictionary dictionary]; if (isH5) { event.properties = [superProperties mutableCopy]; [event.properties addEntriesFromDictionary:dynamicSuperProperties]; [event.properties addEntriesFromDictionary:properties]; [event.properties addEntriesFromDictionary:pluginProperties]; jsonObj = event.jsonObject; if (event.h5TimeString) { jsonObj[@"#time"] = event.h5TimeString; } if (event.h5ZoneOffSet) { jsonObj[@"#zone_offset"] = event.h5ZoneOffSet; } } else { [event.properties addEntriesFromDictionary:pluginProperties]; [event.properties addEntriesFromDictionary:superProperties]; #if TARGET_OS_IOS if ([event isKindOfClass:[TDAutoTrackEvent class]]) { TDAutoTrackEvent *autoEvent = (TDAutoTrackEvent *)event; NSDictionary *autoSuperProperties = [self.autoTrackSuperProperty currentSuperPropertiesWithEventName:event.eventName]; autoSuperProperties = [TDPropertyValidator validateProperties:autoSuperProperties validator:autoEvent]; [event.properties addEntriesFromDictionary:autoSuperProperties]; } #endif [event.properties addEntriesFromDictionary:dynamicSuperProperties]; properties = [TDPropertyValidator validateProperties:properties validator:event]; [event.properties addEntriesFromDictionary:properties]; jsonObj = event.jsonObject; } jsonObj = [event formatDateWithDict:jsonObj]; [TDNotificationManager postAnalyticsTrackWithAppId:self.config.appid event:jsonObj]; if (event.isDebug) { [self.eventTracker trackDebugEvent:jsonObj]; } else { [self.eventTracker track:jsonObj immediately:event.immediately saveOnly:event.isTrackPause]; } TDLogInfo(@"track success"); } #pragma mark - innerFlush control - (void)startFlushTimer { [self stopFlushTimer]; dispatch_async(dispatch_get_main_queue(), ^{ if (self.config.uploadInterval > 0) { self.timer = [NSTimer scheduledTimerWithTimeInterval:[self.config.uploadInterval integerValue] target:self selector:@selector(autoFlushWithTimer:) userInfo:nil repeats:YES]; } }); } - (void)stopFlushTimer { dispatch_async(dispatch_get_main_queue(), ^{ if (self.timer) { [self.timer invalidate]; self.timer = nil; } }); } #if TARGET_OS_IOS //MARK: - Auto Track - (void)autoTrackWithEvent:(TDAutoTrackEvent *)event properties:(NSDictionary *)properties { [self handleTimeEvent:event]; [self asyncAutoTrackEventObject:event properties:properties]; } /// Add event to event queue - (void)asyncAutoTrackEventObject:(TDAutoTrackEvent *)event properties:(NSDictionary *)properties { event.isEnabled = self.isEnabled; event.trackPause = self.isTrackPause; event.isOptOut = self.isOptOut; event.accountId = self.accountId; event.distinctId = self.identifyId; [self calibratedTimeWithEvent:event]; NSDictionary *dynamicProperties = [self.superProperty obtainDynamicSuperProperties]; NSMutableDictionary *autoTrackDynamicProperties = [NSMutableDictionary dictionary]; [autoTrackDynamicProperties addEntriesFromDictionary:[self.autoTrackSuperProperty obtainAutoTrackDynamicSuperProperties]]; [autoTrackDynamicProperties addEntriesFromDictionary:[self.autoTrackSuperProperty obtainDynamicSuperPropertiesWithType:event.autoTrackEventType currentProperties:event.properties]]; NSMutableDictionary *unionProperties = [NSMutableDictionary dictionary]; if (dynamicProperties) { [unionProperties addEntriesFromDictionary:dynamicProperties]; } if (autoTrackDynamicProperties) { [unionProperties addEntriesFromDictionary:autoTrackDynamicProperties]; } event.dynamicSuperProperties = unionProperties; dispatch_async(td_trackQueue, ^{ [self trackEvent:event properties:[properties copy] isH5:NO]; }); } - (BOOL)isViewControllerIgnored:(UIViewController *)viewController { if (viewController == nil) { return false; } NSString *screenName = NSStringFromClass([viewController class]); if (_ignoredViewControllers != nil && _ignoredViewControllers.count > 0) { if ([_ignoredViewControllers containsObject:screenName]) { return true; } } return false; } - (TDAutoTrackSuperProperty *)autoTrackSuperProperty { if (!_autoTrackSuperProperty) { _autoTrackSuperProperty = [[TDAutoTrackSuperProperty alloc] init]; } return _autoTrackSuperProperty; } #endif //MARK: - Private - (void)handleTimeEvent:(TDTrackEvent *)trackEvent { BOOL isTrackDuration = [self.trackTimer isExistEvent:trackEvent.eventName]; BOOL isEndEvent = [trackEvent.eventName isEqualToString:TD_APP_END_EVENT]; BOOL isStartEvent = [trackEvent.eventName isEqualToString:TD_APP_START_EVENT]; BOOL isStateInit = [TDAppLifeCycle shareInstance].state == TDAppLifeCycleStateInit; if (isStateInit) { trackEvent.foregroundDuration = [self.trackTimer foregroundDurationOfEvent:trackEvent.eventName isActive:YES systemUptime:trackEvent.systemUpTime]; [self.trackTimer removeEvent:trackEvent.eventName]; } else if (isStartEvent) { trackEvent.backgroundDuration = [self.trackTimer backgroundDurationOfEvent:trackEvent.eventName isActive:NO systemUptime:trackEvent.systemUpTime]; [self.trackTimer removeEvent:trackEvent.eventName]; } else if (isEndEvent) { trackEvent.foregroundDuration = [self.trackTimer foregroundDurationOfEvent:trackEvent.eventName isActive:YES systemUptime:trackEvent.systemUpTime]; [self.trackTimer removeEvent:trackEvent.eventName]; } else if (isTrackDuration) { BOOL isActive = [TDAppState shareInstance].isActive; trackEvent.foregroundDuration = [self.trackTimer foregroundDurationOfEvent:trackEvent.eventName isActive:isActive systemUptime:trackEvent.systemUpTime]; trackEvent.backgroundDuration = [self.trackTimer backgroundDurationOfEvent:trackEvent.eventName isActive:isActive systemUptime:trackEvent.systemUpTime]; [self.trackTimer removeEvent:trackEvent.eventName]; } else { if (trackEvent.eventName == TD_APP_END_EVENT) { return; } } } + (NSMutableDictionary *)_getAllInstances { NSMutableDictionary *dict = nil; [g_lock lock]; dict = [g_instances mutableCopy]; [g_lock unlock]; return dict; } + (void)track_crashEventWithMessage:(NSString *)msg { #if TARGET_OS_IOS [ThinkingExceptionHandler trackCrashWithMessage:msg]; #endif } //MARK: - SDK instance + (nullable ThinkingAnalyticsSDK *)defaultInstance { NSString *appId = [self defaultAppId]; return [self instanceWithAppid:appId]; } + (nullable ThinkingAnalyticsSDK *)instanceWithAppid:(NSString *)appid { appid = appid.td_trim; if (appid == nil || appid.length == 0) { appid = [ThinkingAnalyticsSDK defaultAppId]; } ThinkingAnalyticsSDK *sdk = nil; [g_lock lock]; sdk = g_instances[appid]; [g_lock unlock]; return sdk; } //MARK: - track event - (void)innerTrack:(NSString *)event { [self innerTrack:event properties:nil time:nil timeZone:nil]; } - (void)innerTrack:(NSString *)event properties:(NSDictionary *)propertieDict { [self innerTrack:event properties:propertieDict time:nil timeZone:nil]; } - (void)innerTrackDebug:(NSString *)event properties:(NSDictionary *)propertieDict { TDTrackEvent *trackEvent = [[TDTrackEvent alloc] initWithName:event]; trackEvent.isDebug = YES; [self handleTimeEvent:trackEvent]; [self asyncTrackEventObject:trackEvent properties:propertieDict isH5:NO]; } - (void)innerTrack:(NSString *)event properties:(NSDictionary * _Nullable)propertieDict time:(NSDate * _Nullable)time timeZone:(NSTimeZone * _Nullable)timeZone { TDTrackEvent *trackEvent = [[TDTrackEvent alloc] initWithName:event]; if (time) { trackEvent.time = time; trackEvent.timeValueType = TDEventTimeValueTypeTimeOnly; if (timeZone) { trackEvent.timeZone = timeZone; trackEvent.timeValueType = TDEventTimeValueTypeTimeAndZone; } } [self asyncTrackEventObject:trackEvent properties:propertieDict isH5:NO]; } - (void)innerTrackWithEventModel:(TDEventModel *)eventModel { TDTrackEvent *baseEvent = nil; if ([eventModel.eventType isEqualToString:TD_EVENT_TYPE_TRACK_FIRST]) { TDTrackFirstEvent *trackEvent = [[TDTrackFirstEvent alloc] initWithName:eventModel.eventName]; trackEvent.firstCheckId = eventModel.extraID; baseEvent = trackEvent; } else if ([eventModel.eventType isEqualToString:TD_EVENT_TYPE_TRACK_UPDATE]) { TDTrackUpdateEvent *trackEvent = [[TDTrackUpdateEvent alloc] initWithName:eventModel.eventName]; trackEvent.eventId = eventModel.extraID; baseEvent = trackEvent; } else if ([eventModel.eventType isEqualToString:TD_EVENT_TYPE_TRACK_OVERWRITE]) { TDTrackOverwriteEvent *trackEvent = [[TDTrackOverwriteEvent alloc] initWithName:eventModel.eventName]; trackEvent.eventId = eventModel.extraID; baseEvent = trackEvent; } else if ([eventModel.eventType isEqualToString:TD_EVENT_TYPE_TRACK]) { TDTrackEvent *trackEvent = [[TDTrackEvent alloc] initWithName:eventModel.eventName]; baseEvent = trackEvent; } if (eventModel.time) { baseEvent.time = eventModel.time; baseEvent.timeValueType = TDEventTimeValueTypeTimeOnly; if (eventModel.timeZone) { baseEvent.timeZone = eventModel.timeZone; baseEvent.timeValueType = TDEventTimeValueTypeTimeAndZone; } } [self asyncTrackEventObject:baseEvent properties:eventModel.properties isH5:NO]; } - (void)innerTimeEvent:(NSString *)event { if ([self hasDisabled]) { return; } NSError *error = nil; [TDPropertyValidator validateEventOrPropertyName:event withError:&error]; if (error) { return; } [self.trackTimer trackEvent:event withSystemUptime:[TDCoreDeviceInfo bootTime]]; } //MARK: - user id - (void)innerSetIdentify:(NSString *)distinctId { if ([self hasDisabled]) { return; } if (![distinctId isKindOfClass:[NSString class]]) { TDLogError(@"identify cannot null", distinctId); return; } NSString *trimmedId = [distinctId stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; if (trimmedId.length == 0) { TDLogError(@"accountId invald", distinctId); return; } TDLogInfo(@"Set distinct ID, Distinct Id = %@", distinctId); @synchronized (self.file) { self.identifyId = distinctId; [self.file archiveIdentifyId:distinctId]; [TDNotificationManager postAnalyticsSetDistinctIdEventWithAppId:self.config.appid accountId:self.accountId distinctId:self.identifyId]; } } - (NSString *)innerDistinctId { return self.identifyId; } - (NSString *)innerAccountId { return self.accountId; } // TAThirdParty model used. - (NSString *)getAccountId { return [self innerAccountId]; } - (void)innerLogin:(NSString *)accountId { if ([self hasDisabled]) { return; } if (![accountId isKindOfClass:[NSString class]]) { TDLogError(@"accountId invald", accountId); return; } NSString *trimmedId = [accountId stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; if (trimmedId.length == 0) { TDLogError(@"accountId invald", accountId); return; } TDLogInfo(@"Login SDK, AccountId = %@", accountId); @synchronized (self.file) { self.accountId = accountId; [self.file archiveAccountID:accountId]; [TDNotificationManager postAnalyticsLoginEventWithAppId:self.config.appid accountId:self.accountId distinctId:self.identifyId]; } } - (void)innerLogout { if ([self hasDisabled]) { return; } TDLogInfo(@"Logout SDK."); @synchronized (self.file) { self.accountId = nil; [self.file archiveAccountID:nil]; [TDNotificationManager postAnalyticsLogoutEventWithAppId:self.config.appid distinctId:self.identifyId]; } } //MARK: - user profile - (void)innerUserSet:(NSDictionary *)properties { TDUserEventSet *event = [[TDUserEventSet alloc] init]; [self asyncUserEventObject:event properties:properties isH5:NO]; } - (void)innerUserUnset:(NSString *)propertyName { if ([propertyName isKindOfClass:[NSString class]] && propertyName.length > 0) { NSDictionary *properties = @{propertyName: @0}; TDUserEventUnset *event = [[TDUserEventUnset alloc] init]; [self asyncUserEventObject:event properties:properties isH5:NO]; } } - (void)innerUserUnsets:(NSArray *)propertyNames { NSMutableDictionary *dict = [NSMutableDictionary dictionary]; for (NSString *name in propertyNames) { if ([name isKindOfClass:[NSString class]] && name.length > 0) { dict[name] = @0; } } if (dict.count > 0) { TDUserEventUnset *event = [[TDUserEventUnset alloc] init]; [self asyncUserEventObject:event properties:dict isH5:NO]; } } - (void)innerUserSetOnce:(NSDictionary *)properties { TDUserEventSetOnce *event = [[TDUserEventSetOnce alloc] init]; [self asyncUserEventObject:event properties:properties isH5:NO]; } - (void)innerUserAdd:(NSDictionary *)properties { TDUserEventAdd *event = [[TDUserEventAdd alloc] init]; [self asyncUserEventObject:event properties:properties isH5:NO]; } - (void)innerUserAdd:(NSString *)propertyName andPropertyValue:(NSNumber *)propertyValue { if (propertyName && propertyValue) { [self innerUserAdd:@{propertyName: propertyValue}]; } } - (void)innerUserDelete { TDUserEventDelete *event = [[TDUserEventDelete alloc] init]; [self asyncUserEventObject:event properties:nil isH5:NO]; } - (void)innerUserAppend:(NSDictionary *)properties { TDUserEventAppend *event = [[TDUserEventAppend alloc] init]; [self asyncUserEventObject:event properties:properties isH5:NO]; } - (void)innerUserUniqAppend:(NSDictionary *)properties { TDUserEventUniqueAppend *event = [[TDUserEventUniqueAppend alloc] init]; [self asyncUserEventObject:event properties:properties isH5:NO]; } //MARK: - super properties - (void)innerSetSuperProperties:(NSDictionary *)properties { if ([self hasDisabled]) { return; } dispatch_async(td_trackQueue, ^{ [self.superProperty registerSuperProperties:properties]; }); } - (void)innerUnsetSuperProperty:(NSString *)property { if ([self hasDisabled]) { return; } dispatch_async(td_trackQueue, ^{ [self.superProperty unregisterSuperProperty:property]; }); } - (void)innerClearSuperProperties { if ([self hasDisabled]) { return; } dispatch_async(td_trackQueue, ^{ [self.superProperty clearSuperProperties]; }); } - (NSDictionary *)innerCurrentSuperProperties { return [self.superProperty currentSuperProperties]; } - (void)innerRegisterDynamicSuperProperties:(NSDictionary *(^)(void))dynamicSuperProperties { if ([self hasDisabled]) { return; } if (!dynamicSuperProperties) { TDLogError(@"Ignoring empty"); return; } @synchronized (self.superProperty) { [self.superProperty registerDynamicSuperProperties:dynamicSuperProperties]; } } - (TDPresetProperties *)innerGetPresetProperties { NSMutableDictionary *presetDic = [NSMutableDictionary dictionary]; NSDictionary *pluginProperties = [self.propertyPluginManager currentPropertiesForPluginClasses:@[TDPresetPropertyPlugin.class]]; [presetDic addEntriesFromDictionary:pluginProperties]; NSDateFormatter *timeFormatter = [[NSDateFormatter alloc] init]; timeFormatter.dateFormat = kDefaultTimeFormat; timeFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; timeFormatter.calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; timeFormatter.timeZone = self.config.defaultTimeZone; presetDic = [TDJSONUtil formatDateWithFormatter:timeFormatter dict:presetDic]; return [[TDPresetProperties alloc] initWithDictionary:presetDic]; } //MARK: - SDK error callback - (void)innerRegisterErrorCallback:(void(^)(NSInteger code, NSString * _Nullable errorMsg, NSString * _Nullable ext))errorCallback { self.errorCallback = errorCallback; } //MARK: - - (BOOL)innerIsViewTypeIgnored:(Class)aClass { return [self.ignoredViewTypeList containsObject:aClass]; } - (void)autoFlushWithTimer:(NSTimer *)timer { if ([self hasDisabled] || self.isTrackPause) { return; } [self.eventTracker flush]; } - (void)innerFlush { if ([self hasDisabled] || self.isTrackPause) { return; } TDLogInfo(@"flush success. By manual."); [self.eventTracker flush]; } - (void)innerSetNetworkType:(TDReportingNetworkType)type { if ([self hasDisabled]) { return; } self.config.reportingNetworkType = type; } - (void)innerSetTrackStatus: (TDTrackStatus)status { switch (status) { case TDTrackStatusPause: { TDLogInfo(@"Change status to Pause") self.isEnabled = NO; dispatch_async(td_trackQueue, ^{ [self.file archiveIsEnabled:NO]; }); break; } case TDTrackStatusStop: { TDLogInfo(@"Change status to Stop") [self doOptOutTracking]; break; } case TDTrackStatusSaveOnly: { TDLogInfo(@"Change status to SaveOnly") self.trackPause = YES; self.isEnabled = YES; self.isOptOut = NO; dispatch_async(td_trackQueue, ^{ [self.file archiveTrackPause:YES]; [self.file archiveIsEnabled:YES]; [self.file archiveOptOut:NO]; }); break; } case TDTrackStatusNormal: { TDLogInfo(@"Change status to Normal") self.trackPause = NO; self.isEnabled = YES; self.isOptOut = NO; dispatch_async(td_trackQueue, ^{ [self.file archiveTrackPause:NO]; [self.file archiveIsEnabled:self.isEnabled]; [self.file archiveOptOut:NO]; }); [self innerFlush]; break; } default: break; } } - (NSString *)innetGetTimeString:(NSDate *)date { return [date td_formatWithTimeZone:self.config.defaultTimeZone formatString:kDefaultTimeFormat]; } @end