Ta android + ios
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "ThinkingAnalyticsSDKPrivate.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void (^TDFlushConfigBlock)(NSDictionary *result, NSError * _Nullable error);
|
||||
|
||||
@interface TANetwork : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
|
||||
|
||||
@property (nonatomic, copy) NSString *appid;
|
||||
@property (nonatomic, strong) NSURL *serverURL;
|
||||
|
||||
@property (nonatomic, strong) NSURL *serverDebugURL;
|
||||
@property (nonatomic, assign) ThinkingAnalyticsDebugMode debugMode;
|
||||
@property (nonatomic, strong) TDSecurityPolicy *securityPolicy;
|
||||
@property (nonatomic, copy) TDURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge;
|
||||
|
||||
- (BOOL)flushEvents:(NSArray<NSDictionary *> *)events;
|
||||
//- (void)flushEvents:(NSArray<NSDictionary *> *)recordArray completion:(nullable void(^)(BOOL))completion;
|
||||
- (void)fetchRemoteConfig:(NSString *)appid handler:(TDFlushConfigBlock)handler;
|
||||
- (int)flushDebugEvents:(NSDictionary *)record withAppid:(NSString *)appid;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43b25c6a08dffb94091064201bb4bbfc
|
||||
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,309 @@
|
||||
#import "TANetwork.h"
|
||||
|
||||
#import "NSData+TDGzip.h"
|
||||
#import "TDJSONUtil.h"
|
||||
#import "TDLogging.h"
|
||||
#import "TDSecurityPolicy.h"
|
||||
#import "TDAppState.h"
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#import "TDToastView.h"
|
||||
#endif
|
||||
|
||||
static NSString *kTAIntegrationType = @"TA-Integration-Type";
|
||||
static NSString *kTAIntegrationVersion = @"TA-Integration-Version";
|
||||
static NSString *kTAIntegrationCount = @"TA-Integration-Count";
|
||||
static NSString *kTAIntegrationExtra = @"TA-Integration-Extra";
|
||||
static NSString *kTADatasType = @"TA-Datas-Type";
|
||||
|
||||
@implementation TANetwork
|
||||
|
||||
- (NSURLSession *)sharedURLSession {
|
||||
static NSURLSession *sharedSession = nil;
|
||||
@synchronized(self) {
|
||||
if (sharedSession == nil) {
|
||||
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
sharedSession = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
|
||||
}
|
||||
}
|
||||
return sharedSession;
|
||||
}
|
||||
|
||||
- (NSString *)URLEncode:(NSString *)string {
|
||||
NSString *encodedString = [string stringByAddingPercentEncodingWithAllowedCharacters:[[NSCharacterSet characterSetWithCharactersInString:@"?!@#$^&%*+,:;='\"`<>()[]{}/\\| "] invertedSet]];
|
||||
return encodedString;
|
||||
}
|
||||
|
||||
- (int)flushDebugEvents:(NSDictionary *)record withAppid:(NSString *)appid {
|
||||
__block int debugResult = -1;
|
||||
NSMutableDictionary *recordDic = [record mutableCopy];
|
||||
NSMutableDictionary *properties = [[recordDic objectForKey:@"properties"] mutableCopy];
|
||||
|
||||
if ([ThinkingAnalyticsSDK isTrackEvent:[record objectForKey:@"#type"]]) {
|
||||
@synchronized ([TDDeviceInfo sharedManager]) {
|
||||
[properties addEntriesFromDictionary:[[TDDeviceInfo sharedManager] getAutomaticData]];
|
||||
}
|
||||
}
|
||||
[recordDic setObject:properties forKey:@"properties"];
|
||||
NSString *jsonString = [TDJSONUtil JSONStringForObject:recordDic];
|
||||
NSMutableURLRequest *request = [self buildDebugRequestWithJSONString:jsonString withAppid:appid withDeviceId:[[[TDDeviceInfo sharedManager] getAutomaticData] objectForKey:@"#device_id"]];
|
||||
dispatch_semaphore_t flushSem = dispatch_semaphore_create(0);
|
||||
|
||||
void (^block)(NSData *, NSURLResponse *, NSError *) = ^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
|
||||
if (error || ![response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
debugResult = -2;
|
||||
TDLogError(@"Debug Networking error:%@", error);
|
||||
[self callbackNetworkErrorWithRequest:jsonString error:error.debugDescription];
|
||||
dispatch_semaphore_signal(flushSem);
|
||||
return;
|
||||
}
|
||||
NSHTTPURLResponse *urlResponse = (NSHTTPURLResponse *)response;
|
||||
if ([urlResponse statusCode] == 200) {
|
||||
NSError *err;
|
||||
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSDictionary *retDic = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&err];
|
||||
if (err) {
|
||||
TDLogError(@"Debug data json error:%@", err);
|
||||
debugResult = -2;
|
||||
} else if ([[retDic objectForKey:@"errorLevel"] isEqualToNumber:[NSNumber numberWithInt:1]]) {
|
||||
debugResult = 1;
|
||||
NSArray* errorProperties = [retDic objectForKey:@"errorProperties"];
|
||||
NSMutableString *errorStr = [NSMutableString string];
|
||||
for (id obj in errorProperties) {
|
||||
NSString *errorReasons = [obj objectForKey:@"errorReason"];
|
||||
NSString *propertyName = [obj objectForKey:@"propertyName"];
|
||||
[errorStr appendFormat:@" propertyName:%@ errorReasons:%@\n", propertyName, errorReasons];
|
||||
}
|
||||
TDLogError(@"Debug data error:%@", errorStr);
|
||||
} else if ([[retDic objectForKey:@"errorLevel"] isEqualToNumber:[NSNumber numberWithInt:2]]) {
|
||||
debugResult = 2;
|
||||
NSString *errorReasons = [[retDic objectForKey:@"errorReasons"] componentsJoinedByString:@" "];
|
||||
TDLogError(@"Debug data error:%@", errorReasons);
|
||||
} else if ([[retDic objectForKey:@"errorLevel"] isEqualToNumber:[NSNumber numberWithInt:0]]) {
|
||||
debugResult = 0;
|
||||
TDLogDebug(@"Verify data success.");
|
||||
} else if ([[retDic objectForKey:@"errorLevel"] isEqualToNumber:[NSNumber numberWithInt:-1]]) {
|
||||
debugResult = -1;
|
||||
NSString *errorReasons = [[retDic objectForKey:@"errorReasons"] componentsJoinedByString:@" "];
|
||||
TDLogError(@"Debug mode error:%@", errorReasons);
|
||||
}
|
||||
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
if (debugResult == 0 || debugResult == 1 || debugResult == 2) {
|
||||
#if TARGET_OS_IOS
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIApplication *application = [TDAppState sharedApplication];
|
||||
if (![application isKindOfClass:UIApplication.class]) {
|
||||
return;
|
||||
}
|
||||
UIWindow *window = application.keyWindow;
|
||||
[TDToastView showInWindow:window text:[NSString stringWithFormat:@"The current mode is:%@", self.debugMode == ThinkingAnalyticsDebugOnly ? @"DebugOnly(Data is not persisted) \n The test joint debugging stage is allowed to open \n Please turn off the Debug function before the official launch" : @"Debug"] duration:2.0];
|
||||
});
|
||||
#endif
|
||||
}
|
||||
});
|
||||
|
||||
@try {
|
||||
if ([retDic isKindOfClass:[NSDictionary class]]) {
|
||||
if ([[(NSDictionary *)retDic objectForKey:@"errorLevel"] integerValue] != 0) {
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:retDic options:NSJSONWritingPrettyPrinted error:NULL];
|
||||
NSString *string = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
[self callbackNetworkErrorWithRequest:jsonString error:string];
|
||||
}
|
||||
}
|
||||
} @catch (NSException *exception) {
|
||||
|
||||
}
|
||||
} else {
|
||||
debugResult = -2;
|
||||
NSString *urlResponse = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
TDLogError(@"%@", [NSString stringWithFormat:@"Debug %@ network failed with response '%@'.", self, urlResponse]);
|
||||
[self callbackNetworkErrorWithRequest:jsonString error:urlResponse];
|
||||
}
|
||||
dispatch_semaphore_signal(flushSem);
|
||||
};
|
||||
|
||||
NSURLSessionDataTask *task = [[self sharedURLSession] dataTaskWithRequest:request completionHandler:block];
|
||||
[task resume];
|
||||
|
||||
dispatch_semaphore_wait(flushSem, DISPATCH_TIME_FOREVER);
|
||||
return debugResult;
|
||||
}
|
||||
|
||||
- (BOOL)flushEvents:(NSArray<NSDictionary *> *)recordArray {
|
||||
__block BOOL flushSucc = YES;
|
||||
UInt64 time = [[NSDate date] timeIntervalSince1970] * 1000;
|
||||
NSDictionary *flushDic = @{
|
||||
@"data": recordArray,
|
||||
@"#app_id": self.appid,
|
||||
@"#flush_time": @(time),
|
||||
};
|
||||
|
||||
__block BOOL isEncrypt;
|
||||
[recordArray enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
if ([obj.allKeys containsObject:@"ekey"]) {
|
||||
isEncrypt = YES;
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
|
||||
NSString *jsonString = [TDJSONUtil JSONStringForObject:flushDic];
|
||||
NSMutableURLRequest *request = [self buildRequestWithJSONString:jsonString];
|
||||
[request addValue:[TDDeviceInfo sharedManager].libName forHTTPHeaderField:kTAIntegrationType];
|
||||
[request addValue:[TDDeviceInfo sharedManager].libVersion forHTTPHeaderField:kTAIntegrationVersion];
|
||||
[request addValue:@(recordArray.count).stringValue forHTTPHeaderField:kTAIntegrationCount];
|
||||
[request addValue:@"iOS" forHTTPHeaderField:kTAIntegrationExtra];
|
||||
if (isEncrypt) {
|
||||
[request addValue:@"1" forHTTPHeaderField:kTADatasType];
|
||||
}
|
||||
// [request addValue:@"Keep-Alive" forHTTPHeaderField:@"Connection"];
|
||||
// [request addValue:@"timeout=15,max=100" forHTTPHeaderField:@"Keep-Alive"];
|
||||
|
||||
dispatch_semaphore_t flushSem = dispatch_semaphore_create(0);
|
||||
|
||||
void (^block)(NSData *, NSURLResponse *, NSError *) = ^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
if (error || ![response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
flushSucc = NO;
|
||||
TDLogError(@"Networking error:%@", error);
|
||||
[self callbackNetworkErrorWithRequest:jsonString error:error.debugDescription];
|
||||
dispatch_semaphore_signal(flushSem);
|
||||
return;
|
||||
}
|
||||
|
||||
NSHTTPURLResponse *urlResponse = (NSHTTPURLResponse *)response;
|
||||
if ([urlResponse statusCode] == 200) {
|
||||
flushSucc = YES;
|
||||
TDLogDebug(@"flush success sendContent---->:%@",flushDic);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
id result = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
|
||||
TDLogDebug(@"flush success responseData---->%@",result);
|
||||
|
||||
@try {
|
||||
if ([result isKindOfClass:[NSDictionary class]]) {
|
||||
if ([[(NSDictionary *)result objectForKey:@"code"] integerValue] != 0) {
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:result options:NSJSONWritingPrettyPrinted error:NULL];
|
||||
NSString *string = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
[self callbackNetworkErrorWithRequest:jsonString error:string];
|
||||
}
|
||||
}
|
||||
} @catch (NSException *exception) {
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
flushSucc = NO;
|
||||
NSString *urlResponse = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
TDLogError(@"%@", [NSString stringWithFormat:@"%@ network failed with response '%@'.", self, urlResponse]);
|
||||
[self callbackNetworkErrorWithRequest:jsonString error:urlResponse];
|
||||
}
|
||||
|
||||
dispatch_semaphore_signal(flushSem);
|
||||
};
|
||||
|
||||
NSURLSessionDataTask *task = [[self sharedURLSession] dataTaskWithRequest:request completionHandler:block];
|
||||
[task resume];
|
||||
dispatch_semaphore_wait(flushSem, DISPATCH_TIME_FOREVER);
|
||||
return flushSucc;
|
||||
}
|
||||
|
||||
- (void)callbackNetworkErrorWithRequest:(NSString *)request error:(NSString *)error {
|
||||
if (request == nil && error == nil) return;
|
||||
|
||||
ThinkingAnalyticsSDK *tdSDK = [ThinkingAnalyticsSDK sharedInstanceWithAppid:self.appid];
|
||||
if (tdSDK.errorCallback) {
|
||||
NSInteger code = 10001;
|
||||
NSString *errorMsg = error;
|
||||
NSString *ext = request;
|
||||
tdSDK.errorCallback(code, errorMsg, ext);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSMutableURLRequest *)buildRequestWithJSONString:(NSString *)jsonString {
|
||||
|
||||
NSData *zippedData = [NSData td_gzipData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
NSString *postBody = [zippedData base64EncodedStringWithOptions:0];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.serverURL];
|
||||
// NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://192.168.20.23:8991/sync"]];
|
||||
[request setHTTPMethod:@"POST"];
|
||||
[request setHTTPBody:[postBody dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
NSString *contentType = [NSString stringWithFormat:@"text/plain"];
|
||||
[request addValue:contentType forHTTPHeaderField:@"Content-Type"];
|
||||
[request setTimeoutInterval:60.0];
|
||||
return request;
|
||||
}
|
||||
|
||||
- (NSMutableURLRequest *)buildDebugRequestWithJSONString:(NSString *)jsonString withAppid:(NSString *)appid withDeviceId:(NSString *)deviceId {
|
||||
// dryRun=0, if the verification is passed, it will be put into storage. dryRun=1, no storage
|
||||
int dryRun = _debugMode == ThinkingAnalyticsDebugOnly ? 1 : 0;
|
||||
NSString *postData = [NSString stringWithFormat:@"appid=%@&source=client&dryRun=%d&deviceId=%@&data=%@", appid, dryRun, deviceId, [self URLEncode:jsonString]];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.serverDebugURL];
|
||||
[request setHTTPMethod:@"POST"];
|
||||
request.HTTPBody = [postData dataUsingEncoding:NSUTF8StringEncoding];
|
||||
return request;
|
||||
}
|
||||
|
||||
- (void)fetchRemoteConfig:(NSString *)appid handler:(TDFlushConfigBlock)handler {
|
||||
void (^block)(NSData *, NSURLResponse *, NSError *) = ^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
if (error || ![response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
TDLogError(@"Fetch remote config network failed:%@", error);
|
||||
return;
|
||||
}
|
||||
NSError *err;
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
NSDictionary *ret = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&err];
|
||||
if (err) {
|
||||
TDLogError(@"Fetch remote config json error:%@", err);
|
||||
} else if ([ret isKindOfClass:[NSDictionary class]] && [ret[@"code"] isEqualToNumber:[NSNumber numberWithInt:0]]) {
|
||||
TDLogDebug(@"Fetch remote config for %@ : %@", appid, [ret objectForKey:@"data"]);
|
||||
handler([ret objectForKey:@"data"], error);
|
||||
} else {
|
||||
TDLogError(@"Fetch remote config failed");
|
||||
}
|
||||
};
|
||||
NSString *urlStr = [NSString stringWithFormat:@"%@?appid=%@", self.serverURL, appid];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlStr]];
|
||||
[request setHTTPMethod:@"Get"];
|
||||
NSURLSessionDataTask *task = [[self sharedURLSession] dataTaskWithRequest:request completionHandler:block];
|
||||
[task resume];
|
||||
}
|
||||
|
||||
#pragma mark - NSURLSessionDelegate
|
||||
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
|
||||
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
NSURLCredential *credential = nil;
|
||||
|
||||
if (self.sessionDidReceiveAuthenticationChallenge) {
|
||||
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
|
||||
} else {
|
||||
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
|
||||
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
|
||||
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
|
||||
if (credential) {
|
||||
disposition = NSURLSessionAuthChallengeUseCredential;
|
||||
} else {
|
||||
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
}
|
||||
} else {
|
||||
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
|
||||
}
|
||||
} else {
|
||||
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
}
|
||||
}
|
||||
|
||||
if (completionHandler) {
|
||||
completionHandler(disposition, credential);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2b161a17028d0d419e63bf51c436166
|
||||
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,28 @@
|
||||
//
|
||||
// TAReachability.h
|
||||
// ThinkingSDK
|
||||
//
|
||||
// Created by Yangxiongon 2022/6/1.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "TDConstant.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TAReachability : NSObject
|
||||
|
||||
+ (ThinkingNetworkType)convertNetworkType:(NSString *)networkType;
|
||||
|
||||
+ (instancetype)shareInstance;
|
||||
|
||||
- (void)startMonitoring;
|
||||
|
||||
- (void)stopMonitoring;
|
||||
|
||||
- (NSString *)networkState;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3bf8629734a44364eb0dd013447864dc
|
||||
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,192 @@
|
||||
//
|
||||
// TAReachability.m
|
||||
// ThinkingSDK
|
||||
//
|
||||
// Created by Yangxiongon 2022/6/1.
|
||||
//
|
||||
|
||||
#import "TAReachability.h"
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
|
||||
|
||||
#if __has_include(<ThinkingSDK/TDLogging.h>)
|
||||
#import <ThinkingSDK/TDLogging.h>
|
||||
#else
|
||||
#import "TDLogging.h"
|
||||
#endif
|
||||
|
||||
|
||||
@interface TAReachability ()
|
||||
#if TARGET_OS_IOS
|
||||
@property (atomic, assign) SCNetworkReachabilityRef reachability;
|
||||
#endif
|
||||
@property (nonatomic, assign) BOOL isWifi;
|
||||
@property (nonatomic, assign) BOOL isWwan;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TAReachability
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
static void ThinkingReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) {
|
||||
TAReachability *instance = (__bridge TAReachability *)info;
|
||||
if (instance && [instance isKindOfClass:[TAReachability class]]) {
|
||||
[instance reachabilityChanged:flags];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//MARK: - Public Methods
|
||||
|
||||
+ (instancetype)shareInstance {
|
||||
static dispatch_once_t onceToken;
|
||||
static TAReachability *reachability = nil;
|
||||
dispatch_once(&onceToken, ^{
|
||||
reachability = [[TAReachability alloc] init];
|
||||
});
|
||||
return reachability;
|
||||
}
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
|
||||
- (NSString *)networkState {
|
||||
if (self.isWifi) {
|
||||
return @"WIFI";
|
||||
} else if (self.isWwan) {
|
||||
return [self currentRadio];
|
||||
} else {
|
||||
return @"NULL";
|
||||
}
|
||||
}
|
||||
|
||||
- (void)startMonitoring {
|
||||
[self stopMonitoring];
|
||||
|
||||
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL,"thinkingdata.cn");
|
||||
self.reachability = reachability;
|
||||
|
||||
if (self.reachability != NULL) {
|
||||
SCNetworkReachabilityFlags flags;
|
||||
BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(self.reachability, &flags);
|
||||
if (didRetrieveFlags) {
|
||||
self.isWifi = (flags & kSCNetworkReachabilityFlagsReachable) && !(flags & kSCNetworkReachabilityFlagsIsWWAN);
|
||||
self.isWwan = (flags & kSCNetworkReachabilityFlagsIsWWAN);
|
||||
}
|
||||
|
||||
SCNetworkReachabilityContext context = {0, (__bridge void *)self, NULL, NULL, NULL};
|
||||
if (SCNetworkReachabilitySetCallback(self.reachability, ThinkingReachabilityCallback, &context)) {
|
||||
if (!SCNetworkReachabilityScheduleWithRunLoop(self.reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes)) {
|
||||
SCNetworkReachabilitySetCallback(self.reachability, NULL, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stopMonitoring {
|
||||
if (!self.reachability) {
|
||||
return;
|
||||
}
|
||||
SCNetworkReachabilityUnscheduleFromRunLoop(self.reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
||||
}
|
||||
|
||||
+ (ThinkingNetworkType)convertNetworkType:(NSString *)networkType {
|
||||
if ([@"NULL" isEqualToString:networkType]) {
|
||||
return ThinkingNetworkTypeALL;
|
||||
} else if ([@"WIFI" isEqualToString:networkType]) {
|
||||
return ThinkingNetworkTypeWIFI;
|
||||
} else if ([@"2G" isEqualToString:networkType]) {
|
||||
return ThinkingNetworkType2G;
|
||||
} else if ([@"3G" isEqualToString:networkType]) {
|
||||
return ThinkingNetworkType3G;
|
||||
} else if ([@"4G" isEqualToString:networkType]) {
|
||||
return ThinkingNetworkType4G;
|
||||
}else if([@"5G"isEqualToString:networkType])
|
||||
{
|
||||
return ThinkingNetworkType5G;
|
||||
}
|
||||
return ThinkingNetworkTypeNONE;
|
||||
}
|
||||
|
||||
//MARK: - Private Methods
|
||||
|
||||
- (void)reachabilityChanged:(SCNetworkReachabilityFlags)flags {
|
||||
self.isWifi = (flags & kSCNetworkReachabilityFlagsReachable) && !(flags & kSCNetworkReachabilityFlagsIsWWAN);
|
||||
self.isWwan = (flags & kSCNetworkReachabilityFlagsIsWWAN);
|
||||
}
|
||||
|
||||
- (NSString *)currentRadio {
|
||||
NSString *networkType = @"NULL";
|
||||
@try {
|
||||
static CTTelephonyNetworkInfo *info = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
info = [[CTTelephonyNetworkInfo alloc] init];
|
||||
});
|
||||
NSString *currentRadio = nil;
|
||||
#ifdef __IPHONE_12_0
|
||||
if (@available(iOS 12.0, *)) {
|
||||
NSDictionary *serviceCurrentRadio = [info serviceCurrentRadioAccessTechnology];
|
||||
if ([serviceCurrentRadio isKindOfClass:[NSDictionary class]] && serviceCurrentRadio.allValues.count>0) {
|
||||
currentRadio = serviceCurrentRadio.allValues[0];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (currentRadio == nil && [info.currentRadioAccessTechnology isKindOfClass:[NSString class]]) {
|
||||
currentRadio = info.currentRadioAccessTechnology;
|
||||
}
|
||||
|
||||
if ([currentRadio isEqualToString:CTRadioAccessTechnologyLTE]) {
|
||||
networkType = @"4G";
|
||||
} else if ([currentRadio isEqualToString:CTRadioAccessTechnologyeHRPD] ||
|
||||
[currentRadio isEqualToString:CTRadioAccessTechnologyCDMAEVDORevB] ||
|
||||
[currentRadio isEqualToString:CTRadioAccessTechnologyCDMAEVDORevA] ||
|
||||
[currentRadio isEqualToString:CTRadioAccessTechnologyCDMAEVDORev0] ||
|
||||
[currentRadio isEqualToString:CTRadioAccessTechnologyCDMA1x] ||
|
||||
[currentRadio isEqualToString:CTRadioAccessTechnologyHSUPA] ||
|
||||
[currentRadio isEqualToString:CTRadioAccessTechnologyHSDPA] ||
|
||||
[currentRadio isEqualToString:CTRadioAccessTechnologyWCDMA]) {
|
||||
networkType = @"3G";
|
||||
} else if ([currentRadio isEqualToString:CTRadioAccessTechnologyEdge] ||
|
||||
[currentRadio isEqualToString:CTRadioAccessTechnologyGPRS]) {
|
||||
networkType = @"2G";
|
||||
}
|
||||
#ifdef __IPHONE_14_1
|
||||
else if (@available(iOS 14.1, *)) {
|
||||
if ([currentRadio isKindOfClass:[NSString class]]) {
|
||||
if([currentRadio isEqualToString:CTRadioAccessTechnologyNRNSA] ||
|
||||
[currentRadio isEqualToString:CTRadioAccessTechnologyNR]) {
|
||||
networkType = @"5G";
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} @catch (NSException *exception) {
|
||||
TDLogError(@"%@: %@", self, exception);
|
||||
}
|
||||
|
||||
return networkType;
|
||||
}
|
||||
|
||||
#elif TARGET_OS_OSX
|
||||
|
||||
+ (ThinkingNetworkType)convertNetworkType:(NSString *)networkType {
|
||||
return ThinkingNetworkTypeWIFI;
|
||||
}
|
||||
|
||||
- (void)startMonitoring {
|
||||
}
|
||||
|
||||
- (void)stopMonitoring {
|
||||
}
|
||||
|
||||
- (NSString *)currentRadio {
|
||||
return @"WIFI";
|
||||
}
|
||||
|
||||
- (NSString *)networkState {
|
||||
return @"WIFI";
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 166a2da05be55d24899e432265dcb5af
|
||||
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,50 @@
|
||||
|
||||
/**
|
||||
Thinks AFNetworking: https://github.com/AFNetworking/AFNetworking
|
||||
*/
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if __has_include(<ThinkingSDK/TDConstant.h>)
|
||||
#import <ThinkingSDK/TDConstant.h>
|
||||
#else
|
||||
#import "TDConstant.h"
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@interface TDSecurityPolicy: NSObject<NSCopying>
|
||||
|
||||
@property (nonatomic, assign) BOOL allowInvalidCertificates;
|
||||
@property (nonatomic, assign) BOOL validatesDomainName;
|
||||
@property (nonatomic, copy) TDURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge;
|
||||
+ (instancetype)policyWithPinningMode:(TDSSLPinningMode)pinningMode;
|
||||
+ (instancetype)defaultPolicy;
|
||||
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain;
|
||||
|
||||
@end
|
||||
|
||||
#ifndef __Require_Quiet
|
||||
#define __Require_Quiet(assertion, exceptionLabel) \
|
||||
do \
|
||||
{ \
|
||||
if ( __builtin_expect(!(assertion), 0) ) \
|
||||
{ \
|
||||
goto exceptionLabel; \
|
||||
} \
|
||||
} while ( 0 )
|
||||
#endif
|
||||
|
||||
#ifndef __Require_noErr_Quiet
|
||||
#define __Require_noErr_Quiet(errorCode, exceptionLabel) \
|
||||
do \
|
||||
{ \
|
||||
if ( __builtin_expect(0 != (errorCode), 0) ) \
|
||||
{ \
|
||||
goto exceptionLabel; \
|
||||
} \
|
||||
} while ( 0 )
|
||||
#endif
|
||||
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6067047a0ad2a043a6724e06a1e7a16
|
||||
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,248 @@
|
||||
#import "TDSecurityPolicy.h"
|
||||
|
||||
#import "TDLogging.h"
|
||||
|
||||
static id TDPublicKeyForCertificate(NSData *certificate) {
|
||||
id allowedPublicKey = nil;
|
||||
SecCertificateRef allowedCertificate;
|
||||
SecPolicyRef policy = nil;
|
||||
SecTrustRef allowedTrust = nil;
|
||||
SecTrustResultType result;
|
||||
|
||||
allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);
|
||||
__Require_Quiet(allowedCertificate != NULL, _out);
|
||||
|
||||
policy = SecPolicyCreateBasicX509();
|
||||
__Require_noErr_Quiet(SecTrustCreateWithCertificates(allowedCertificate, policy, &allowedTrust), _out);
|
||||
__Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out);
|
||||
|
||||
allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);
|
||||
|
||||
_out:
|
||||
if (allowedTrust) {
|
||||
CFRelease(allowedTrust);
|
||||
}
|
||||
|
||||
if (policy) {
|
||||
CFRelease(policy);
|
||||
}
|
||||
|
||||
if (allowedCertificate) {
|
||||
CFRelease(allowedCertificate);
|
||||
}
|
||||
|
||||
return allowedPublicKey;
|
||||
}
|
||||
|
||||
static BOOL TDServerTrustIsValid(SecTrustRef serverTrust) {
|
||||
BOOL isValid = NO;
|
||||
SecTrustResultType result;
|
||||
__Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);
|
||||
|
||||
isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
|
||||
|
||||
_out:
|
||||
return isValid;
|
||||
}
|
||||
|
||||
static NSArray * TDCertificateTrustChainForServerTrust(SecTrustRef serverTrust) {
|
||||
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
|
||||
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
|
||||
|
||||
for (CFIndex i = 0; i < certificateCount; i++) {
|
||||
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
|
||||
[trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)];
|
||||
}
|
||||
|
||||
return [NSArray arrayWithArray:trustChain];
|
||||
}
|
||||
|
||||
static NSArray * TDPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) {
|
||||
SecPolicyRef policy = SecPolicyCreateBasicX509();
|
||||
CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
|
||||
NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];
|
||||
for (CFIndex i = 0; i < certificateCount; i++) {
|
||||
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
|
||||
|
||||
SecCertificateRef someCertificates[] = {certificate};
|
||||
CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL);
|
||||
|
||||
SecTrustRef trust;
|
||||
__Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out);
|
||||
|
||||
SecTrustResultType result;
|
||||
__Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out);
|
||||
|
||||
[trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)];
|
||||
|
||||
_out:
|
||||
if (trust) {
|
||||
CFRelease(trust);
|
||||
}
|
||||
|
||||
if (certificates) {
|
||||
CFRelease(certificates);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
CFRelease(policy);
|
||||
|
||||
return [NSArray arrayWithArray:trustChain];
|
||||
}
|
||||
|
||||
static BOOL TDSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) {
|
||||
return [(__bridge id)key1 isEqual:(__bridge id)key2];
|
||||
}
|
||||
|
||||
@interface TDSecurityPolicy ()
|
||||
|
||||
@property (nonatomic, assign) TDSSLPinningMode SSLPinningMode;
|
||||
@property (nonatomic, strong, nullable) NSSet <NSData *> *pinnedCertificates;
|
||||
@property (readwrite, nonatomic, strong) NSSet *pinnedPublicKeys;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TDSecurityPolicy
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
self.validatesDomainName = YES;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (NSSet *)certificatesInBundle:(NSBundle *)bundle {
|
||||
NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];
|
||||
|
||||
NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]];
|
||||
for (NSString *path in paths) {
|
||||
NSData *certificateData = [NSData dataWithContentsOfFile:path];
|
||||
[certificates addObject:certificateData];
|
||||
}
|
||||
|
||||
return [NSSet setWithSet:certificates];
|
||||
}
|
||||
|
||||
+ (NSSet *)defaultPinnedCertificates {
|
||||
static NSSet *_defaultPinnedCertificates = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
|
||||
_defaultPinnedCertificates = [self certificatesInBundle:bundle];
|
||||
});
|
||||
|
||||
return _defaultPinnedCertificates;
|
||||
}
|
||||
|
||||
+ (instancetype)defaultPolicy {
|
||||
TDSecurityPolicy *securityPolicy = [[self alloc] init];
|
||||
securityPolicy.SSLPinningMode = TDSSLPinningModeNone;
|
||||
return securityPolicy;
|
||||
}
|
||||
|
||||
+ (instancetype)policyWithPinningMode:(TDSSLPinningMode)pinningMode {
|
||||
return [self policyWithPinningMode:pinningMode withPinnedCertificates:[self defaultPinnedCertificates]];
|
||||
}
|
||||
|
||||
+ (instancetype)policyWithPinningMode:(TDSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates {
|
||||
TDSecurityPolicy *securityPolicy = [[self alloc] init];
|
||||
securityPolicy.SSLPinningMode = pinningMode;
|
||||
[securityPolicy setPinnedCertificates:pinnedCertificates];
|
||||
return securityPolicy;
|
||||
}
|
||||
|
||||
- (void)setPinnedCertificates:(NSSet *)pinnedCertificates {
|
||||
_pinnedCertificates = pinnedCertificates;
|
||||
|
||||
if (self.pinnedCertificates) {
|
||||
NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]];
|
||||
for (NSData *certificate in self.pinnedCertificates) {
|
||||
id publicKey = TDPublicKeyForCertificate(certificate);
|
||||
if (publicKey) {
|
||||
[mutablePinnedPublicKeys addObject:publicKey];
|
||||
}
|
||||
}
|
||||
self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys];
|
||||
} else {
|
||||
self.pinnedPublicKeys = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain {
|
||||
if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == TDSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
|
||||
TDLogDebug(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSMutableArray *policies = [NSMutableArray array];
|
||||
if (self.validatesDomainName) {
|
||||
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
|
||||
} else {
|
||||
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
|
||||
}
|
||||
|
||||
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
|
||||
|
||||
if (self.SSLPinningMode == TDSSLPinningModeNone) {
|
||||
return self.allowInvalidCertificates || TDServerTrustIsValid(serverTrust);
|
||||
} else if (!TDServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
switch (self.SSLPinningMode) {
|
||||
case TDSSLPinningModeCertificate: {
|
||||
NSMutableArray *pinnedCertificates = [NSMutableArray array];
|
||||
for (NSData *certificateData in self.pinnedCertificates) {
|
||||
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
|
||||
}
|
||||
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
|
||||
|
||||
if (!TDServerTrustIsValid(serverTrust)) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSArray *serverCertificates = TDCertificateTrustChainForServerTrust(serverTrust);
|
||||
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
|
||||
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
case TDSSLPinningModePublicKey: {
|
||||
NSUInteger trustedPublicKeyCount = 0;
|
||||
NSArray *publicKeys = TDPublicKeyTrustChainForServerTrust(serverTrust);
|
||||
|
||||
for (id trustChainPublicKey in publicKeys) {
|
||||
for (id pinnedPublicKey in self.pinnedPublicKeys) {
|
||||
if (TDSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
|
||||
trustedPublicKeyCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return trustedPublicKeyCount > 0;
|
||||
}
|
||||
|
||||
default:
|
||||
return NO;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - NSCopying
|
||||
- (instancetype)copyWithZone:(NSZone *)zone {
|
||||
TDSecurityPolicy *securityPolicy = [[[self class] allocWithZone:zone] init];
|
||||
securityPolicy.SSLPinningMode = self.SSLPinningMode;
|
||||
securityPolicy.allowInvalidCertificates = self.allowInvalidCertificates;
|
||||
securityPolicy.validatesDomainName = self.validatesDomainName;
|
||||
securityPolicy.pinnedCertificates = [self.pinnedCertificates copyWithZone:zone];
|
||||
return securityPolicy;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f66440e92e611647afe05768069d1fd
|
||||
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