Ta android + ios
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// TDFile.h
|
||||
// ThinkingSDK
|
||||
//
|
||||
// Created by LiHuanan on 2020/9/8.
|
||||
// Copyright © 2020 thinkingdata. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TDFile : NSObject
|
||||
|
||||
@property(strong,nonatomic) NSString* appid;
|
||||
|
||||
- (instancetype)initWithAppid:(NSString*)appid;
|
||||
|
||||
- (void)archiveSessionID:(long long)sessionid;
|
||||
- (long long)unarchiveSessionID ;
|
||||
|
||||
- (void)archiveIdentifyId:(nullable NSString *)identifyId;
|
||||
- (NSString*)unarchiveIdentifyID ;
|
||||
|
||||
- (void)archiveAccountID:(nullable NSString *)accountID;
|
||||
- (NSString*)unarchiveAccountID ;
|
||||
|
||||
- (void)archiveUploadSize:(NSNumber *)uploadSize;
|
||||
- (NSNumber*)unarchiveUploadSize;
|
||||
|
||||
- (void)archiveUploadInterval:(NSNumber *)uploadInterval;
|
||||
- (NSNumber*)unarchiveUploadInterval;
|
||||
|
||||
|
||||
- (void)archiveSuperProperties:(nullable NSDictionary *)superProperties;
|
||||
- (NSDictionary*)unarchiveSuperProperties;
|
||||
|
||||
- (void)archiveTrackPause:(BOOL)trackPause;
|
||||
- (BOOL)unarchiveTrackPause;
|
||||
|
||||
- (void)archiveOptOut:(BOOL)optOut;
|
||||
- (BOOL)unarchiveOptOut;
|
||||
|
||||
- (void)archiveIsEnabled:(BOOL)isEnabled;
|
||||
- (BOOL)unarchiveEnabled;
|
||||
|
||||
- (void)archiveDeviceId:(NSString *)deviceId;
|
||||
- (NSString *)unarchiveDeviceId;
|
||||
|
||||
- (void)archiveInstallTimes:(NSString *)installTimes;
|
||||
- (NSString *)unarchiveInstallTimes;
|
||||
|
||||
- (BOOL)archiveObject:(id)object withFilePath:(NSString *)filePath;
|
||||
|
||||
- (BOOL)addSkipBackupAttributeToItemAtPath:(NSString *)filePathString;
|
||||
// Compatible with old versions
|
||||
- (NSString*)unarchiveOldLoginId;
|
||||
// Compatible with old versions
|
||||
- (void)deleteOldLoginId;
|
||||
|
||||
@end;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d5b7e371a93424242a391968ce5a0c5e
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,283 @@
|
||||
//
|
||||
// TDFile.m
|
||||
// ThinkingSDK
|
||||
//
|
||||
// Created by LiHuanan on 2020/9/8.
|
||||
// Copyright © 2020 thinkingdata. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TDFile.h"
|
||||
#import "TDLogging.h"
|
||||
#import "TDJSONUtil.h"
|
||||
|
||||
@implementation TDFile
|
||||
|
||||
- (instancetype)initWithAppid:(NSString*)appid
|
||||
{
|
||||
self = [super init];
|
||||
if(self)
|
||||
{
|
||||
self.appid = appid;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)archiveSessionID:(long long)sessionid {
|
||||
NSString *filePath = [self sessionIdFilePath];
|
||||
if (![self archiveObject:@(sessionid) withFilePath:filePath]) {
|
||||
TDLogError(@"%@ unable to archive identifyId", self);
|
||||
}
|
||||
}
|
||||
- (long long)unarchiveSessionID {
|
||||
return [[self unarchiveFromFile:[self sessionIdFilePath] asClass:[NSNumber class]] longLongValue];
|
||||
}
|
||||
|
||||
|
||||
- (void)archiveIdentifyId:(NSString *)identifyId {
|
||||
|
||||
NSString *filePath = [self identifyIdFilePath];
|
||||
if (![self archiveObject:[identifyId copy] withFilePath:filePath]) {
|
||||
TDLogError(@"%@ unable to archive identifyId", self);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString*)unarchiveIdentifyID {
|
||||
return [self unarchiveFromFile:[self identifyIdFilePath] asClass:[NSString class]];
|
||||
}
|
||||
|
||||
- (NSString*)unarchiveAccountID {
|
||||
return [self unarchiveFromFile:[self accountIDFilePath] asClass:[NSString class]];
|
||||
}
|
||||
|
||||
- (void)archiveUploadSize:(NSNumber *)uploadSize {
|
||||
NSString *filePath = [self uploadSizeFilePath];
|
||||
if (![self archiveObject:uploadSize withFilePath:filePath]) {
|
||||
TDLogError(@"%@ unable to archive uploadSize", self);
|
||||
}
|
||||
}
|
||||
- (NSNumber*)unarchiveUploadSize {
|
||||
NSNumber* uploadSize = [self unarchiveFromFile:[self uploadSizeFilePath] asClass:[NSNumber class]];
|
||||
if (!uploadSize) {
|
||||
uploadSize = [NSNumber numberWithInteger:30];
|
||||
}
|
||||
return uploadSize;
|
||||
}
|
||||
|
||||
- (void)archiveUploadInterval:(NSNumber *)uploadInterval {
|
||||
NSString *filePath = [self uploadIntervalFilePath];
|
||||
if (![self archiveObject:uploadInterval withFilePath:filePath]) {
|
||||
TDLogError(@"%@ unable to archive uploadInterval", self);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSNumber*)unarchiveUploadInterval {
|
||||
NSNumber* uploadInterval = [self unarchiveFromFile:[self uploadIntervalFilePath] asClass:[NSNumber class]];
|
||||
if (!uploadInterval) {
|
||||
uploadInterval = [NSNumber numberWithInteger:30];
|
||||
}
|
||||
return uploadInterval;
|
||||
}
|
||||
|
||||
- (void)archiveAccountID:(NSString *)accountID {
|
||||
NSString *filePath = [self accountIDFilePath];
|
||||
if (![self archiveObject:[accountID copy] withFilePath:filePath]) {
|
||||
TDLogError(@"%@ unable to archive accountID", self);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)archiveSuperProperties:(NSDictionary *)superProperties {
|
||||
NSString *filePath = [self superPropertiesFilePath];
|
||||
if (![self archiveObject:[superProperties copy] withFilePath:filePath]) {
|
||||
TDLogError(@"%@ unable to archive superProperties", self);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSDictionary*)unarchiveSuperProperties {
|
||||
return [self unarchiveFromFile:[self superPropertiesFilePath] asClass:[NSDictionary class]];
|
||||
}
|
||||
|
||||
- (void)archiveTrackPause:(BOOL)trackPause {
|
||||
NSString *filePath = [self trackPauseFilePath];
|
||||
if (![self archiveObject:[NSNumber numberWithBool:trackPause] withFilePath:filePath]) {
|
||||
TDLogError(@"%@ unable to archive trackPause", self);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)unarchiveTrackPause {
|
||||
NSNumber *trackPause = (NSNumber *)[self unarchiveFromFile:[self trackPauseFilePath] asClass:[NSNumber class]];
|
||||
return [trackPause boolValue];
|
||||
}
|
||||
|
||||
- (void)archiveOptOut:(BOOL)optOut {
|
||||
NSString *filePath = [self optOutFilePath];
|
||||
if (![self archiveObject:[NSNumber numberWithBool:optOut] withFilePath:filePath]) {
|
||||
TDLogError(@"%@ unable to archive isOptOut", self);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)unarchiveOptOut {
|
||||
NSNumber *optOut = (NSNumber *)[self unarchiveFromFile:[self optOutFilePath] asClass:[NSNumber class]];
|
||||
return [optOut boolValue];
|
||||
}
|
||||
|
||||
- (void)archiveIsEnabled:(BOOL)isEnabled {
|
||||
NSString *filePath = [self enabledFilePath];
|
||||
if (![self archiveObject:[NSNumber numberWithBool:isEnabled] withFilePath:filePath]) {
|
||||
TDLogError(@"%@ unable to archive isEnabled", self);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)unarchiveEnabled {
|
||||
NSNumber *enabled = (NSNumber *)[self unarchiveFromFile:[self enabledFilePath] asClass:[NSNumber class]];
|
||||
if (enabled == nil) {
|
||||
return YES;
|
||||
} else {
|
||||
return [enabled boolValue];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)archiveDeviceId:(NSString *)deviceId {
|
||||
NSString *filePath = [self deviceIdFilePath];
|
||||
if (![self archiveObject:[deviceId copy] withFilePath:filePath]) {
|
||||
TDLogError(@"%@ unable to archive deviceId", self);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)unarchiveDeviceId {
|
||||
return [self unarchiveFromFile:[self deviceIdFilePath] asClass:[NSString class]];
|
||||
}
|
||||
|
||||
- (void)archiveInstallTimes:(NSString *)installTimes {
|
||||
NSString *filePath = [self installTimesFilePath];
|
||||
if (![self archiveObject:[installTimes copy] withFilePath:filePath]) {
|
||||
TDLogError(@"%@ unable to archive installTimes", self);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)unarchiveInstallTimes {
|
||||
return [self unarchiveFromFile:[self installTimesFilePath] asClass:[NSString class]];
|
||||
}
|
||||
|
||||
- (BOOL)archiveObject:(id)object withFilePath:(NSString *)filePath {
|
||||
@try {
|
||||
if (![NSKeyedArchiver archiveRootObject:object toFile:filePath]) {
|
||||
return NO;
|
||||
}
|
||||
} @catch (NSException *exception) {
|
||||
TDLogError(@"Got exception: %@, reason: %@. You can only send to Thinking values that inherit from NSObject and implement NSCoding.", exception.name, exception.reason);
|
||||
return NO;
|
||||
}
|
||||
|
||||
[self addSkipBackupAttributeToItemAtPath:filePath];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)addSkipBackupAttributeToItemAtPath:(NSString *)filePathString {
|
||||
NSURL *URL = [NSURL fileURLWithPath:filePathString];
|
||||
assert([[NSFileManager defaultManager] fileExistsAtPath:[URL path]]);
|
||||
|
||||
NSError *error = nil;
|
||||
BOOL success = [URL setResourceValue:[NSNumber numberWithBool:YES]
|
||||
forKey:NSURLIsExcludedFromBackupKey error:&error];
|
||||
if (!success) {
|
||||
TDLogError(@"Error excluding %@ from backup %@", [URL lastPathComponent], error);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
- (id)unarchiveFromFile:(NSString *)filePath asClass:(Class)class {
|
||||
id unarchivedData = nil;
|
||||
@try {
|
||||
unarchivedData = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
|
||||
if (![unarchivedData isKindOfClass:class]) {
|
||||
unarchivedData = nil;
|
||||
}
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
TDLogError(@"Error unarchive in %@", filePath);
|
||||
unarchivedData = nil;
|
||||
NSError *error = NULL;
|
||||
BOOL removed = [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
|
||||
if (!removed) {
|
||||
TDLogDebug(@"Error remove file in %@, error: %@", filePath, error);
|
||||
}
|
||||
}
|
||||
return unarchivedData;
|
||||
}
|
||||
|
||||
- (NSString *)superPropertiesFilePath {
|
||||
return [self persistenceFilePath:@"superProperties"];
|
||||
}
|
||||
|
||||
- (NSString *)accountIDFilePath {
|
||||
return [self persistenceFilePath:@"accountID"];
|
||||
}
|
||||
|
||||
- (NSString *)uploadSizeFilePath {
|
||||
return [self persistenceFilePath:@"uploadSize"];
|
||||
}
|
||||
|
||||
- (NSString *)uploadIntervalFilePath {
|
||||
return [self persistenceFilePath:@"uploadInterval"];
|
||||
}
|
||||
|
||||
- (NSString *)identifyIdFilePath {
|
||||
return [self persistenceFilePath:@"identifyId"];
|
||||
}
|
||||
|
||||
- (NSString *)sessionIdFilePath {
|
||||
return [self persistenceFilePath:@"sessionId"];
|
||||
}
|
||||
|
||||
- (NSString *)enabledFilePath {
|
||||
return [self persistenceFilePath:@"isEnabled"];
|
||||
}
|
||||
|
||||
- (NSString *)trackPauseFilePath {
|
||||
return [self persistenceFilePath:@"trackPause"];
|
||||
}
|
||||
|
||||
- (NSString *)optOutFilePath {
|
||||
return [self persistenceFilePath:@"optOut"];
|
||||
}
|
||||
|
||||
- (NSString *)deviceIdFilePath {
|
||||
return [self persistenceFilePath:@"deviceId"];
|
||||
}
|
||||
|
||||
- (NSString *)installTimesFilePath {
|
||||
return [self persistenceFilePath:@"installTimes"];
|
||||
}
|
||||
|
||||
- (NSString *)persistenceFilePath:(NSString *)data{
|
||||
NSString *filename = [NSString stringWithFormat:@"thinking-%@-%@.plist", self.appid, data];
|
||||
return [[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject]
|
||||
stringByAppendingPathComponent:filename];
|
||||
}
|
||||
|
||||
- (NSString*)unarchiveOldLoginId {
|
||||
return [[NSUserDefaults standardUserDefaults] objectForKey:@"thinkingdata_accountId"];
|
||||
}
|
||||
|
||||
- (void)deleteOldLoginId {
|
||||
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"thinkingdata_accountId"];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
|
||||
[dic setObject:self.appid forKey:@"appid"];
|
||||
[dic setObject:[self unarchiveIdentifyID]?:@"" forKey:@"distincid"];
|
||||
[dic setObject:[self unarchiveAccountID]?:@"" forKey:@"accountID"];
|
||||
[dic setObject:[self unarchiveUploadSize] forKey:@"uploadSize"];
|
||||
[dic setObject:[self unarchiveUploadInterval] forKey:@"uploadInterval"];
|
||||
[dic setObject:[self unarchiveSuperProperties]?:@{} forKey:@"superProperties"];
|
||||
[dic setObject:[NSNumber numberWithBool:[self unarchiveOptOut] ]forKey:@"optOut"];
|
||||
[dic setObject:[NSNumber numberWithBool:[self unarchiveEnabled]] forKey:@"isEnabled"];
|
||||
[dic setObject:[NSNumber numberWithBool:[self unarchiveTrackPause]] forKey:@"isTrackPause"];
|
||||
[dic setObject:[self unarchiveDeviceId]?:@"" forKey:@"deviceId"];
|
||||
[dic setObject:[self unarchiveInstallTimes]?:@"" forKey:@"installTimes"];
|
||||
return [TDJSONUtil JSONStringForObject:dic];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8cea36e1d5b36b645ad9fb61e7396876
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,14 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface TDKeychainHelper : NSObject
|
||||
|
||||
- (void)saveDeviceId:(NSString *)string;
|
||||
- (void)saveInstallTimes:(NSString *)string;
|
||||
- (void)readOldKeychain;
|
||||
|
||||
- (NSString *)readDeviceId;
|
||||
- (NSString *)readInstallTimes;
|
||||
- (NSString *)getInstallTimesOld;
|
||||
- (NSString *)getDeviceIdOld;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 56f4c1da82e73e04e89b68eee21ee250
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,121 @@
|
||||
#import "TDKeychainHelper.h"
|
||||
#import "TDLogging.h"
|
||||
|
||||
static NSString * const TDKeychainService = @"com.thinkingddata.analytics.service";
|
||||
static NSString * const TDInstallTimes = @"com.thinkingddata.analytics.installtimes";
|
||||
static NSString * const TDDeviceID = @"com.thinkingddata.analytics.deviceid";
|
||||
|
||||
@interface TDKeychainHelper ()
|
||||
|
||||
@property (atomic, strong) NSDictionary *oldKeychain;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TDKeychainHelper
|
||||
|
||||
- (void)saveItem:(NSString *)string forKey:(NSString *)key {
|
||||
NSData *encodeData = [string dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSString *originPassword = [self dataForKey:key];
|
||||
if (originPassword.length > 0) {
|
||||
NSMutableDictionary *updateAttributes = [NSMutableDictionary dictionary];
|
||||
updateAttributes[(__bridge id)kSecValueData] = encodeData;
|
||||
NSMutableDictionary *query = [self keychainQueryWithAccount:key];
|
||||
OSStatus statusCode = SecItemUpdate((__bridge CFDictionaryRef)query,(__bridge CFDictionaryRef)updateAttributes);
|
||||
if (statusCode != noErr) {
|
||||
TDLogError(@"Keychain Update Error" );
|
||||
}
|
||||
} else {
|
||||
NSMutableDictionary *attributes = [self keychainQueryWithAccount:key];
|
||||
attributes[(__bridge id)kSecValueData] = encodeData;
|
||||
OSStatus statusCode = SecItemAdd((__bridge CFDictionaryRef)attributes, nil);
|
||||
if (statusCode != noErr) {
|
||||
TDLogError(@"Keychain Add Error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)saveInstallTimes:(NSString *)string {
|
||||
[self saveItem:string forKey:TDInstallTimes];
|
||||
}
|
||||
|
||||
- (void)saveDeviceId:(NSString *)string {
|
||||
[self saveItem:string forKey:TDDeviceID];
|
||||
}
|
||||
|
||||
- (NSString *)dataForKey:(NSString *)key {
|
||||
NSMutableDictionary *attributes = [self keychainQueryWithAccount:key];
|
||||
attributes[(__bridge id)kSecMatchLimit] = (__bridge id)(kSecMatchLimitOne);
|
||||
attributes[(__bridge id)kSecReturnData] = (__bridge id)(kCFBooleanTrue);
|
||||
CFTypeRef data = nil;
|
||||
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)attributes,(CFTypeRef *)&data);
|
||||
if (status != errSecSuccess) {
|
||||
return nil;
|
||||
}
|
||||
NSData *encodeData = [NSData dataWithData:(__bridge NSData *)data];
|
||||
if (data) {
|
||||
CFRelease(data);
|
||||
}
|
||||
if (encodeData) {
|
||||
return [[NSString alloc] initWithData:encodeData encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)readOldKeychain {
|
||||
NSMutableDictionary *genericPasswordQuery = [[NSMutableDictionary alloc] init];
|
||||
[genericPasswordQuery setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
|
||||
[genericPasswordQuery setObject:@"ThinkingdataService" forKey:(__bridge id)kSecAttrGeneric];
|
||||
[genericPasswordQuery setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
|
||||
[genericPasswordQuery setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes];
|
||||
NSDictionary *tempQuery = [NSDictionary dictionaryWithDictionary:genericPasswordQuery];
|
||||
CFTypeRef cfDictionary = NULL;
|
||||
if (SecItemCopyMatching((__bridge CFDictionaryRef)tempQuery, &cfDictionary) == noErr) {
|
||||
NSMutableDictionary *returnDict = [NSMutableDictionary dictionaryWithDictionary:(__bridge_transfer NSDictionary *)cfDictionary];
|
||||
[returnDict setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
|
||||
[returnDict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
|
||||
|
||||
OSStatus keychainErr = noErr;
|
||||
CFTypeRef cfXmlData = NULL;
|
||||
keychainErr = SecItemCopyMatching((__bridge CFDictionaryRef)returnDict, &cfXmlData);
|
||||
|
||||
if (keychainErr == noErr) {
|
||||
NSData *xmlData = (__bridge_transfer NSData *)cfXmlData;
|
||||
NSPropertyListFormat fmt;
|
||||
NSDictionary *resultsInfo = [NSPropertyListSerialization propertyListWithData:xmlData options:NSPropertyListMutableContainersAndLeaves format:&fmt error:nil];
|
||||
self.oldKeychain = resultsInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)getInstallTimesOld {
|
||||
if (self.oldKeychain)
|
||||
return [NSString stringWithFormat:@"%@",[self.oldKeychain objectForKey:@"thinking_setup_index"]];
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *)getDeviceIdOld {
|
||||
if (self.oldKeychain)
|
||||
return [self.oldKeychain objectForKey:@"thinking_device_id"];
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *)readDeviceId {
|
||||
NSString *data = [self dataForKey:TDDeviceID];
|
||||
return data;
|
||||
}
|
||||
|
||||
- (NSString *)readInstallTimes {
|
||||
NSString *data = [self dataForKey:TDInstallTimes];
|
||||
return data;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *)keychainQueryWithAccount:(NSString *)account {
|
||||
NSMutableDictionary *query = [NSMutableDictionary dictionary];
|
||||
query[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword;
|
||||
query[(__bridge id)kSecAttrService] = TDKeychainService;
|
||||
query[(__bridge id)kSecAttrAccount] = account;
|
||||
return query;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b23cf96728e0d454eb567e2acccc6894
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,19 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class TDEventRecord;
|
||||
|
||||
@interface TDSqliteDataQueue : NSObject
|
||||
|
||||
+ (TDSqliteDataQueue *)sharedInstanceWithAppid:(NSString *)appid;
|
||||
- (NSInteger)addObject:(id)obj withAppid:(NSString *)appid;
|
||||
- (NSArray<TDEventRecord *> *)getFirstRecords:(NSUInteger)recordSize withAppid:(NSString *)appid;
|
||||
- (BOOL)removeFirstRecords:(NSUInteger)recordSize withAppid:(NSString *)appid;
|
||||
- (void)deleteAll:(NSString *)appid;
|
||||
- (NSInteger)sqliteCountForAppid:(NSString *)appid;
|
||||
- (void)addColumnText:(NSString *)columnText;
|
||||
|
||||
- (NSArray *)upadteRecordIds:(NSArray<NSNumber *> *)recordIds;
|
||||
- (BOOL)removeDataWithuids:(NSArray *)recordIDs;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fdb622acdcbf89a4689922948177488e
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,347 @@
|
||||
#import "TDSqliteDataQueue.h"
|
||||
#import <sqlite3.h>
|
||||
|
||||
#import "TDLogging.h"
|
||||
#import "TDJSONUtil.h"
|
||||
#import "TDConfig.h"
|
||||
#import "TDEventRecord.h"
|
||||
|
||||
@implementation TDSqliteDataQueue {
|
||||
sqlite3 *_database;
|
||||
NSInteger _allmessageCount;
|
||||
}
|
||||
|
||||
- (void) closeDatabase {
|
||||
sqlite3_close(_database);
|
||||
sqlite3_shutdown();
|
||||
}
|
||||
|
||||
- (void) dealloc {
|
||||
[self closeDatabase];
|
||||
}
|
||||
|
||||
+ (TDSqliteDataQueue *)sharedInstanceWithAppid:(NSString *)appid {
|
||||
static TDSqliteDataQueue *sharedInstance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSString *filepath = [[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"TDData-data.plist"];
|
||||
sharedInstance = [[self alloc] initWithPath:filepath withAppid:appid];
|
||||
TDLogDebug(@"sqlite path:%@", filepath);
|
||||
});
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (id)initWithPath:(NSString *)filePath withAppid:(NSString *)appid {
|
||||
self = [super init];
|
||||
if (sqlite3_initialize() != SQLITE_OK) {
|
||||
return nil;
|
||||
}
|
||||
if (sqlite3_open_v2([filePath UTF8String], &_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL) == SQLITE_OK ) {
|
||||
NSString *_sql = @"create table if not exists TDData (id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT, appid TEXT, creatAt INTEGER)";
|
||||
char *errorMsg;
|
||||
if (sqlite3_exec(_database, [_sql UTF8String], NULL, NULL, &errorMsg)==SQLITE_OK) {
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_allmessageCount = [self sqliteCount];
|
||||
|
||||
if (![self isExistColumnInTable:@"appid"] || ![self isExistColumnInTable:@"creatAt"]) {
|
||||
[self addColumn:appid];
|
||||
} else if (_allmessageCount > 0) {
|
||||
[self delExpiredData];
|
||||
}
|
||||
|
||||
if (![self isExistColumnInTable:@"uuid"]) {
|
||||
[self addColumnText:@"uuid"];
|
||||
}
|
||||
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)addColumn:(NSString *)appid {
|
||||
int epochInterval = [[NSDate date] timeIntervalSince1970];
|
||||
NSString *query;
|
||||
if (appid.length > 0 && [appid isKindOfClass: [NSString class]])
|
||||
query = [NSString stringWithFormat:@"alter table TDData add 'appid' TEXT default \"%@\"", appid];
|
||||
else
|
||||
query = [NSString stringWithFormat:@"alter table TDData add 'appid' TEXT"];
|
||||
NSString *query2 = [NSString stringWithFormat:@"alter table TDData add 'creatAt' INTEGER default %d ", epochInterval];
|
||||
char *errMsg;
|
||||
@try {
|
||||
sqlite3_exec(_database, [query UTF8String], NULL, NULL, &errMsg);
|
||||
sqlite3_exec(_database, [query2 UTF8String], NULL, NULL, &errMsg);
|
||||
} @catch (NSException *exception) {
|
||||
TDLogError(@"addColumn: %@", exception);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addColumnText:(NSString *)columnText {
|
||||
NSString *query = [NSString stringWithFormat:@"alter table TDData add '%@' TEXT", columnText];;
|
||||
char *errMsg;
|
||||
@try {
|
||||
sqlite3_exec(_database, [query UTF8String], NULL, NULL, &errMsg);
|
||||
} @catch (NSException *exception) {
|
||||
TDLogError(@"addColumn: %@", exception);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isExistColumnInTable:(NSString *)column {
|
||||
sqlite3_stmt *statement = nil;
|
||||
NSString *sql = [NSString stringWithFormat:@"PRAGMA table_info(TDData)"];
|
||||
if (sqlite3_prepare_v2(_database, [sql UTF8String], -1, &statement, NULL) != SQLITE_OK ) {
|
||||
sqlite3_finalize(statement);
|
||||
return NO;
|
||||
}
|
||||
while (sqlite3_step(statement) == SQLITE_ROW) {
|
||||
NSString *columntem = [[NSString alloc] initWithCString:(char *)sqlite3_column_text(statement, 1) encoding:NSUTF8StringEncoding];
|
||||
|
||||
if ([column isEqualToString:columntem]) {
|
||||
sqlite3_finalize(statement);
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)delExpiredData {
|
||||
NSTimeInterval oneDay = 24*60*60*1;
|
||||
NSDate *date = [[NSDate alloc] initWithTimeIntervalSinceNow: -oneDay * [TDConfig expirationDays]];
|
||||
int expirationDate = [date timeIntervalSince1970];
|
||||
[self removeOldRecords:expirationDate];
|
||||
}
|
||||
|
||||
- (NSInteger)addObject:(id)obj withAppid:(NSString *)appid {
|
||||
NSUInteger maxCacheSize = [TDConfig maxNumEvents];
|
||||
if (_allmessageCount >= maxCacheSize) {
|
||||
[self removeFirstRecords:100 withAppid:nil];
|
||||
}
|
||||
|
||||
NSString *jsonStr = [TDJSONUtil JSONStringForObject:obj];
|
||||
if (!jsonStr) {
|
||||
return [self sqliteCountForAppid:appid];
|
||||
}
|
||||
NSTimeInterval epochInterval = [[NSDate date] timeIntervalSince1970];
|
||||
NSString *query = @"INSERT INTO TDData(content, appid, creatAt) values(?, ?, ?)";
|
||||
sqlite3_stmt *insertStatement;
|
||||
int rc;
|
||||
rc = sqlite3_prepare_v2(_database, [query UTF8String],-1, &insertStatement, nil);
|
||||
if (rc == SQLITE_OK) {
|
||||
sqlite3_bind_text(insertStatement, 1, [jsonStr UTF8String], -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(insertStatement, 2, [appid UTF8String], -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_int(insertStatement, 3, epochInterval);
|
||||
|
||||
rc = sqlite3_step(insertStatement);
|
||||
if (rc == SQLITE_DONE) {
|
||||
_allmessageCount ++;
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(insertStatement);
|
||||
return [self sqliteCountForAppid:appid];
|
||||
}
|
||||
|
||||
- (NSArray<TDEventRecord *> *)getFirstRecords:(NSUInteger)recordSize withAppid:(NSString *)appid {
|
||||
if (_allmessageCount == 0) {
|
||||
return @[];
|
||||
}
|
||||
|
||||
NSMutableArray *records = [[NSMutableArray alloc] init];
|
||||
NSString *query = @"SELECT id,content FROM TDData where appid=? ORDER BY id ASC LIMIT ?";
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
int rc = sqlite3_prepare_v2(_database, [query UTF8String], -1, &stmt, NULL);
|
||||
if (rc == SQLITE_OK) {
|
||||
sqlite3_bind_text(stmt, 1, [appid UTF8String], -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_int(stmt, 2, (int)recordSize);
|
||||
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
sqlite3_int64 index = sqlite3_column_int64(stmt, 0);
|
||||
char *jsonChar = (char *)sqlite3_column_text(stmt, 1);
|
||||
if (!jsonChar) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NSData *jsonData = [[NSString stringWithUTF8String:jsonChar] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSError *err;
|
||||
if (jsonData) {
|
||||
NSDictionary *eventDict = [NSJSONSerialization JSONObjectWithData:jsonData
|
||||
options:NSJSONReadingMutableContainers
|
||||
error:&err];
|
||||
if (!err && [eventDict isKindOfClass:[NSDictionary class]]) {
|
||||
[records addObject:[[TDEventRecord alloc] initWithIndex:[NSNumber numberWithLongLong:index] content:eventDict]];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
return records;
|
||||
}
|
||||
|
||||
- (BOOL)removeDataWithuids:(NSArray *)uids {
|
||||
|
||||
if (uids.count == 0) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSString *query = [NSString stringWithFormat:@"DELETE FROM TDData WHERE uuid IN (%@);", [uids componentsJoinedByString:@","]];
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
if (sqlite3_prepare_v2(_database, query.UTF8String, -1, &stmt, NULL) != SQLITE_OK) {
|
||||
TDLogError(@"Delete records Error: %s", sqlite3_errmsg(_database));
|
||||
return NO;
|
||||
}
|
||||
BOOL success = YES;
|
||||
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||
TDLogError(@"Delete records Error: %s", sqlite3_errmsg(_database));
|
||||
success = NO;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
_allmessageCount = [self sqliteCount];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)removeFirstRecords:(NSUInteger)recordSize withAppid:(NSString *)appid {
|
||||
NSString *query;
|
||||
|
||||
if (appid.length == 0) {
|
||||
query = @"DELETE FROM TDData WHERE id IN (SELECT id FROM TDData ORDER BY id ASC LIMIT ?)";
|
||||
} else {
|
||||
query = @"DELETE FROM TDData WHERE id IN (SELECT id FROM TDData where appid=? ORDER BY id ASC LIMIT ?)";
|
||||
}
|
||||
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
int rc = sqlite3_prepare_v2(_database, [query UTF8String], -1, &stmt, NULL);
|
||||
|
||||
if (rc == SQLITE_OK) {
|
||||
if (appid.length == 0) {
|
||||
sqlite3_bind_int(stmt, 1, (int)recordSize);
|
||||
} else {
|
||||
sqlite3_bind_text(stmt, 1, [appid UTF8String], -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_int(stmt, 2, (int)recordSize);
|
||||
}
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc != SQLITE_DONE && rc != SQLITE_OK) {
|
||||
sqlite3_finalize(stmt);
|
||||
return NO;
|
||||
}
|
||||
} else {
|
||||
sqlite3_finalize(stmt);
|
||||
return NO;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
_allmessageCount = [self sqliteCount];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSArray *)upadteRecordIds:(NSArray<NSNumber *> *)recordIds {
|
||||
if (recordIds.count == 0) {
|
||||
return @[];
|
||||
}
|
||||
NSMutableArray *uids = [NSMutableArray arrayWithCapacity:recordIds.count];
|
||||
[recordIds enumerateObjectsUsingBlock:^(NSNumber *recordId, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
NSString *uuid = [self rand13NumString];
|
||||
NSString *query = [NSString stringWithFormat:@"UPDATE TDData SET uuid = '%@' WHERE id = %lld;", uuid, [recordId longLongValue]];
|
||||
if ([self execUpdateSQL:query]) {
|
||||
[uids addObject:uuid];
|
||||
}
|
||||
}];
|
||||
return uids;
|
||||
}
|
||||
|
||||
|
||||
|
||||
-(NSString *)rand13NumString
|
||||
{
|
||||
int NUMBER_OF_CHARS = 13;
|
||||
|
||||
char data[NUMBER_OF_CHARS];
|
||||
|
||||
for (int x = 0; x < NUMBER_OF_CHARS; data[x++] = (char)('1' + (arc4random_uniform(9))));
|
||||
|
||||
NSString *numString = [[NSString alloc] initWithBytes:data length:NUMBER_OF_CHARS encoding:NSUTF8StringEncoding];
|
||||
|
||||
return numString;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)execUpdateSQL:(NSString *)sql {
|
||||
|
||||
sqlite3_stmt *stmt;
|
||||
if (sqlite3_prepare_v2(_database, sql.UTF8String, -1, &stmt, NULL) != SQLITE_OK) {
|
||||
TDLogError(@"Update Records Error: %s", sqlite3_errmsg(_database));
|
||||
sqlite3_finalize(stmt);
|
||||
return NO;
|
||||
}
|
||||
if (sqlite3_step(stmt) != SQLITE_DONE) {
|
||||
TDLogError(@"Update Records Error: %s", sqlite3_errmsg(_database));
|
||||
sqlite3_finalize(stmt);
|
||||
return NO;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)removeOldRecords:(int)timestamp {
|
||||
NSString *query = @"DELETE FROM TDData WHERE creatAt<?";
|
||||
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
int rc = sqlite3_prepare_v2(_database, [query UTF8String], -1, &stmt, NULL);
|
||||
if (rc == SQLITE_OK) {
|
||||
sqlite3_bind_int(stmt, 1, (int)timestamp);
|
||||
sqlite3_step(stmt);
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
_allmessageCount = [self sqliteCount];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSInteger)sqliteCount {
|
||||
return [self sqliteCountForAppid:nil];
|
||||
}
|
||||
|
||||
- (NSInteger)sqliteCountForAppid:(NSString *)appid {
|
||||
NSString *query;
|
||||
NSInteger count = 0;
|
||||
if (appid == nil) {
|
||||
query = @"select count(*) from TDData";
|
||||
} else {
|
||||
query = @"select count(*) from TDData where appid=? ";
|
||||
}
|
||||
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
int rc = sqlite3_prepare_v2(_database, [query UTF8String], -1, &stmt, NULL);
|
||||
|
||||
if (rc == SQLITE_OK) {
|
||||
if (appid.length > 0) {
|
||||
sqlite3_bind_text(stmt, 1, [appid UTF8String], -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
count = sqlite3_column_int(stmt, 0);
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
return count;
|
||||
}
|
||||
|
||||
- (void)deleteAll:(NSString *)appid {
|
||||
if ([appid isKindOfClass:[NSString class]] && appid.length > 0) {
|
||||
NSString *query = @"DELETE FROM TDData where appid=? ";
|
||||
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
int rc = sqlite3_prepare_v2(_database, [query UTF8String], -1, &stmt, NULL);
|
||||
if (rc == SQLITE_OK) {
|
||||
sqlite3_bind_text(stmt, 1, [appid UTF8String], -1, SQLITE_TRANSIENT);
|
||||
sqlite3_step(stmt);
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
_allmessageCount = [self sqliteCount];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 712995a22fa5736499cf8ceee5c55c8d
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user