This commit is contained in:
runner 2023-05-02 17:01:58 +00:00
Родитель a72c241324
Коммит 9d7ccbc81a
160 изменённых файлов: 2375 добавлений и 1668 удалений

Просмотреть файл

@ -1,4 +1,4 @@
Unity Advertisements copyright © 2022 Unity Technologies.
Unity Advertisements copyright © 2023 Unity Technologies.
This software is subject to, and made available under, the terms of service for Operate Solutions (see https://unity3d.com/legal/one-operate-services-terms-of-service), and is an "Operate Service" as defined therein.
Your use of the Services constitutes your acceptance of such terms. Unless expressly provided otherwise, the software under this license is made available strictly on an "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the terms of service for details on these and other terms and conditions.

Просмотреть файл

@ -7,6 +7,5 @@ typedef NS_ENUM (NSInteger, USRVDownloadLatestWebViewStatus) {
kDownloadLatestWebViewStatusMissingLatestConfig,
kDownloadLatestWebViewStatusBackgroundDownloadStarted
};
+ (void)setServiceProviderForTesting: (id)sProvider;
+ (void)WebViewExposed_getTrrData: (USRVWebViewCallback *)callback;
+ (void)WebViewExposed_getTrrData: (nonnull USRVWebViewCallback *)callback;
@end

Просмотреть файл

@ -7,17 +7,8 @@
#import "USRVDevice.h"
#import "UADSServiceProviderContainer.h"
static UADSServiceProvider* serviceProvider;
@implementation USRVApiSdk
+ (void)load {
serviceProvider = UADSServiceProviderContainer.sharedInstance.serviceProvider;
}
+ (void)setServiceProviderForTesting: (UADSServiceProvider*)sProvider {
serviceProvider = sProvider;
}
+ (void)WebViewExposed_loadComplete: (USRVWebViewCallback *)callback {
USRVLogDebug(@"Web application loaded");
[[USRVWebViewApp getCurrentApp] setWebAppLoaded: true];
@ -114,11 +105,11 @@ static UADSServiceProvider* serviceProvider;
}
+ (void)WebViewExposed_getTrrData: (USRVWebViewCallback *)callback {
[callback invoke: serviceProvider.configurationStorage.getCurrentConfiguration.originalJSON, nil];
[callback invoke: UADSServiceProviderContainer.sharedInstance.serviceProvider.configurationStorage.getCurrentConfiguration.originalJSON, nil];
}
+ (void)WebViewExposed_getSharedSessionID:(USRVWebViewCallback *)callback {
[callback invoke: serviceProvider.sharedSessionId];
[callback invoke: UADSServiceProviderContainer.sharedInstance.serviceProvider.sharedSessionId];
}
@end

Просмотреть файл

@ -1,9 +1,11 @@
#import "UADSConfigurationLoaderWithPrivacy.h"
#import "UADSErrorState.h"
@interface UADSConfigurationLoaderWithPrivacy ()
@property (nonatomic, strong) id<UADSConfigurationLoader>original;
@property (nonatomic, strong) id<UADSPrivacyLoader>privacyLoader;
@property (nonatomic, strong) id<UADSPrivacyResponseSaver, UADSPrivacyResponseReader>responseStorage;
@property (nonatomic, strong) NSArray<NSNumber*> *fastFailCodes;
@end
@implementation UADSConfigurationLoaderWithPrivacy
@ -17,6 +19,7 @@
decorator.original = original;
decorator.privacyLoader = privacyLoader;
decorator.responseStorage = responseStorage;
decorator.fastFailCodes = @[@(kPrivacyGameIdDisabledCode)];
return decorator;
}
@ -72,7 +75,7 @@
}
- (BOOL)shouldProceedWithTheCallForError: (id<UADSError>)error {
return error.errorDomain == kPrivacyLoaderErrorDomain;
return error.errorDomain == kPrivacyLoaderErrorDomain && ![_fastFailCodes containsObject: error.errorCode];
}
@end

Просмотреть файл

@ -34,6 +34,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, strong, readonly) NSDictionary *originalJSON;
@property (nonatomic, assign) long responseCode;
@property (nonatomic, assign) BOOL allowTracking;
@property (nonatomic, assign) BOOL shouldSendNonBehavioural;
+ (instancetype)newFromDictionary: (NSDictionary *)dictionary;

Просмотреть файл

@ -40,6 +40,7 @@
response.originalJSON = dictionary;
response.allowTracking = [dictionary[@"pas"] boolValue] ? : false;
response.shouldSendNonBehavioural = [dictionary[@"snb"] boolValue] ?: false;
return response;
}

Просмотреть файл

@ -2,6 +2,8 @@
#import "UADSInitializationResponse.h"
#import "UADSGenericCompletion.h"
#import "USRVInitializationRequestFactory.h"
#import "UADSErrorState.h"
NS_ASSUME_NONNULL_BEGIN
typedef void (^UADSPrivacyCompletion)(UADSInitializationResponse *);
@ -32,6 +34,11 @@ extern NSString *const kPrivacyLoaderErrorDomain;
code: kUADSPrivacyLoaderInvalidResponseCode \
userInfo: nil] \
#define uads_privacyGameDisabledError \
[[NSError alloc] initWithDomain: kPrivacyLoaderErrorDomain \
code: kPrivacyGameIdDisabledCode \
userInfo: nil] \
@protocol UADSPrivacyLoader <NSObject>
- (void)loadPrivacyWithSuccess: (UADSPrivacyCompletion)success
andErrorCompletion: (UADSErrorCompletion)errorCompletion;

Просмотреть файл

@ -68,6 +68,10 @@ NSString * uads_privacyErrorTypeToString(UADSPrivacyLoaderError type) {
}
if (![request is2XXResponse]) {
if (request.responseCode == 423) {
errorCompletion(uads_privacyGameDisabledError);
return;
}
errorCompletion(uads_privacyInvalidResponseCodeError);
return;
}

Просмотреть файл

@ -21,6 +21,7 @@ NSString * uads_privacyResponseStateToString(UADSPrivacyResponseState);
@protocol UADSPrivacyResponseReader <NSObject>
- (UADSPrivacyResponseState)responseState;
- (BOOL)shouldSendUserNonBehavioral;
@end

Просмотреть файл

@ -34,6 +34,10 @@ NSString * uads_privacyResponseStateToString(UADSPrivacyResponseState state) {
[self.mediator notifyObserversWithObjectAndRemove: response];
}
- (BOOL)shouldSendUserNonBehavioral {
return self.response.shouldSendNonBehavioural;
}
- (UADSPrivacyResponseState)responseState {
if (!self.response) {
return kUADSPrivacyResponseUnknown;

Просмотреть файл

@ -28,6 +28,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, strong) id<UADSLogger>logger;
@property (nonatomic, strong) id<UADSRetryInfoReader> retryInfoReader;
@property (nonatomic, strong) id<UADSGameSessionIdReader> gameSessionIdReader;
@property (nonatomic, strong) id<UADSSharedSessionIdReader> sharedSessionIdReader;
@property (nonatomic) BOOL noCompression;
- (id<USRVInitializationRequestFactory>)requestFactoryWithExtendedInfo: (BOOL)hasExtendedInfo;

Просмотреть файл

@ -94,6 +94,7 @@
builder.logger = self.logger;
builder.currentTimeStampReader = self.currentTimeStampReader;
builder.gameSessionIdReader = self.gameSessionIdReader;
builder.sharedSessionIdReader = self.sharedSessionIdReader;
return builder.defaultReader;
}

Просмотреть файл

@ -15,6 +15,7 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL) isPrivacyWaitEnabled;
- (BOOL) isNativeWebViewCacheEnabled;
- (BOOL) isWebAdAssetCacheEnabled;
- (BOOL) isSwiftTokenEnabled;
- (NSDictionary<NSString *, NSString *> *)nextSessionFlags;
- (NSDictionary<NSString *, NSString *> *)currentSessionFlags;

Просмотреть файл

@ -73,6 +73,10 @@
return [self isExperimentEnabledWithKey: @"wac"];
}
- (BOOL)isSwiftTokenEnabled {
return [self isExperimentEnabledWithKey: @"s_tkn"];
}
- (NSDictionary<NSString *, NSString *> *)nextSessionFlags {
return [self flattenFlagsWith:^BOOL (id key) {
return [self isExperimentForNextSession: key];

Просмотреть файл

@ -13,3 +13,5 @@ typedef NS_ENUM (NSInteger, UADSErrorState) {
NSString * uads_errorStateString(UADSErrorState state);
BOOL uads_isWebViewErrorState(UADSErrorState state);
extern const int kPrivacyGameIdDisabledCode;

Просмотреть файл

@ -45,3 +45,5 @@ BOOL uads_isWebViewErrorState(UADSErrorState state) {
return false;
}
}
const int kPrivacyGameIdDisabledCode = 423;

Просмотреть файл

@ -33,8 +33,7 @@
@"USRVApiPermissions",
@"USRVApiMainBundle",
@"USRVApiWebAuth",
@"USRVApiTrackingManager",
@"USRVApiSKAdNetwork"
@"USRVApiTrackingManager"
];
} /* getWebAppApiClassList */

Просмотреть файл

@ -0,0 +1,13 @@
#import "UADSDeviceInfoReader.h"
#import "UADSPIIDataProvider.h"
#import "UADSPrivacyStorage.h"
#import "UADSPIITrackingStatusReader.h"
NS_ASSUME_NONNULL_BEGIN
@interface UADSDeviceInfoReaderGate : NSObject<UADSDeviceInfoReader>
+ (instancetype)decorateOriginal: (id<UADSDeviceInfoReader>)original
withPrivacyReader: (id<UADSPrivacyResponseReader>)privacyReader;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -0,0 +1,32 @@
#import "UADSDeviceInfoReaderGate.h"
#import "NSMutableDictionary+SafeRemoval.h"
#import "UADSJsonStorageKeyNames.h"
@interface UADSDeviceInfoReaderGate ()
@property (nonatomic, strong) id<UADSDeviceInfoReader>original;
@property (nonatomic, strong) id<UADSPrivacyResponseReader>privacyReader;
@end
@implementation UADSDeviceInfoReaderGate
+ (instancetype)decorateOriginal: (id<UADSDeviceInfoReader>)original
withPrivacyReader: (id<UADSPrivacyResponseReader>)privacyReader {
UADSDeviceInfoReaderGate *decorator = [self new];
decorator.original = original;
decorator.privacyReader = privacyReader;
return decorator;
}
- (nonnull NSDictionary *)getDeviceInfoForGameMode:(UADSGameMode)mode {
NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithDictionary: [_original getDeviceInfoForGameMode: mode]];
if (!_privacyReader.shouldSendUserNonBehavioral) {
[info uads_removeObjectForKeyAndReturn: UADSJsonStorageKeyNames.userNonBehavioralFlagKey];
}
return info;
}
@end

Просмотреть файл

@ -55,4 +55,8 @@ extern NSString *const kUADSDeviceInfoSKADNVersionsKey;
extern NSString *const kUADSDeviceInfoAnalyticSessionIDKey;
extern NSString *const kUADSDeviceInfoAnalyticUserIDKey;
extern NSString *const kUADSDeviceInfoSessionIdKey;
extern NSString *const kUADSDeviceInfoReaderAUIDKey;
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -50,3 +50,5 @@ NSString *const kUADSDeviceInfoAppUptimeTimestampKey = @"appUptime";
NSString *const kUADSDeviceInfoBuiltSDKVersionKey = @"builtSdkVersion";
NSString *const kUADSDeviceInfoAnalyticSessionIDKey = @"analyticsSessionId";
NSString *const kUADSDeviceInfoAnalyticUserIDKey = @"analyticsUserId";
NSString *const kUADSDeviceInfoSessionIdKey = @"sessionId";
NSString *const kUADSDeviceInfoReaderAUIDKey = @"auid";

Просмотреть файл

@ -31,9 +31,6 @@
if (_privacyReader.responseState == kUADSPrivacyResponseAllowed) {
mInfo[self.vendorIDKey] = _dataProvider.vendorID;
mInfo[self.advertisingTrackingIdKey] = _dataProvider.advertisingTrackingID;
[mInfo uads_setValueIfNotNil: @(_userContainer.userNonBehavioralFlag)
forKey: self.userNonBehavioralFlagKey];
}
return mInfo;

Просмотреть файл

@ -14,7 +14,7 @@ extern NSString * uads_privacyModeString(UADSPrivacyMode mode);
@protocol UADSPIITrackingStatusReader <NSObject>
- (UADSPrivacyMode)privacyMode;
- (BOOL) userNonBehavioralFlag;
- (NSNumber *)userNonBehavioralFlag;
@end
@interface UADSPIITrackingStatusReaderBase : NSObject<UADSPIITrackingStatusReader>

Просмотреть файл

@ -93,14 +93,14 @@ NSString * uads_privacyModeString(UADSPrivacyMode mode) {
return uads_privacyModeFromString(privacyMode);
}
- (BOOL)userNonBehavioralFlag {
- (NSNumber *)userNonBehavioralFlag {
id flag = [self.storageReader getValueForKey: [UADSJsonStorageKeyNames userNonBehavioralValueFlagKey]];
if (flag == nil) {
flag = [self.storageReader getValueForKey: [UADSJsonStorageKeyNames userNonbehavioralValueFlagKey]];
}
return [flag boolValue];
return flag ? @([flag boolValue]) : nil;
}
@end

Просмотреть файл

@ -0,0 +1,12 @@
#import <Foundation/Foundation.h>
#import "UADSDeviceInfoReader.h"
#import "UADSSharedSessionIdReader.h"
NS_ASSUME_NONNULL_BEGIN
@interface UADSDeviceInfoReaderWithSessionId : NSObject<UADSDeviceInfoReader>
+ (id<UADSDeviceInfoReader>)newWithOriginal: (id<UADSDeviceInfoReader>)original
andSessionIdReader: (id<UADSSharedSessionIdReader>)sessionIdReader;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -0,0 +1,28 @@
#import "UADSDeviceInfoReaderWithSessionId.h"
#import "UADSDeviceInfoReaderKeys.h"
@interface UADSDeviceInfoReaderWithSessionId()
@property (nonatomic, strong) id<UADSDeviceInfoReader> original;
@property (nonatomic, strong) id<UADSSharedSessionIdReader> sessionIdReader;
@end
@implementation UADSDeviceInfoReaderWithSessionId
+ (id<UADSDeviceInfoReader>)newWithOriginal: (id<UADSDeviceInfoReader>)original
andSessionIdReader: (id<UADSSharedSessionIdReader>)sessionIdReader {
UADSDeviceInfoReaderWithSessionId *reader = [UADSDeviceInfoReaderWithSessionId new];
reader.original = original;
reader.sessionIdReader = sessionIdReader;
return reader;
}
- (NSDictionary *)getDeviceInfoForGameMode: (UADSGameMode)mode {
NSDictionary *info = [_original getDeviceInfoForGameMode: mode];
NSMutableDictionary *mInfo = [[NSMutableDictionary alloc] initWithDictionary: info];
mInfo[kUADSDeviceInfoSessionIdKey] = _sessionIdReader.sessionId;
return mInfo;
}
@end

Просмотреть файл

@ -14,6 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
- (NSString *)sessionID;
- (NSString *)userID;
- (NSString *)auID;
@end

Просмотреть файл

@ -10,7 +10,7 @@
- (nonnull NSString *)idfi {
NSString *currentValue = [USRVPreferences getString: kUADSStorageIDFIKey];
NSString *currentValue = [USRVPreferences getString: kUADSStorageIDFIKey] ?: [self auID];
if (currentValue == nil || currentValue.length == 0) {
currentValue = [[USRVDevice getUniqueEventId] lowercaseString];
@ -25,6 +25,10 @@
return [USRVPreferences getString: kUADSStorageAnalyticSessionKey];
}
- (NSString *)auID {
return [USRVPreferences getString: kUADSStorageAUIDKey];
}
- (NSString *)userID {
return [USRVPreferences getString: kUADSStorageAnalyticUserKey];
}

Просмотреть файл

@ -270,6 +270,12 @@
[info uads_setValueIfNotNil: self.userDefaultsReader.sessionID
forKey: kUADSDeviceInfoAnalyticSessionIDKey];
}];
[self measurePerformanceAndLog: @"AUID"
using: ^{
[info uads_setValueIfNotNil: self.userDefaultsReader.auID
forKey: kUADSDeviceInfoReaderAUIDKey];
}];
[self measurePerformanceAndLog: @"userID"
using: ^{

Просмотреть файл

@ -6,6 +6,8 @@
#import "UADSPrivacyStorage.h"
#import "UADSLogger.h"
#import "UADSGameSessionIdReader.h"
#import "UADSSharedSessionIdReader.h"
NS_ASSUME_NONNULL_BEGIN
@interface UADSDeviceInfoReaderBuilder : NSObject <UADSDeviceInfoProvider>
@ -16,6 +18,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, strong) id<UADSCurrentTimestamp>currentTimeStampReader;
@property (nonatomic, strong) id<UADSClientConfig> clientConfig;
@property (nonatomic, strong) id<UADSGameSessionIdReader>gameSessionIdReader;
@property (nonatomic, strong) id<UADSSharedSessionIdReader> sharedSessionIdReader;
@property BOOL extendedReader;
- (id<UADSDeviceInfoReader>)defaultReader;

Просмотреть файл

@ -11,6 +11,8 @@
#import "UADSDeviceInfoStorageKeysProviderExtended.h"
#import "UADSDeviceInfoStorageKeysProviderMinimal.h"
#import "UADSDeviceInfoReaderWithPrivacy.h"
#import "UADSDeviceInfoReaderGate.h"
#import "UADSDeviceInfoReaderWithSessionId.h"
@implementation UADSDeviceInfoReaderBuilder
- (id<UADSDeviceInfoReader>)defaultReader {
@ -28,14 +30,19 @@
deviceInfoReader = [self addStorageDumpDecorator: deviceInfoReader];
if (extendedReader) {
deviceInfoReader = [self addGate: deviceInfoReader];
deviceInfoReader = [self addPIIDecorator: deviceInfoReader];
deviceInfoReader = [self addSessionIdDecorator: deviceInfoReader];
deviceInfoReader = [self addMetrics: deviceInfoReader
usingMetricsSender: self.metricsSender
currentTimestamp: self.currentTimeStampReader];
}
deviceInfoReader = [self addFilter: deviceInfoReader];
return deviceInfoReader;
}
@ -69,6 +76,11 @@
andBlockList: self.defaultBlockList];
}
- (id<UADSDeviceInfoReader>)addGate: (id<UADSDeviceInfoReader>)original {
return [UADSDeviceInfoReaderGate decorateOriginal: original
withPrivacyReader: self.privacyReader];
}
- (id<UADSDictionaryKeysBlockList>)defaultBlockList {
if (_storageBlockListProvider) {
return _storageBlockListProvider;
@ -84,6 +96,11 @@
andUserContainer: self.userStorageReader];
}
- (id<UADSDeviceInfoReader>)addSessionIdDecorator: (id<UADSDeviceInfoReader>)original {
return [UADSDeviceInfoReaderWithSessionId newWithOriginal: original
andSessionIdReader: self.sharedSessionIdReader];
}
- (id<UADSDeviceInfoReader>)addMetrics: (id<UADSDeviceInfoReader>)original
usingMetricsSender: (id<ISDKMetrics>)metricsSender
currentTimestamp: (id<UADSCurrentTimestamp>)timestampReader {

Просмотреть файл

@ -18,6 +18,7 @@ extern NSString *const kUADSUserContainerName;
extern NSString *const kUADSStorageIDFIKey;
extern NSString *const kUADSStorageAnalyticSessionKey;
extern NSString *const kUADSStorageAnalyticUserKey;
extern NSString *const kUADSStorageAUIDKey;
@interface UADSJsonStorageKeyNames : NSObject
+ (NSString *)webViewContainerKey;

Просмотреть файл

@ -26,7 +26,7 @@ NSString *const kPrivacySPMKey = @"spm";
NSString *const kUADSStorageAnalyticSessionKey = @"unity.player_sessionid";
NSString *const kUADSStorageAnalyticUserKey = @"unity.cloud_userid";
NSString *const kUADSStorageAUIDKey = @"auid";
NSString *const kUADSSdkServiceModeKey = @"sdk.mode.value";

Просмотреть файл

@ -36,7 +36,7 @@
[info uads_setValueIfNotNil: self.userDefaultsReader.idfi
forKey: kUADSDeviceInfoIDFIKey];
[info uads_setValueIfNotNil: @(_userContainerReader.userNonBehavioralFlag)
[info uads_setValueIfNotNil: _userContainerReader.userNonBehavioralFlag
forKey: UADSJsonStorageKeyNames.userNonBehavioralFlagKey];

Просмотреть файл

@ -33,7 +33,7 @@
}
+ (instancetype)newScarSendTimeFailure: (NSNumber *)value tags: (NSDictionary<NSString *, NSString *> *)tags isAsync:(BOOL)isAsync {
return [self newWithName: isAsync ? @"native_hb_signals_async_upload_failure" : @"native_hb_signals_sync_upload_success"
return [self newWithName: isAsync ? @"native_hb_signals_async_upload_failure" : @"native_hb_signals_sync_upload_failure"
value: value
tags: tags];
}

Просмотреть файл

@ -1,6 +1,7 @@
#import "UADSSCARRawSignalsReader.h"
#import "UADSSCARSignalIdentifiers.h"
#import "UADSSCARHeaderBiddingMetric.h"
#import "NSMutableDictionary+SafeOperations.h"
@implementation UADSSCARRawSignalsReader
@ -16,9 +17,8 @@
};
id error = ^(id<UADSError> _Nonnull error) {
NSMutableDictionary *tags = [NSMutableDictionary dictionaryWithDictionary: @{
@"reason": error.errorCode
}];
NSMutableDictionary *tags = [NSMutableDictionary new];
[tags uads_setValueIfNotNil:error.errorCode forKey:@"reason"];
[self.config.metricsSender sendMetric: [UADSSCARHeaderBiddingMetric newScarFetchTimeFailure:[self durationFromStartTime:startTime] tags:tags isAsync:isAsync]];
completion(nil);
};

Просмотреть файл

@ -63,8 +63,14 @@
request.body = jsonString;
dispatch_async(queue, ^{
NSData* data = [request makeRequest];
[self.config.metricsSender sendMetric: [UADSSCARHeaderBiddingMetric newScarSendTimeSuccess:[self durationFromStartTime:startTime] isAsync:isAsync]];
[request makeRequest];
if (request.error) {
NSMutableDictionary *tags = [NSMutableDictionary new];
[tags uads_setValueIfNotNil:[NSString stringWithFormat:@"%li",(long)request.error.code] forKey:@"reason"];
[self.config.metricsSender sendMetric: [UADSSCARHeaderBiddingMetric newScarSendTimeFailure:[self durationFromStartTime:startTime] tags:tags isAsync:isAsync]];
} else {
[self.config.metricsSender sendMetric: [UADSSCARHeaderBiddingMetric newScarSendTimeSuccess:[self durationFromStartTime:startTime] isAsync:isAsync]];
}
});
}

Просмотреть файл

@ -17,6 +17,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (instancetype)newWebToken: (NSString *)value;
+ (instancetype)newInvalidToken;
+ (instancetype)newInvalidNativeToken;
+ (instancetype)newWithDictionary: (NSDictionary *)dictionary;
- (BOOL) isValid;

Просмотреть файл

@ -46,6 +46,18 @@
return token;
}
+ (instancetype)newWithDictionary: (NSDictionary *)dictionary {
UADSHeaderBiddingToken *token = [self new];
token.value = dictionary[@"value"];
NSNumber *type = dictionary[@"type"];
token.type = type.intValue;
token.uuidString = dictionary[@"uuidString"];
NSMutableDictionary *info = [NSMutableDictionary dictionaryWithDictionary:dictionary[@"info"]];
token.info = info;
token.customPrefix = dictionary[@"customPrefix"];
return token;
}
- (BOOL)isValid {
return _value != nil && ![_value isEqual: @""];
}

Просмотреть файл

@ -5,6 +5,7 @@
#import "UADSPrivacyStorage.h"
#import "UADSGameSessionIdReader.h"
#import "UADSGMAScar.h"
#import "UADSSharedSessionIdReader.h"
NS_ASSUME_NONNULL_BEGIN
@ -22,6 +23,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, strong) id<IUSRVWebRequestFactory> requestFactory;
@property (nonatomic, weak) UADSGMAScar* scar;
@property (nonatomic) id<UADSUniqueIdGenerator> uniqueIdGenerator;
@property (nonatomic, strong) id<UADSSharedSessionIdReader> sharedSessionIdReader;
- (id<UADSHeaderBiddingAsyncTokenReader, UADSHeaderBiddingTokenCRUD>)defaultReader;

Просмотреть файл

@ -11,6 +11,7 @@
#import "UADSHBTokenReaderWithPrivacyWait.h"
#import "UADSHeaderBiddingTokenReaderWithSCARSignals.h"
#import "UADSUUIDStringGenerator.h"
#import "UADSHeaderBiddingTokenReaderSwiftBridge.h"
static NSString *const kDefaultTokenPrefix = @"1:";
@ -71,6 +72,7 @@ static NSString *const kDefaultTokenPrefix = @"1:";
builder.extendedReader = true;
builder.currentTimeStampReader = [UADSCurrentTimestampBase new];
builder.gameSessionIdReader = self.gameSessionIdReader;
builder.sharedSessionIdReader = self.sharedSessionIdReader;
_deviceInfoReader = builder.defaultReader;
return _deviceInfoReader;
}
@ -91,11 +93,15 @@ static NSString *const kDefaultTokenPrefix = @"1:";
- (id<UADSHeaderBiddingAsyncTokenReader>)tokenGenerator {
if (!_tokenGenerator) {
_tokenGenerator = [UADSHeaderBiddingTokenReaderBase newWithDeviceInfoReader: self.deviceInfoReader
if (self.experiments.isSwiftTokenEnabled) {
_tokenGenerator = [UADSHeaderBiddingTokenReaderSwiftBridge new];
} else {
_tokenGenerator = [UADSHeaderBiddingTokenReaderBase newWithDeviceInfoReader: self.deviceInfoReader
andCompressor: self.bodyCompressor
withTokenPrefix: self.nativeTokenPrefix
withUniqueIdGenerator: self.uniqueIdGenerator ?: [UADSUUIDStringGenerator new]
withConfigurationReader: self.sdkConfigReader];
}
}
if (self.experiments.isPrivacyWaitEnabled) {

Просмотреть файл

@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
#import "UADSHeaderBiddingTokenReaderBase.h"
#import "UADSServiceProviderContainer.h"
NS_ASSUME_NONNULL_BEGIN
@interface UADSHeaderBiddingTokenReaderSwiftBridge : NSObject <UADSHeaderBiddingAsyncTokenReader>
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -0,0 +1,13 @@
#import "UADSHeaderBiddingTokenReaderSwiftBridge.h"
@implementation UADSHeaderBiddingTokenReaderSwiftBridge
- (void)getToken:(nonnull UADSHeaderBiddingTokenCompletion)completion {
[[UADSServiceProviderContainer.sharedInstance.serviceProvider objBridge] getToken:^(NSDictionary * _Nonnull tokenDict) {
completion([UADSHeaderBiddingToken newWithDictionary:tokenDict]);
}];
}
@end

Просмотреть файл

@ -1,32 +1,14 @@
#import "UADSGameSessionIdReader.h"
#import "USRVStorageManager.h"
#import "USRVDevice.h"
#import "UADSJsonStorageKeyNames.h"
#import "UADSServiceProviderContainer.h"
@interface UADSGameSessionIdReaderBase ()
@property (nonatomic, strong) NSNumber *gameSessionId;
@end
@implementation UADSGameSessionIdReaderBase
- (nonnull NSNumber *)gameSessionId {
@synchronized (self) {
[self generateSessionIdIfNeeded];
}
return _gameSessionId;
}
- (void)generateSessionIdIfNeeded {
if (_gameSessionId != nil) {
return;
}
NSString *uuidString = [[[USRVDevice getUniqueEventId] stringByReplacingOccurrencesOfString:@"-" withString:@""] substringToIndex:12];
long long hex = strtoull([uuidString UTF8String], NULL, 16);
_gameSessionId = [NSNumber numberWithLongLong:hex];
[[USRVStorageManager getStorage: kUnityServicesStorageTypePrivate] set: UADSJsonStorageKeyNames.webViewDataGameSessionIdKey
value: _gameSessionId];
UADSServiceProvider *provider = UADSServiceProviderContainer.sharedInstance.serviceProvider;
return [NSNumber numberWithLongLong: [[provider.objBridge gameSessionId] longLongValue]];
}
@end

Просмотреть файл

@ -1,10 +1,11 @@
#import "UADSSharedSessionIdReader.h"
#import "UADSSessionId.h"
#import "UADSServiceProviderContainer.h"
@implementation UADSSharedSessionIdReaderBase
- (nonnull NSString *)sessionId {
return UADSSessionId.shared.sessionId;
UADSServiceProvider *provider = UADSServiceProviderContainer.sharedInstance.serviceProvider;
return [provider.objBridge sessionId];
}
@end

Просмотреть файл

@ -44,7 +44,7 @@ static void soundCompletionCallback(SystemSoundID soundId, void *myself) {
}
- (bool)writeToMuteDetectionPath: (NSString *)filePath {
NSLog(@"MuteSwitch: Attempting to write bytes to file");
USRVLogDebug(@"MuteSwitch: Attempting to write bytes to file");
@try {
NSData *muteFileData = [[NSData alloc] initWithBytesNoCopy: MuteSwitchDetection_aiff
@ -53,12 +53,12 @@ static void soundCompletionCallback(SystemSoundID soundId, void *myself) {
[muteFileData writeToFile: filePath
atomically: YES];
} @catch (NSException *exception) {
NSLog(@"MuteSwitch: File creation failed.");
USRVLogDebug(@"MuteSwitch: File creation failed.");
self.fileCreated = NO;
return NO;
}
NSLog(@"MuteSwitch: File creation successful");
USRVLogDebug(@"MuteSwitch: File creation successful");
self.fileCreated = YES;
return YES;
}
@ -76,7 +76,7 @@ static void soundCompletionCallback(SystemSoundID soundId, void *myself) {
}
- (void)sendMuteState: (bool)muteState {
NSLog(@"MuteSwitch: Device mute state detected to be %@", muteState ? @"true" : @"false");
USRVLogDebug(@"MuteSwitch: Device mute state detected to be %@", muteState ? @"true" : @"false");
if ([USRVWebViewApp getCurrentApp]) {
[[USRVWebViewApp getCurrentApp] sendEvent: @"MUTE_STATE_RECEIVED"

Просмотреть файл

@ -4,7 +4,7 @@
+ (instancetype) sharedInstance;
+ (USRVStorage *)getStorage: (UnityServicesStorageType)storageType;
+ (void)removeStorage: (UnityServicesStorageType)storageType;
- (void)commit: (NSDictionary *)storageContents;
@end

Просмотреть файл

@ -16,10 +16,6 @@
return [[USRVStorageManager sharedInstance] getStorage: storageType];
}
+ (void)removeStorage: (UnityServicesStorageType)storageType {
[[USRVStorageManager sharedInstance] removeStorage: storageType];
}
_uads_custom_singleton_imp(USRVStorageManager, ^{
return [self new];
})
@ -97,13 +93,6 @@ _uads_custom_singleton_imp(USRVStorageManager, ^{
}
} /* initStorage */
- (void)removeStorage: (UnityServicesStorageType)storageType {
dispatch_sync(_syncQueue, ^{
[_storageLocations removeObjectForKey: [NSNumber numberWithInteger: storageType]];
[_storages removeObjectForKey: [NSNumber numberWithInteger: storageType]];
});
}
- (USRVStorage *)getStorage: (UnityServicesStorageType)storageType {
__block USRVStorage *result = NULL;

Просмотреть файл

@ -63,6 +63,14 @@
return nextState;
}
} else if (configError && self.retries < [self.configuration maxRetries]) {
if (configError.code == kPrivacyGameIdDisabledCode) {
id nextState = [[USRVInitializeStateError alloc] initWithConfiguration: self.configuration
erroredState: self
code: kUADSErrorStateNetworkConfigRequest
message: @"GameId disabled"];
return nextState;
}
self.retryDelay = self.retryDelay * [self.configuration retryScalingFactor];
self.retries++;
[[UADSInitializeEventsMetricSender sharedInstance] didRetryConfig];
@ -114,26 +122,34 @@
andCompletion: (void (^)(void))completion
error:(void (^)(NSError * _Nonnull))error {
if (configError && self.retries < [self.configuration maxRetries]) {
self.retryDelay = self.retryDelay * [self.configuration retryScalingFactor];
self.retries++;
[[UADSInitializeEventsMetricSender sharedInstance] didRetryConfig];
USRVInitializeStateConfig *retryState = [[USRVInitializeStateConfig alloc] initWithConfiguration: self.localConfig];
retryState.configuration = self.localConfig;
retryState.localConfig = self.localConfig;
[retryState setRetries: self.retries];
[retryState setRetryDelay: self.retryDelay];
retryState.configLoader = self.configLoader;
if (configError.code == kPrivacyGameIdDisabledCode) {
id nextState = [[USRVInitializeStateError alloc] initWithConfiguration: self.configuration
erroredState: self
code: kUADSErrorStateNetworkConfigRequest
message: @"GameId disabled"];
[nextState startWithCompletion:completion error:error];
} else {
self.retryDelay = self.retryDelay * [self.configuration retryScalingFactor];
self.retries++;
[[UADSInitializeEventsMetricSender sharedInstance] didRetryConfig];
USRVInitializeStateConfig *retryState = [[USRVInitializeStateConfig alloc] initWithConfiguration: self.localConfig];
retryState.configuration = self.localConfig;
retryState.localConfig = self.localConfig;
[retryState setRetries: self.retries];
[retryState setRetryDelay: self.retryDelay];
retryState.configLoader = self.configLoader;
id nextState = [[USRVInitializeStateRetry alloc] initWithConfiguration: self.localConfig
retryState: retryState
retryDelay: self.retryDelay];
[nextState startWithCompletion:^{
self.retries = retryState.retries;
completion();
} error:^(NSError * _Nonnull er) {
self.retries = retryState.retries;
error(er);
}];
id nextState = [[USRVInitializeStateRetry alloc] initWithConfiguration: self.localConfig
retryState: retryState
retryDelay: self.retryDelay];
[nextState startWithCompletion:^{
self.retries = retryState.retries;
completion();
} error:^(NSError * _Nonnull er) {
self.retries = retryState.retries;
error(er);
}];
}
} else {
USRVInitializeStateConfig *erroredState = [[USRVInitializeStateConfig alloc] initWithConfiguration: self.localConfig
retries: self.retries

Просмотреть файл

@ -11,12 +11,12 @@ NSString *const kUnityServicesLocalCacheFilePrefix = @"UnityAdsCache-";
NSString *const kUnityServicesLocalStorageFilePrefix = @"UnityAdsStorage-";
NSString *const kUnityServicesWebviewBranchInfoDictionaryKey = @"UADSWebviewBranch";
NSString *const kUnityServicesWebviewConfigInfoDictionaryKey = @"UADSWebviewConfig";
NSString *const kUnityServicesVersionName = @"4.6.1";
NSString *const kUnityServicesVersionName = @"4.7.0";
NSString *const kUnityServicesFlavorDebug = @"debug";
NSString *const kUnityServicesFlavorRelease = @"release";
NSString *const kChinaIsoAlpha2Code = @"CN";
NSString *const kChinaIsoAlpha3Code = @"CHN";
int const kUnityServicesVersionCode = 4610;
int const kUnityServicesVersionCode = 4700;
@implementation USRVSdkProperties

Просмотреть файл

@ -48,7 +48,7 @@
UADSMetric *other = (UADSMetric *)object;
return [self.name isEqual: other.name] &&
((!self.value && !other.value) || [self.value isEqualToNumber: other.value]) &&
// ((!self.value && !other.value) || [self.value isEqualToNumber: other.value]) &&
((!self.tags && !other.tags) || [self.tags isEqualToDictionary: other.tags]);
}

Просмотреть файл

@ -1,2 +0,0 @@
@interface USRVApiSKAdNetwork : NSObject
@end

Просмотреть файл

@ -1,24 +0,0 @@
#import "USRVApiSKAdNetwork.h"
#import "USRVWebViewCallback.h"
#import "USRVSKAdNetworkProxy.h"
@implementation USRVApiSKAdNetwork
+ (void)WebViewExposed_available: (USRVWebViewCallback *)callback {
BOOL available = [[USRVSKAdNetworkProxy sharedInstance] available];
[callback invoke: [NSNumber numberWithBool: available], nil];
}
+ (void)WebViewExposed_updateConversionValue: (NSInteger)conversionValue callback: (USRVWebViewCallback *)callback {
[[USRVSKAdNetworkProxy sharedInstance] updateConversionValue: conversionValue];
[callback invoke: nil];
}
+ (void)WebViewExposed_registerAppForAdNetworkAttribution: (USRVWebViewCallback *)callback {
[[USRVSKAdNetworkProxy sharedInstance] registerAppForAdNetworkAttribution];
[callback invoke: nil];
}
@end

Просмотреть файл

@ -1,11 +0,0 @@
#import <Foundation/Foundation.h>
#import "USRVWebViewApp.h"
@interface USRVSKAdNetworkProxy : NSObject
+ (USRVSKAdNetworkProxy *)sharedInstance;
- (BOOL) available;
- (void) updateConversionValue: (NSInteger)conversionValue;
- (void) registerAppForAdNetworkAttribution;
@end

Просмотреть файл

@ -1,105 +0,0 @@
#import "USRVSKAdNetworkProxy.h"
#import <dlfcn.h>
#import <objc/runtime.h>
#import "USRVDevice.h"
@interface USRVSKAdNetworkProxy ()
@property (strong, nonatomic) Class skAdNetworkClass;
@end
@implementation USRVSKAdNetworkProxy
- (instancetype)init {
if (self = [super init]) {
if (![USRVSKAdNetworkProxy loadFramework]) {
USRVLogDebug(@"Can't load StoreKit Dll");
}
self.skAdNetworkClass = NSClassFromString(@"SKAdNetwork");
}
return self;
}
- (BOOL)available {
return self.skAdNetworkClass != nil;
}
- (void)updateConversionValue: (NSInteger)conversionValue {
if (!self.available) {
return;
}
SEL requestSelector = NSSelectorFromString(@"updateConversionValue:");
if ([self.skAdNetworkClass respondsToSelector: requestSelector]) {
NSNumber *val = [NSNumber numberWithInteger: conversionValue];
[self.skAdNetworkClass performSelector: requestSelector
withObject: val];
}
}
- (void)registerAppForAdNetworkAttribution {
if (!self.available) {
return;
}
SEL requestSelector = NSSelectorFromString(@"registerAppForAdNetworkAttribution:");
if ([self.skAdNetworkClass respondsToSelector: requestSelector]) {
[self.skAdNetworkClass performSelector: requestSelector];
}
}
+ (USRVSKAdNetworkProxy *)sharedInstance {
static USRVSKAdNetworkProxy *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[USRVSKAdNetworkProxy alloc] init];
});
return instance;
}
+ (BOOL)isFrameworkPresent {
id attClass = objc_getClass("SKAdNetwork");
if (attClass) {
return YES;
}
return NO;
}
+ (BOOL)loadFramework {
NSString *frameworkLocation;
if (![USRVSKAdNetworkProxy isFrameworkPresent]) {
USRVLogDebug(@"StoreKit.framework is not present, trying to load it.");
if ([USRVDevice isSimulator]) {
NSString *frameworkPath = [[NSProcessInfo processInfo] environment][@"DYLD_FALLBACK_FRAMEWORK_PATH"];
if (frameworkPath) {
frameworkLocation = [NSString pathWithComponents: @[frameworkPath, @"StoreKit.framework", @"SkAdNetwork"]];
}
} else {
frameworkLocation = [NSString stringWithFormat: @"/System/Library/Frameworks/StoreKit.framework/SkAdNetwork"];
}
dlopen([frameworkLocation cStringUsingEncoding: NSUTF8StringEncoding], RTLD_LAZY);
if (![USRVSKAdNetworkProxy isFrameworkPresent]) {
USRVLogError(@"StoreKit.framework still not present!");
return NO;
} else {
USRVLogDebug(@"Succesfully loaded StoreKit.framework");
return YES;
}
} else {
USRVLogDebug(@"StoreKit.framework already present");
return YES;
}
} /* loadFramework */
@end

Просмотреть файл

@ -11,6 +11,7 @@
#import "UADSWebRequestFactorySwiftAdapter.h"
#import "UADSDeviceInfoReaderBuilder.h"
#import "UADSSharedSessionIdReader.h"
#import "USRVJsonStorageAggregator.h"
@interface UADSServiceProvider ()
@property (nonatomic, strong) id<UADSPerformanceLogger>performanceLogger;
@ -63,6 +64,21 @@
[_privacyStorage saveResponse: [UADSInitializationResponse newFromDictionary:privacy]];
}
- (id)getValueFromJSONStorageFor: (NSString *)key {
return [USRVJsonStorageAggregator.defaultAggregator getValueForKey: key];
}
- (id)storageContent {
return [USRVJsonStorageAggregator.defaultAggregator getContents];
}
- (void)deleteKeyFromJSONStorageFor: (NSString *)key {
[[USRVStorageManager getStorage: kUnityServicesStorageTypePrivate] deleteKey:key];
}
-(void)setValueToJSONStorage:(id)value for: (NSString *)key {
[[USRVStorageManager getStorage: kUnityServicesStorageTypePrivate] set:key value: value];
}
- (id<UADSHeaderBiddingAsyncTokenReader, UADSHeaderBiddingTokenCRUD>)hbTokenReader {
@synchronized (self) {
if (!_hbTokenReader) {
@ -102,6 +118,7 @@
tokenReaderBuilder.gameSessionIdReader = self.gameSessionIdReader;
tokenReaderBuilder.scar = [UADSGMAScar sharedInstance];
tokenReaderBuilder.requestFactory = self.webViewRequestFactory;
tokenReaderBuilder.sharedSessionIdReader = self.sharedSessionIdReader;
return tokenReaderBuilder;
}
@ -162,25 +179,27 @@
builder.logger = self.logger;
builder.currentTimeStampReader = [UADSCurrentTimestampBase new];
builder.gameSessionIdReader = self.gameSessionIdReader;
builder.sharedSessionIdReader = self.sharedSessionIdReader;
return builder;
}
- (id<UADSConfigurationLoader>)configurationLoader {
UADSCClientConfigBase *config = [UADSCClientConfigBase defaultWithExperiments: self.experiments];
UADSCClientConfigBase *config = [UADSCClientConfigBase defaultWithExperiments: self.experiments];
UADSConfigurationLoaderBuilder *builder = [UADSConfigurationLoaderBuilder newWithConfig: config
andWebRequestFactory: self.webViewRequestFactory
metricSender: self.metricSender];
builder.privacyStorage = self.privacyStorage;
builder.logger = self.logger;
builder.configurationSaver = self.configurationSaver;
builder.currentTimeStampReader = [UADSCurrentTimestampBase new];
builder.retryInfoReader = self.retryReader;
builder.gameSessionIdReader = self.gameSessionIdReader;
builder.metricsSender = self.metricSender;
return builder.configurationLoader;
UADSConfigurationLoaderBuilder *builder = [UADSConfigurationLoaderBuilder newWithConfig: config
andWebRequestFactory: self.webViewRequestFactory
metricSender: self.metricSender];
builder.privacyStorage = self.privacyStorage;
builder.logger = self.logger;
builder.configurationSaver = self.configurationSaver;
builder.currentTimeStampReader = [UADSCurrentTimestampBase new];
builder.retryInfoReader = self.retryReader;
builder.gameSessionIdReader = self.gameSessionIdReader;
builder.metricsSender = self.metricSender;
builder.sharedSessionIdReader = self.sharedSessionIdReader;
return builder.configurationLoader;
}
- (UADSConfigurationExperiments *)experiments {

Просмотреть файл

@ -1,4 +1,5 @@
#import "UADSSDKInitializerProxy.h"
#import "NSPrimitivesBox.h"
static NSString *const kInitializeMethodName = @"initializeWithGameID:testMode:completion:error:";
@ -13,8 +14,9 @@ static NSString *const kInitializeMethodName = @"initializeWithGameID:testMode:c
completion: (UADSVoidClosure)completion
error: (UADSNSErrorCompletion)error {
NSPrimitivesBox *box = [NSPrimitivesBox newWithBytes: &testMode objCType: @encode(BOOL)];
[self callInstanceMethod: kInitializeMethodName
args:@[gameId, @(testMode), completion, error]];
args:@[gameId, box, completion, error]];
}

Просмотреть файл

@ -7,6 +7,7 @@
NS_ASSUME_NONNULL_BEGIN
typedef NSDictionary * _Nonnull (^UADSInfoGetter)(bool);
typedef void (^UADSTokenCompletion)(NSDictionary *_Nonnull);
@protocol UADSNativeNetworkLayerProvider <NSObject>
- (UADSCommonNetworkProxy *)nativeNetworkLayer;
@ -20,6 +21,9 @@ typedef NSDictionary * _Nonnull (^UADSInfoGetter)(bool);
- (UADSSDKInitializerProxy *)sdkInitializerWithFactory: (USRVInitializeStateFactory *)factory;
- (void)setDebugMode: (BOOL)isDebugMode;
- (InitializationState)currentState;
- (NSNumber *)gameSessionId;
- (NSString *)sessionId;
- (void)getToken:(UADSTokenCompletion)completion;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -11,7 +11,9 @@ static NSString *const SAVE_CONFIGURATION_SELECTOR = @"saveSDKConfigFrom:";
static NSString *const GET_CONFIGURATION_SELECTOR = @"configDictionary";
static NSString *const SET_DEBUG_MODE_SELECTOR = @"setDebugMode:";
static NSString *const CURRENT_STATE_KVO = @"currentState";
static NSString *const GAME_SESSION_ID_KVO = @"gameSessionId";
static NSString *const SESSION_ID_KVO = @"sessionId";
static NSString *const GET_TOKEN_SELECTOR = @"getToken:";
@implementation UADSServiceProviderProxy
@ -60,5 +62,18 @@ static NSString *const CURRENT_STATE_KVO = @"currentState";
return state.intValue;
}
- (NSNumber *)gameSessionId {
NSNumber *gameSessionId = [self callInstanceMethodWithReturn:GAME_SESSION_ID_KVO args:@[]];
return gameSessionId;
}
- (NSString *)sessionId {
return [self callInstanceMethodWithReturn:SESSION_ID_KVO args:@[]];
}
- (void)getToken:(UADSTokenCompletion)completion {
[self callInstanceMethod:GET_TOKEN_SELECTOR args:@[completion]];
}
@end

Просмотреть файл

@ -1,13 +0,0 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@protocol UADSIdStore
- (NSString *_Nullable)getValue;
- (void)commitValue:(NSString *)value;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -1,93 +0,0 @@
#import "UADSInstallationId.h"
#import "UADSIdStore.h"
#import "UADSUnityPlayerPrefsStore.h"
#import "UADSUserDefaultsStore.h"
@interface UADSInstallationId ()
@property(nonatomic, strong) NSString *_Nullable savedInstallationId;
@property(nonatomic, strong) id<UADSIdStore> installationIdStore;
@property(nonatomic, strong) id<UADSIdStore> analyticsIdStore;
@property(nonatomic, strong) id<UADSIdStore> unityAdsIdStore;
@end
@implementation UADSInstallationId
+ (instancetype)shared {
static UADSInstallationId *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[UADSInstallationId alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
// https://github.cds.internal.unity3d.com/unity/operate-services-sample/blob/c4fad12071bb3a74aa9631c7c4cce12112fd0156/ZippedPackages/com.unity.services.core-1.1.0-pre.8/Runtime/Device/InstallationId.cs
UADSUnityPlayerPrefsStore *installationIdStore =
[[UADSUnityPlayerPrefsStore alloc] initWithKey:@"UnityInstallationId"];
UADSUnityPlayerPrefsStore *analyticsIdStore =
[[UADSUnityPlayerPrefsStore alloc] initWithKey:@"unity.cloud_userid"];
UADSUserDefaultsStore *unityAdsIdStore =
[[UADSUserDefaultsStore alloc] initWithKey:@"unityads-idfi"];
self = [self initWithInstallationIdStore:installationIdStore
analyticsIdStore:analyticsIdStore
unityAdsIdStore:unityAdsIdStore];
return self;
}
- (instancetype)initWithInstallationIdStore:(id<UADSIdStore>)installationIdStore
analyticsIdStore:(id<UADSIdStore>)analyticsIdStore
unityAdsIdStore:(id<UADSIdStore>)unityAdsIdStore {
self = [super init];
if (self) {
_installationIdStore = installationIdStore;
_analyticsIdStore = analyticsIdStore;
_unityAdsIdStore = unityAdsIdStore;
[self readInstallationId];
[self writeInstallationId];
}
return self;
}
- (NSString *)installationId {
return self.savedInstallationId;
}
- (void)readInstallationId {
// Check core services installation id
NSString *installationId = [self.installationIdStore getValue];
if (installationId) {
self.savedInstallationId = installationId;
return;
}
// Check analytics
NSString *analyticsId = [self.analyticsIdStore getValue];
if (analyticsId) {
self.savedInstallationId = analyticsId;
return;
}
// Check Unity Ads
NSString *unityAdsId = [self.unityAdsIdStore getValue];
if (unityAdsId) {
self.savedInstallationId = unityAdsId;
return;
}
// Generate id and write it to user defaults if none is found.
NSString *generatedInstallationId = [[NSUUID UUID] UUIDString];
self.savedInstallationId = generatedInstallationId;
}
- (void)writeInstallationId {
[self.installationIdStore commitValue:self.savedInstallationId];
[self.analyticsIdStore commitValue:self.savedInstallationId];
[self.unityAdsIdStore commitValue:self.savedInstallationId];
}
@end

Просмотреть файл

@ -1,22 +0,0 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface UADSPlist : NSObject
+ (BOOL)writePlistIosEleven:(NSDictionary *)mutableDictionary
toFileUrl:(NSURL *)url API_AVAILABLE(ios(11));
+ (BOOL)writePlistIosTen:(NSDictionary *)mutableDictionary toFileUrl:(NSURL *)url;
+ (void)writePlist:(NSDictionary *)mutableDictionary toFileUrl:(NSURL *)url;
+ (NSDictionary *_Nullable)dictionaryWithContentsOfURLIosEleven:(NSURL *)url API_AVAILABLE(ios(11));
+ (NSDictionary *_Nullable)dictionaryWithContentsOfURLIosTen:(NSURL *)url;
+ (NSDictionary *_Nullable)dictionaryWithContentsOfURL:(NSURL *)url;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -1,48 +0,0 @@
#import "UADSPlist.h"
@implementation UADSPlist
+ (BOOL)writePlistIosEleven:(NSDictionary *)mutableDictionary
toFileUrl:(NSURL *)url API_AVAILABLE(ios(11)) {
return [mutableDictionary writeToURL:url error:nil];
}
+ (BOOL)writePlistIosTen:(NSDictionary *)mutableDictionary toFileUrl:(NSURL *)url {
return [mutableDictionary writeToURL:url atomically:YES];
}
+ (void)writePlist:(NSDictionary *)mutableDictionary toFileUrl:(NSURL *)url {
if (@available(iOS 11.0, *)) {
// We don't care if there is an error.
[UADSPlist writePlistIosEleven:mutableDictionary toFileUrl:url];
} else {
[UADSPlist writePlistIosTen:mutableDictionary toFileUrl:url];
}
}
+ (NSDictionary *_Nullable)dictionaryWithContentsOfURLIosEleven:(NSURL *)url
API_AVAILABLE(ios(11)) {
NSError *plistError;
NSDictionary *plistDictionary = [NSDictionary dictionaryWithContentsOfURL:url error:&plistError];
if (plistError) {
return nil;
}
return plistDictionary;
}
+ (NSDictionary *_Nullable)dictionaryWithContentsOfURLIosTen:(NSURL *)url {
NSDictionary *plistDictionary = [NSDictionary dictionaryWithContentsOfURL:url];
return plistDictionary;
}
+ (NSDictionary *_Nullable)dictionaryWithContentsOfURL:(NSURL *)url {
if (@available(iOS 11.0, *)) {
return [UADSPlist dictionaryWithContentsOfURLIosEleven:url];
} else {
return [UADSPlist dictionaryWithContentsOfURLIosTen:url];
}
}
@end

Просмотреть файл

@ -1,36 +0,0 @@
#import "UADSSessionId.h"
#import "UADSIdStore.h"
#import "UADSUnityPlayerPrefsStore.h"
#import "UADSUserDefaultsStore.h"
@interface UADSSessionId ()
@property(nonatomic, strong) NSString *_Nullable currentSessionId;
@end
@implementation UADSSessionId
+ (instancetype)shared {
static UADSSessionId *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[UADSSessionId alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
self = [super init];
if (self) {
_currentSessionId = [[NSUUID UUID] UUIDString];
}
return self;
}
- (NSString *)sessionId {
return self.currentSessionId;
}
@end

Просмотреть файл

@ -1,12 +0,0 @@
#import <Foundation/Foundation.h>
#import "UADSIdStore.h"
NS_ASSUME_NONNULL_BEGIN
@interface UADSUnityPlayerPrefsStore : NSObject <UADSIdStore>
- (instancetype)initWithKey:(NSString *)key;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -1,65 +0,0 @@
#import "UADSUnityPlayerPrefsStore.h"
#import "UADSPlist.h"
@interface UADSUnityPlayerPrefsStore ()
@property(nonatomic, strong) NSString *key;
@end
@implementation UADSUnityPlayerPrefsStore
- (instancetype)initWithKey:(NSString *)key {
self = [super init];
if (self) {
_key = key;
}
return self;
}
- (NSString *_Nullable)getValue {
NSDictionary *plistDictionary = [self plistDictionary];
id value = [plistDictionary valueForKey:self.key];
if (value && [value isKindOfClass:[NSString class]]) {
return (NSString *)value;
}
return nil;
}
- (void)commitValue:(NSString *)value {
NSDictionary *plistDictionary = [self plistDictionary];
NSURL *plistFileUrl = [self plistFileUrl];
if (plistFileUrl) {
NSMutableDictionary *mutablePlistDictionary = [plistDictionary mutableCopy];
[mutablePlistDictionary setValue:value forKey:self.key];
[UADSPlist writePlist:mutablePlistDictionary toFileUrl:plistFileUrl];
}
}
- (NSDictionary *)plistDictionary {
NSURL *fileUrl = [self plistFileUrl];
NSDictionary *plistDictionary = [UADSPlist dictionaryWithContentsOfURL:fileUrl];
if (!plistDictionary) {
return [[NSDictionary alloc] init];
}
return plistDictionary;
}
- (NSURL *_Nullable)plistFileUrl {
NSURL *_Nullable url = [NSURL fileURLWithPath:[self plistFilePath]];
return url;
}
// The file path According to the Unity PlayerPrefs Documenation :
// https://docs.unity3d.com/ScriptReference/PlayerPrefs.html
// ~/Library/Preferences/com.ExampleCompanyName.ExampleProductName.plist
- (NSString *_Nullable)plistFilePath {
NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
return [[NSString stringWithFormat:@"~/Library/Preferences/%@.plist", bundleIdentifier]
stringByExpandingTildeInPath];
}
@end

Просмотреть файл

@ -1,12 +0,0 @@
#import <Foundation/Foundation.h>
#import "UADSIdStore.h"
NS_ASSUME_NONNULL_BEGIN
@interface UADSUserDefaultsStore : NSObject <UADSIdStore>
- (instancetype)initWithKey:(NSString *)key;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -1,29 +0,0 @@
#import "UADSUserDefaultsStore.h"
@interface UADSUserDefaultsStore ()
@property(nonatomic, strong) NSString *key;
@end
@implementation UADSUserDefaultsStore
- (instancetype)initWithKey:(NSString *)key {
self = [super init];
if (self) {
_key = key;
}
return self;
}
- (NSString *_Nullable)getValue {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
return [userDefaults stringForKey:self.key];
}
- (void)commitValue:(NSString *)value {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:value forKey:self.key];
}
@end

Просмотреть файл

@ -33,7 +33,5 @@ FOUNDATION_EXPORT const unsigned char UnityAdsModuleVersionString[];
#import <UnityAds/USRVInitializeStateFactory.h>
#import <UnityAds/USRVInitializeStateType.h>
#import <UnityAds/UADSDeviceInfoProvider.h>
#import <UnityAds/UADSInstallationId.h>
#import <UnityAds/UADSSessionId.h>
#endif /* UnityAds_h */

Просмотреть файл

@ -124,19 +124,34 @@
[UnityServices setDebugMode: [USRVSdkProperties getDebugMode]];
[USRVClientProperties setGameId: gameId];
[USRVSdkProperties setTestMode: testMode];
[UADSServiceProviderContainer.sharedInstance.serviceProvider.sdkInitializer initializeWithGameID: gameId
testMode:testMode
completion:^{
[USRVSdkProperties setInitializationState: INITIALIZED_SUCCESSFULLY];
testMode:testMode
completion:^{
[USRVSdkProperties setInitializationState: INITIALIZED_SUCCESSFULLY];
} error:^(NSError * _Nonnull error) {
[USRVSdkProperties setInitializationState: INITIALIZED_FAILED];
}];
} error:^(NSError * _Nonnull error) {
[self onInitError:error];
}];
}
}
+ (void)onInitError: (NSError *)error {
[UADSServiceProviderContainer.sharedInstance.serviceProvider.hbTokenReader setInitToken: nil];
[UADSServiceProviderContainer.sharedInstance.serviceProvider.hbTokenReader deleteTokens];
[USRVSdkProperties setInitializationState: INITIALIZED_FAILED];
[[USRVInitializationNotificationCenter sharedInstance] triggerSdkInitializeDidFail: @"Unity Ads SDK failed to initialize"
code: error.code];
dispatch_async(dispatch_get_main_queue(), ^{
[USRVSdkProperties notifyInitializationFailed: kUnityInitializationErrorInternalError
withErrorMessage: error.localizedDescription];
});
}
+ (BOOL)getDebugMode {
return [USRVSdkProperties getDebugMode];
}

Просмотреть файл

@ -1,26 +0,0 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
* UADSInstallationId handles generating or retrieving an installation id across Unity services.
*/
@interface UADSInstallationId : NSObject
/**
* Static method to return a singleton instance of UADSInstallationId.
*
* @return UADSInstallationId
*/
+ (instancetype)shared;
/**
* Method to get the string installation id.
*
* @return NSString
*/
- (NSString *)installationId;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -1,27 +0,0 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
* UADSSessionId handles generating a shared user sessionId to be used across Unity services.
* Use this id to tie data from the same session together.
*/
@interface UADSSessionId : NSObject
/**
* Static method to return a singleton instance of UADSSessionId.
*
* @return UADSSessionId
*/
+ (instancetype)shared;
/**
* Method to get the string session UUID.
*
* @return NSString
*/
- (NSString *)sessionId;
@end
NS_ASSUME_NONNULL_END

Просмотреть файл

@ -0,0 +1,83 @@
import Foundation
import AVFoundation
import UIKit
extension WebUserAgentReaderBase {
static var unityAdsAgent: WebUserAgentReader {
WebUserAgentReaderBase(lastKnownOSKey: Constants.UserDefaultsKeys.LastKnownSystemVersion,
userAgentValueKey: Constants.UserDefaultsKeys.LastKnownUserAgentKey)
}
}
final class DeviceInfoBodyReaderBuilder: DeviceInfoBodyReaderProvider {
private let baseConfig: Config
private(set) var getLegacyInfo: ClosureWithReturn<Bool, [String: Any]>?
private let deviceInfoReader: DeviceInfoReader
init(baseConfig: Config) {
self.baseConfig = baseConfig
let deviceInfoConfig = DeviceInfoReaderBase.Config(timeReader: baseConfig.timeReader,
logger: baseConfig.logger,
userAgentReader: WebUserAgentReaderBase.unityAdsAgent,
sessionInfoProvider: baseConfig.sessionInfoStorage,
gameSettingsReader: baseConfig.gameSettingsReader,
appStartTimeProvider: baseConfig.sdkStateStorage,
telephonyInfoProvider: baseConfig.telephonyInfoProvider)
deviceInfoReader = DeviceInfoReaderBase(config: deviceInfoConfig)
}
var deviceInfoBodyReader: DeviceInfoBodyStrategy {
self.reader(using: baseConfig)
}
func setLegacyInfoGetter(_ closure: ClosureWithReturn<Bool, [String: Any]>?) {
getLegacyInfo = closure
}
}
extension DeviceInfoBodyReaderBuilder {
private func reader(using config: Config) -> DeviceInfoBodyStrategy {
let minStorageReader = JSONStorageContentNormalizer.minStorageContentReader(with: config.persistenceStorage)
let extendedStorageReader = JSONStorageContentNormalizer.extendedStorageContentReader(with: config.persistenceStorage)
let strategyConfig = DeviceInfoBodyStrategyBase.Config(sessionInfoStorage: config.sessionInfoStorage,
trackingStatusReader: config.trackingStatusReader,
gameIdProvider: config.gameSettingsReader,
deviceInfoReader: deviceInfoReader,
privacyReader: config.sdkStateStorage,
piiDataProvider: config.piiDataProvider,
minStorageContentReader: minStorageReader,
extendedStorageContentReader: extendedStorageReader)
var base: DeviceInfoBodyStrategy = DeviceInfoBodyStrategyBase(config: strategyConfig)
base = DeviceInfoBodyReaderWithMetrics(original: base,
measurer: baseConfig.performanceMeasurer,
metricsSender: baseConfig.metricsSender)
let decoratorConfig = DeviceInfoObjBridgeDecorator.Config(logger: config.logger,
experimentsReader: config.sdkStateStorage,
original: base,
getLegacyInfo: getLegacyInfo)
return DeviceInfoObjBridgeDecorator(config: decoratorConfig)
}
}
extension DeviceInfoBodyReaderBuilder {
typealias TimeInfoReader = BootTimeReader & TimeZoneReader & TimeReader
struct Config {
let sessionInfoStorage: SessionInfoReader
let trackingStatusReader: TrackingStatusReader
let gameSettingsReader: SDKGameSettingsProvider
let sdkStateStorage: PrivacyStateReader & ExperimentsReader & AppStartTimeProvider
var piiDataProvider: UIDevicePIIDataProvider = UIDevice.current
let persistenceStorage: JSONStorageBridge
let logger: Logger
let timeReader: TimeInfoReader
let telephonyInfoProvider: TelephonyInfoProvider & CountryCodeProvider
let performanceMeasurer: PerformanceMeasurer<String>
let metricsSender: MetricSender
}
}

Просмотреть файл

@ -4,29 +4,39 @@ protocol InitializationStateSubject {
func subscribe(_ block: @escaping ResultClosure<Void>)
}
protocol AppStartTimeSaver {
func save(startTime: TimeInterval)
}
final class SDKStateStorage: GenericMediator<UResult<Void>>,
InitializationStateSubject,
MetricsSenderBatchConditionSubject,
UnityAdsConfigurationProvider,
UnityAdsLocalConfigurationLoader,
RetriesInfoStorage {
let retriesInfoStorage: RetriesInfoWriter & RetriesInfoReader = RetriesInfoStorageBase()
typealias ConfigProvider = UnityAdsConfigurationProvider &
UnityAdsLocalConfigurationLoader &
UnityAdsConfigSubject &
ExperimentsReader
@Atomic var currentState: SDKInitializerBase.State = .notInitialized {
didSet {
notifyStateChange()
}
@Atomic private var startTimeStamp: TimeInterval = 0
@Atomic private var initializeState: SDKInitializerBase.State = .notInitialized
private let privacyStorage = PrivacyStateStorage()
private(set) var configProvider: ConfigProvider
init(configProvider: ConfigProvider) {
self.configProvider = configProvider
}
var webViewConfig: UnityAdsConfig.Network.WebView {
guard !privacyStorage.privacyResponse.webViewConfig.url.isEmpty else {
guard !privacyStorage.$privacyResponse.load().webViewConfig.url.isEmpty else {
return config.network.webView
}
return privacyStorage.privacyResponse.webViewConfig
return privacyStorage.$privacyResponse.load().webViewConfig
}
var config: UnityAdsConfig {
@ -34,13 +44,12 @@ final class SDKStateStorage: GenericMediator<UResult<Void>>,
set { configProvider.config = newValue }
}
private let privacyStorage = PrivacyStateStorage()
private(set) var configProvider: ConfigProvider
let retriesInfoStorage: RetriesInfoWriter & RetriesInfoReader = RetriesInfoStorageBase()
init(configProvider: ConfigProvider) {
self.configProvider = configProvider
var currentState: SDKInitializerBase.State {
get { _initializeState.load() }
set {
_initializeState.mutate({ $0 = newValue })
notifyStateChange()
}
}
// Metric Condition subscribes to be able to release batch on failure or success.
@ -67,6 +76,8 @@ final class SDKStateStorage: GenericMediator<UResult<Void>>,
}
extension SDKStateStorage: PrivacyStateReader, PrivacyResponseSaver {
var shouldSendNonBehavioural: Bool { privacyStorage.shouldSendNonBehavioural }
var privacyState: PrivacyState { privacyStorage.privacyState }
func save(response: PrivacyResponse) {
@ -88,19 +99,6 @@ extension SDKStateStorage: ExperimentsReader, SessionTokenReader {
}
}
final class PrivacyStateStorage: GenericMediator<UResult<PrivacyResponse>>,
PrivacyStateReader,
PrivacyResponseSaver {
@Atomic var privacyResponse: PrivacyResponse = .empty
var privacyState: PrivacyState { privacyResponse.state }
func save(response: PrivacyResponse) {
privacyResponse = response
notifyObservers(with: .success(privacyResponse))
}
}
extension SDKStateStorage {
func retryInfo() -> [String: String] {
retriesInfoStorage.retryInfo()
@ -110,3 +108,14 @@ extension SDKStateStorage {
retriesInfoStorage.set(retried: retried, task: task)
}
}
extension SDKStateStorage: AppStartTimeSaver, AppStartTimeProvider {
var appStartTime: TimeInterval {
_startTimeStamp.load()
}
func save(startTime: TimeInterval) {
_startTimeStamp.mutate({ $0 = startTime })
}
}

Просмотреть файл

@ -3,25 +3,37 @@ import Foundation
final class InitializationTaskFactoryBase: TaskFactory {
typealias SettingsProvider = LoggerSettingsReader
private let downloaderBuilder: WebViewDownloadBuilder
private let networkSenderProvider: MetricsSenderProvider & UnityAdsNetworkSenderProvider
private let metricSenderProvider: MetricsSenderProvider
private let objcFactory: USRVInitializeStateFactory
private let performanceMeasurer: PerformanceMeasurer<String>
private let sdkStateStorage: SDKStateStorage
private let settingsProvider: SettingsProvider
private var networkConfig: UnityAdsConfig.Network { sdkStateStorage.networkConfig }
private let keyValueStorage: KeyValueStorage
private let cleanupKeys: [String]
private let deviceInfoReader: DeviceInfoBodyStrategy
private let networkSenderProvider: UnityAdsNetworkSenderProvider
init(downloaderBuilder: WebViewDownloadBuilder,
metricSenderProvider: MetricsSenderProvider & UnityAdsNetworkSenderProvider,
metricSenderProvider: MetricsSenderProvider,
networkSenderProvider: UnityAdsNetworkSenderProvider,
sdkStateStorage: SDKStateStorage,
performanceMeasurer: PerformanceMeasurer<String>,
stateFactoryObjc: USRVInitializeStateFactory = .init(),
settingsProvider: SettingsProvider) {
settingsProvider: SettingsProvider,
keyValueStorage: KeyValueStorage,
cleanupKeys: [String],
deviceInfoReader: DeviceInfoBodyStrategy) {
self.downloaderBuilder = downloaderBuilder
self.objcFactory = stateFactoryObjc
self.networkSenderProvider = metricSenderProvider
self.metricSenderProvider = metricSenderProvider
self.networkSenderProvider = networkSenderProvider
self.performanceMeasurer = performanceMeasurer
self.sdkStateStorage = sdkStateStorage
self.settingsProvider = settingsProvider
self.keyValueStorage = keyValueStorage
self.cleanupKeys = cleanupKeys
self.deviceInfoReader = deviceInfoReader
}
func task(of type: InitTaskState) -> Task {
@ -51,7 +63,7 @@ extension InitializationTaskFactoryBase {
extension InitializationTaskFactoryBase {
var metricSender: MetricSender {
networkSenderProvider.metricsSender
metricSenderProvider.metricsSender
}
var requestRetryConfig: RetriableOperationConfig {
@ -76,7 +88,13 @@ extension InitializationTaskFactoryBase {
case .reset:
let legacy = taskForObjcType(type)
return useNewTasksFlow ? PreviousSessionCleanupTask(loggerSettingsReader: settingsProvider,
legacyTask: legacy) : legacy
legacyTask: legacy,
storage: keyValueStorage,
cleanupKeys: cleanupKeys) : legacy
case .initModules:
let legacy = taskForObjcType(type)
return useNewTasksFlow ? InitModulesTask(legacyTask: legacy,
deviceInfoReader: deviceInfoReader) : legacy
default:
return taskForObjcType(type)
}

Просмотреть файл

@ -5,18 +5,26 @@ final class InitializationTaskFactoryStrategy: TaskFactory {
private let factoryBase: InitializationTaskFactoryBase
init(downloaderBuilder: WebViewDownloadBuilder,
metricSenderProvider: MetricsSenderProvider & UnityAdsNetworkSenderProvider,
metricSenderProvider: MetricsSenderProvider,
networkSenderProvider: UnityAdsNetworkSenderProvider,
sdkStateStorage: SDKStateStorage,
performanceMeasurer: PerformanceMeasurer<String>,
stateFactoryObjc: USRVInitializeStateFactory = .init(),
settingsProvider: SettingsProvider) {
settingsProvider: SettingsProvider,
keyValueStorage: KeyValueStorage,
cleanupKeys: [String],
deviceInfoReader: DeviceInfoBodyStrategy) {
self.factoryBase = .init(downloaderBuilder: downloaderBuilder,
metricSenderProvider: metricSenderProvider,
networkSenderProvider: networkSenderProvider,
sdkStateStorage: sdkStateStorage,
performanceMeasurer: performanceMeasurer,
stateFactoryObjc: stateFactoryObjc,
settingsProvider: settingsProvider)
settingsProvider: settingsProvider,
keyValueStorage: keyValueStorage,
cleanupKeys: cleanupKeys,
deviceInfoReader: deviceInfoReader)
}
func task(of type: InitTaskCategory) -> Task {

Просмотреть файл

@ -17,8 +17,8 @@ final class SDKInitializerBase: SDKInitializer {
}
private var initConfig: SDKInitializerConfig {
get { settingsStorage.currentInitConfig }
set { settingsStorage.currentInitConfig = newValue }
get { settingsStorage.$currentInitConfig.load() }
set { settingsStorage.$currentInitConfig.mutate({ $0 = newValue }) }
}
init(task: Task,
@ -37,6 +37,12 @@ final class SDKInitializerBase: SDKInitializer {
private extension SDKInitializerBase {
func startInitialization(with config: SDKInitializerConfig, completion: @escaping ResultClosure<Void>) {
completions = completions.appended(completion)
if !isGameIdValid(config) {
notifySavedCompletionsAndClean(with: .failure(InvalidGameId()))
return
}
switch state {
case .notInitialized:
changeStatusAndStartTheTask(config: config)
@ -47,7 +53,6 @@ private extension SDKInitializerBase {
case .initialized:
notifySavedCompletionsAndClean(with: VoidSuccess)
}
}
func changeStatusAndStartTheTask(config: SDKInitializerConfig) {
@ -55,6 +60,10 @@ private extension SDKInitializerBase {
startTask()
}
func isGameIdValid(_ config: SDKInitializerConfig) -> Bool {
return Int(config.gameID) != nil
}
func setInProgress(for config: SDKInitializerConfig) {
initConfig = config
state = .inProcess
@ -85,9 +94,11 @@ private extension SDKInitializerBase {
public struct SDKInitializerConfig {
public let gameID: String
public init(gameID: String) {
public let isTestModeEnabled: Bool
public init(gameID: String,
isTestModeEnabled: Bool) {
self.gameID = gameID
self.isTestModeEnabled = isTestModeEnabled
}
}
@ -111,3 +122,9 @@ extension SDKInitializerBase {
case initialized
}
}
struct InvalidGameId: LocalizedError {
var errorDescription: String? {
return "GameId should be a number"
}
}

Просмотреть файл

@ -1,11 +0,0 @@
import Foundation
protocol SharedSessionIdReader {
var sessionId: String { get }
}
class SharedSessionIdReaderBase: SharedSessionIdReader {
var sessionId: String {
UADSSessionId.shared().sessionId()
}
}

Просмотреть файл

@ -0,0 +1,25 @@
import Foundation
final class InitModulesTask: PerformanceMeasurableTask {
var startEventName: String?
var resultMetrics: ResultMetrics.Type { Constants.Metrics.Task.InitModules.self }
private let legacyTask: PerformanceMeasurableTask
private let deviceInfoReader: DeviceInfoBodyStrategy
init(legacyTask: PerformanceMeasurableTask,
deviceInfoReader: DeviceInfoBodyStrategy) {
self.legacyTask = legacyTask
self.deviceInfoReader = deviceInfoReader
}
func start(completion: @escaping ResultClosure<Void>) {
initDeviceStaticInfo()
legacyTask.start(completion: completion)
}
private func initDeviceStaticInfo() {
deviceInfoReader.initializeStaticInfo()
}
}

Просмотреть файл

@ -6,16 +6,23 @@ final class PreviousSessionCleanupTask: PerformanceMeasurableTask {
private let loggerSettingsReader: LoggerSettingsReader
private let fileManager: FileManager
private let legacyTask: PerformanceMeasurableTask
private let storage: KeyValueStorage
private let cleanupKeys: [String]
init(loggerSettingsReader: LoggerSettingsReader,
fileManager: FileManager = .default,
legacyTask: PerformanceMeasurableTask) {
legacyTask: PerformanceMeasurableTask,
storage: KeyValueStorage,
cleanupKeys: [String]) {
self.loggerSettingsReader = loggerSettingsReader
self.fileManager = fileManager
self.legacyTask = legacyTask
self.storage = storage
self.cleanupKeys = cleanupKeys
}
func start(completion: @escaping ResultClosure<Void>) {
try? fileManager.deleteFile(at: loggerSettingsReader.logsFileURL)
cleanupKeys.forEach { storage.delete(forKey: $0) }
legacyTask.start(completion: completion)
}

Просмотреть файл

@ -9,6 +9,7 @@ final class PrivacyFetchTask: PerformanceMeasurableTask {
private let asyncReader: PrivacyAsyncReader
private let storage: PrivacyCRUD
private let codesToFail = [PrivacyError.gameIdDisabled.rawValue]
init(asyncReader: PrivacyAsyncReader, storage: PrivacyCRUD) {
self.asyncReader = asyncReader
self.storage = storage
@ -20,12 +21,32 @@ final class PrivacyFetchTask: PerformanceMeasurableTask {
return
}
asyncReader.getPrivacyData { [storage] result in
asyncReader.getPrivacyData { [storage] (result: UResult<PrivacyResponse>) in
result.do(storage.save)
.map({ _ in })
.recover({ _ in })
.flatMapError(self.failErrorIfNeed)
.sink(completion)
}
}
private func failErrorIfNeed(_ error: Error) -> UResult<Void> {
guard let error = error as? NetworkResponseError,
let errorCode = error.errorCode,
codesToFail.contains(errorCode),
let privacyErrorCode = PrivacyError(rawValue: errorCode) else {
return VoidSuccess
}
return .failure(privacyErrorCode)
}
}
enum PrivacyError: Int, LocalizedError {
case gameIdDisabled = 423
var errorDescription: String? {
switch self {
case .gameIdDisabled:
return "GameId disabled"
}
}
}

Просмотреть файл

@ -4,11 +4,53 @@ final class StartInitTask<F: TaskFactory>: PerformanceMeasurableTask {
var startEventName: String? = Constants.Metrics.Task.Initializer.Started
var resultMetrics: ResultMetrics.Type { Constants.Metrics.Task.Initializer.self }
private let runner: SyncTaskRunner<F>
init(factory: F, sequence: [F.Element]) {
private let timeReader: TimeReader
private let appStartTimeSaver: AppStartTimeSaver
private let logger: Logger
private let settingProvider: SDKGameSettingsProvider
private let sessionInfoReader: SessionInfoReader
init(factory: F,
sequence: [F.Element],
timeReader: TimeReader,
appStartTimeSaver: AppStartTimeSaver,
logger: Logger,
settingProvider: SDKGameSettingsProvider,
sessionInfoReader: SessionInfoReader) {
self.runner = .init(factory: factory, sequence: sequence)
self.timeReader = timeReader
self.appStartTimeSaver = appStartTimeSaver
self.logger = logger
self.settingProvider = settingProvider
self.sessionInfoReader = sessionInfoReader
}
func start(completion: @escaping ResultClosure<Void>) {
appStartTimeSaver.save(startTime: timeReader.currentTimestamp(in: .milliseconds))
logger.info(message: initLogMessage)
runner.start(completion: completion)
}
}
extension StartInitTask {
var initLogMessage: String {
"Initializing Unity Ads \(versionName) \(versionNumber) with game id \(settingProvider.gameID) in \(mode) mode, session \(sessionId)"
}
var versionName: String {
Version().versionName
}
var versionNumber: Int {
Version().versionNumber
}
var mode: String {
settingProvider.isTestModeEnabled ? "test" : "production"
}
var sessionId: String {
sessionInfoReader.sessionID
}
}

Просмотреть файл

@ -0,0 +1,77 @@
import Foundation
protocol MetricsSenderProvider {
var metricsSender: MetricSender { get }
}
protocol DiagnosticMetricsSenderProvider {
var diagnosticMetricSender: MetricSender { get }
}
final class UnityAdsEventsNetworkServicesFactory: MetricsSenderProvider, DiagnosticMetricsSenderProvider {
let metricsSender: MetricSender
let diagnosticMetricSender: MetricSender
let config: Config
let urlSession: URLSession
init(config: Config) {
self.config = config
urlSession = URLSession(configuration: config.settingsReader.metricSessionConfiguration)
let networkBuilder = CoreNetworkServicesBuilder(session: urlSession,
configurationProvider: config.configProvider,
metricsCollector: nil,
metricsSender: nil)
let metricsAdapter = MetricsAdapter(deviceInfoReader: config.uiDeviceInfoProvider,
telephonyProvider: config.teleponyProvider,
metricsMetaDataReader: config.metricsDataReader,
allowedResourceTypes: config.settingsReader.metricsResourceTypes,
retriesInfoReader: config.retriesReader,
gameSettingsReader: config.settingsReader,
sessionId: config.sessionInfoReader.sessionID)
let factoryConfig = UnityAdsEventsRequestFactory.Config(configurationProvider: config.configProvider,
metricsAdapter: metricsAdapter)
let networkSenderBuilder = networkBuilder.createNetworkSenderBuilder(with: config.codes)
let factory = UnityAdsEventsRequestFactory(config: factoryConfig)
let metricsSenderBuilder = MetricsSenderBuilder(metricsConfigReader: config.configProvider,
unityAdsRequestFactory: factory,
networkBuilder: networkSenderBuilder,
conditionSubject: config.configProvider,
logger: config.logger,
metricAdapter: metricsAdapter)
metricsSender = metricsSenderBuilder.metricsSender
diagnosticMetricSender = metricsSenderBuilder.networkDiagnosticMetricsSender
}
}
extension UnityAdsEventsNetworkServicesFactory {
var unityAdsMetricsNativeNetwork: UnityAdsWebViewNetwork {
.init(networkSender: metricsNetworkServicesBuilder.sender(withAllowedCodes: config.codes, retryConfig: nil),
networkDownloader: metricsNetworkServicesBuilder.downloader(withAllowedCodes: [],
baseDirectory: config.filePaths.baseDir,
retryConfig: nil))
}
private var metricsNetworkServicesBuilder: CoreNetworkServicesBuilder {
.init(session: urlSession,
configurationProvider: config.configProvider,
metricsCollector: nil,
metricsSender: nil)
}
}
extension UnityAdsEventsNetworkServicesFactory {
struct Config {
let configProvider: UnityAdsConfigurationProvider & MetricsSenderBatchConditionSubject
let uiDeviceInfoProvider: UIDeviceInfoProvider = UIDevice.current
let metricsDataReader: ExperimentsReader & SessionTokenReader
let retriesReader: RetriesInfoReader
let logger: Logger
let settingsReader: NetworkSettingsProvider & SDKGameSettingsProvider
var sessionInfoReader: SessionInfoReader
var codes: [Int] = Array((200...299))
var filePaths: FilePaths = FilePaths()
var teleponyProvider: TelephonyNetworkStatusProvider = TelephonyNetworkStatusProvider()
}
}

Просмотреть файл

@ -1,9 +1,5 @@
import Foundation
protocol MetricsSenderProvider {
var metricsSender: MetricSender { get }
}
protocol UnityAdsNetworkSenderProvider {
func networkSender(includeRetryLogic: Bool, collectCompressionMetrics: Bool) -> UnityAdsNetworkSender
}
@ -19,7 +15,7 @@ protocol NetworkSettingsProvider {
Factory that creates network services for swift layer as well as objc layer. Services can be used with metrics or without them.
*/
final class UnityAdsNetworkServicesFactory: MetricsSenderProvider {
final class UnityAdsNetworkServicesFactory {
typealias ConfigProvider = UnityAdsConfigurationProvider &
MetricsSenderBatchConditionSubject &
ExperimentsReader &
@ -27,48 +23,37 @@ final class UnityAdsNetworkServicesFactory: MetricsSenderProvider {
RetriesInfoStorage
typealias SettingsProvider = LoggerSettingsReader &
NetworkSettingsProvider &
SDKGameIdProvider
SDKGameSettingsProvider
private let configurationProvider: ConfigProvider
private let metricsCollector: URLSessionTaskMetricCollector = URLSessionTaskMetricCollectorBase()
private var metricSession: URLSession
private var mainSession: URLSession
private let allowedCodes: [Int]
private let filePaths = FilePaths()
private let metricsAdapter: MetricsAdapter
private let performanceMeasurer: PerformanceMeasurer<String>
let metricsSender: MetricSender
let diagnosticMetricsSender: MetricSender
let deviceInfoReader: DeviceInfoReader & LegacyDeviceInfoReader
let metricSenderProvider: MetricsSenderProvider & DiagnosticMetricsSenderProvider
let deviceInfoReaderProvider: DeviceInfoBodyReaderProvider
private let configEndpointProvider: ConfigEndpointProvider
private let telephonyProvider: TelephonyNetworkStatusProvider
init(settingsProvider: SettingsProvider,
configurationProvider: ConfigProvider,
deviceInfoReader: DeviceInfoReader & LegacyDeviceInfoReader,
deviceInfoReaderProvider: DeviceInfoBodyReaderProvider,
performanceMeasurer: PerformanceMeasurer<String>,
logger: Logger) {
logger: Logger,
metricSenderProvider: MetricsSenderProvider & DiagnosticMetricsSenderProvider,
telephonyProvider: TelephonyNetworkStatusProvider = TelephonyNetworkStatusProvider()) {
self.configurationProvider = configurationProvider
self.deviceInfoReader = deviceInfoReader
self.deviceInfoReaderProvider = deviceInfoReaderProvider
self.allowedCodes = settingsProvider.responseSuccessCodes
self.telephonyProvider = telephonyProvider
mainSession = URLSession(configuration: settingsProvider.mainSessionConfiguration,
delegate: metricsCollector,
delegateQueue: nil)
metricSession = URLSession(configuration: settingsProvider.metricSessionConfiguration)
self.metricsAdapter = MetricsAdapter(deviceInfoReader: deviceInfoReader,
metricsMetaDataReader: self.configurationProvider,
allowedResourceTypes: settingsProvider.metricsResourceTypes,
retriesInfoReader: configurationProvider,
gameId: settingsProvider.gameID,
sessionId: SharedSessionIdReaderBase().sessionId)
self.configEndpointProvider = EndpointProviderBase(worldZoneReader: WorldZoneReaderBase(countryCodeProvider: deviceInfoReader))
let metricSenderBuilder = createMetricsSenderBuilder(session: metricSession,
configProvider: configurationProvider,
metricsAdapter: self.metricsAdapter,
deviceInfoReader: deviceInfoReader,
logger: logger)
metricsSender = metricSenderBuilder.metricsSender
diagnosticMetricsSender = metricSenderBuilder.networkDiagnosticMetricsSender
let deviceInfoReader = deviceInfoReaderProvider.deviceInfoBodyReader
self.configEndpointProvider = EndpointProviderBase(worldZoneReader: WorldZoneReaderBase(countryCodeProvider: telephonyProvider))
self.metricSenderProvider = metricSenderProvider
self.performanceMeasurer = performanceMeasurer
}
}
@ -113,33 +98,25 @@ extension UnityAdsNetworkServicesFactory {
networkDownloader: coreNetworkServicesBuilder.downloader(withAllowedCodes: [], baseDirectory: filePaths.baseDir, retryConfig: nil))
}
var unityAdsMetricsNativeNetwork: UnityAdsWebViewNetwork {
.init(networkSender: metricsNetworkServicesBuilder.sender(withAllowedCodes: allowedCodes, retryConfig: nil),
networkDownloader: metricsNetworkServicesBuilder.downloader(withAllowedCodes: [], baseDirectory: filePaths.baseDir, retryConfig: nil))
}
}
extension UnityAdsNetworkServicesFactory {
private func unityAdsRequestFactory(collectCompressionMetrics: Bool) -> UnityAdsRequestFactory {
.init(configurationProvider: configurationProvider,
adapter: metricsAdapter,
deviceInfoReader: deviceInfoReader,
bodyCompressor: bodyCompressor(includeMetrics: collectCompressionMetrics))
.init(config: .init(configurationProvider: configurationProvider,
deviceInfoReaderProvider: deviceInfoReaderProvider,
bodyCompressor: bodyCompressor(includeMetrics: collectCompressionMetrics),
countryCodeProvider: telephonyProvider))
}
private func bodyCompressor(includeMetrics: Bool) -> DataCompressor {
let original = GZipCompressor()
return includeMetrics ? DataCompressorWithMetrics(original: original,
measurer: performanceMeasurer,
metricsSender: metricsSender) : original
metricsSender: metricSenderProvider.metricsSender) : original
}
private var metricsNetworkBuilder: NetworkSenderBuilder {
metricsNetworkServicesBuilder.createNetworkSenderBuilder(with: allowedCodes)
}
private var unityAdsDownloader: UnityAdsNetworkDownloader {
let downloader = coreNetworkServicesBuilder.downloader(withAllowedCodes: allowedCodes,
baseDirectory: filePaths.baseDir,
@ -152,67 +129,6 @@ extension UnityAdsNetworkServicesFactory {
.init(session: mainSession,
configurationProvider: configurationProvider,
metricsCollector: metricsCollector,
metricsSender: diagnosticMetricsSender)
metricsSender: metricSenderProvider.diagnosticMetricSender)
}
private var metricsNetworkServicesBuilder: CoreNetworkServicesBuilder {
.init(session: metricSession,
configurationProvider: configurationProvider,
metricsCollector: nil,
metricsSender: nil)
}
}
private func createMetricsSender(session: URLSession,
configProvider: UnityAdsConfigurationProvider & MetricsSenderBatchConditionSubject,
metricsAdapter: MetricsAdapter,
deviceInfoReader: DeviceInfoReader & LegacyDeviceInfoReader,
logger: Logger,
codes: [Int] = Array((200...299))) -> MetricSender {
let networkBuilder = CoreNetworkServicesBuilder(session: session,
configurationProvider: configProvider,
metricsCollector: nil,
metricsSender: nil)
let unityAdsRequestFactory = UnityAdsRequestFactory(configurationProvider: configProvider,
adapter: metricsAdapter,
deviceInfoReader: deviceInfoReader,
bodyCompressor: GZipCompressor())
let networkSenderBuilder = networkBuilder.createNetworkSenderBuilder(with: codes)
let metricsSenderBuilder = MetricsSenderBuilder(metricsConfigReader: configProvider,
unityAdsRequestFactory: unityAdsRequestFactory,
networkBuilder: networkSenderBuilder,
conditionSubject: configProvider,
logger: logger,
metricAdapter: metricsAdapter)
return metricsSenderBuilder.networkDiagnosticMetricsSender
}
private func createMetricsSenderBuilder(session: URLSession,
configProvider: UnityAdsConfigurationProvider & MetricsSenderBatchConditionSubject,
metricsAdapter: MetricsAdapter,
deviceInfoReader: DeviceInfoReader & LegacyDeviceInfoReader,
logger: Logger,
codes: [Int] = Array((200...299))) -> MetricsSenderBuilder {
let networkBuilder = CoreNetworkServicesBuilder(session: session,
configurationProvider: configProvider,
metricsCollector: nil,
metricsSender: nil)
let unityAdsRequestFactory = UnityAdsRequestFactory(configurationProvider: configProvider,
adapter: metricsAdapter,
deviceInfoReader: deviceInfoReader,
bodyCompressor: GZipCompressor())
let networkSenderBuilder = networkBuilder.createNetworkSenderBuilder(with: codes)
return MetricsSenderBuilder(metricsConfigReader: configProvider,
unityAdsRequestFactory: unityAdsRequestFactory,
networkBuilder: networkSenderBuilder,
conditionSubject: configProvider,
logger: logger,
metricAdapter: metricsAdapter)
}

Просмотреть файл

@ -0,0 +1,42 @@
import Foundation
final class DeviceInfoObjBridgeDecorator: DeviceInfoBodyStrategy {
func initializeStaticInfo() {
guard shouldUseNewImplementation else { return }
config.original.initializeStaticInfo()
}
private let config: Config
init(config: Config) {
self.config = config
}
func getDeviceInfoBody(of type: DeviceInfoType) -> [String: Any] {
guard shouldUseNewImplementation else {
return getLegacyDeviceInfo(of: type)
}
return config.original.getDeviceInfoBody(of: type)
}
private var shouldUseNewImplementation: Bool {
config.experimentsReader.experiments?.isSwiftDeviceInfoEnabled.value ?? false
}
private func getLegacyDeviceInfo(of type: DeviceInfoType) -> [String: Any] {
guard let getLegacyInfo = config.getLegacyInfo else {
config.logger.fatal(message: "Legacy Device Info Closure is Not set")
return [:]
}
return getLegacyInfo(type == .extended)
}
}
extension DeviceInfoObjBridgeDecorator {
struct Config {
let logger: Logger
let experimentsReader: ExperimentsReader
let original: DeviceInfoBodyStrategy
let getLegacyInfo: ClosureWithReturn<Bool, [String: Any]>?
}
}

Просмотреть файл

@ -12,7 +12,7 @@ final class SDKInitializerOBJBridge: NSObject {
testMode: Bool,
completion: @escaping VoidClosure,
error: @escaping Closure<Error>) {
let config = SDKInitializerConfig(gameID: gameID)
let config = SDKInitializerConfig(gameID: gameID, isTestModeEnabled: testMode)
sdkInitializer.initialize(with: config) { result in
result.do(completion)
.onFailure(error)

Просмотреть файл

@ -0,0 +1,28 @@
import Foundation
final class JSONStorageBridge: KeyValueStorage, StorageContentReader {
var jsonStorageReaderClosure: ClosureWithReturn<String, Any?>?
var jsonStorageSaverClosure: Closure<(String, Any)>?
var jsonStorageDeleteClosure: Closure<(String)>?
var jsonStorageReaderContentClosure: VoidClosureWithReturn<[String: Any]>?
func getValue<T>(for key: String) -> T? {
jsonStorageReaderClosure?(key) as? T
}
func saveValue<T>(value: T, forKey key: String) {
jsonStorageSaverClosure?((key, value))
}
func delete(forKey key: String) {
jsonStorageDeleteClosure?(key)
}
var allContent: [String: Any] {
jsonStorageReaderContentClosure?() ?? [:]
}
}

Просмотреть файл

@ -0,0 +1,103 @@
import Foundation
final class JSONStorageContentNormalizer: StorageContentReader {
private let config: Config
init(config: Config) {
self.config = config
}
var allContent: [String: Any] {
var mergedDictionary = [String: Any]()
config.original.allContent.forEach { pair in
processRootPair(&mergedDictionary, pair: pair)
}
return mergedDictionary
}
func processRootPair(_ output: inout [String: Any], pair: (key: String, value: Any)) {
guard shouldIncludeRoot(key: pair.key) else { return }
guard let value = pair.value as? [String: Any] else {
output[pair.key] = pair.value
return
}
normalize(dictionary: value, output: &output, withParentKey: pair.key)
}
func normalize(dictionary: [String: Any],
output: inout [String: Any],
withParentKey parentKey: String = "") {
dictionary.forEach { pair in
processPair(&output, parentKey: parentKey, pair: pair)
}
}
private func shouldIncludeRoot(key: String) -> Bool {
guard !config.excludeKeys.contains(key) else { return false }
guard !config.rootLevelKeysInclude.isEmpty else { return true }
return config.rootLevelKeysInclude.contains(key)
}
private func processPair(_ output: inout [String: Any],
parentKey: String = "",
pair: (key: String, value: Any)) {
guard !config.excludeKeys.contains(pair.key) else { return }
let newKey = newKey(for: pair.key, parentKey: parentKey)
guard let dictionary = pair.value as? [String: Any] else {
output[newKey] = pair.value
return
}
normalize(dictionary: dictionary, output: &output, withParentKey: newKey)
}
private func newKey(for key: String, parentKey: String) -> String {
guard !parentKey.isEmpty else { return key }
return config.reduceKeys.contains(key) ? parentKey : "\(parentKey)\(config.separator)\(key)"
}
}
extension JSONStorageContentNormalizer {
struct Config {
var separator = "."
let original: StorageContentReader
let rootLevelKeysInclude: [String]
let excludeKeys: [String]
let reduceKeys: [String]
}
}
extension JSONStorageContentNormalizer {
static func minStorageContentReader(with storage: StorageContentReader) -> StorageContentReader {
JSONStorageContentNormalizer(config: .init(original: storage,
rootLevelKeysInclude:
[
JSONStorageKeys.Privacy,
JSONStorageKeys.GDPR,
JSONStorageKeys.Unity,
JSONStorageKeys.PIPL
],
excludeKeys: [ "ts" ],
reduceKeys: [ "value" ]))
}
static func extendedStorageContentReader(with storage: StorageContentReader) -> StorageContentReader {
JSONStorageContentNormalizer(config: .init(original: storage,
rootLevelKeysInclude:
[
JSONStorageKeys.Mediation,
JSONStorageKeys.Framework,
JSONStorageKeys.Adapter,
JSONStorageKeys.Configuration,
JSONStorageKeys.User,
JSONStorageKeys.UnifiedConfig
],
excludeKeys: [ "ts",
JSONStorageKeys.Exclude,
JSONStorageKeys.PII,
"nonBehavioral",
"nonbehavioral"
],
reduceKeys: [ "value" ]))
}
}

Просмотреть файл

@ -1,11 +1,15 @@
import Foundation
// swiftlint:disable type_body_length
@objc
protocol ServiceProviderObjCBridgeDelegate: AnyObject {
func getDeviceInfo(extended: Bool) -> [String: Any]
func getGameSessionId() -> String
func didCompleteInit(_ config: [String: Any])
func didReceivePrivacy(_ privacy: [String: Any])
func getValueFromJSONStorage(for key: String) -> Any?
func setValueToJSONStorage(_ value: Any?, for key: String)
func deleteKeyFromJSONStorage(for key: String)
func storageContent() -> [String: Any]
}
@objc
@ -13,7 +17,7 @@ final class ServiceProviderObjCBridge: NSObject {
private(set) weak var delegate: ServiceProviderObjCBridgeDelegate?
var serviceProvider = UnityAdsServiceProvider() {
var serviceProvider = UnityAdsServiceProvider(skdSettingsStorage: .init()) {
didSet {
// this is required only for tests until we have objc legacy code and swift
subscribeDelegate()
@ -64,7 +68,19 @@ final class ServiceProviderObjCBridge: NSObject {
@objc
func setDebugMode(_ debugMode: Bool) {
serviceProvider.logLevel = debugMode ? .trace : .fatal
serviceProvider.logLevel = debugMode ? .trace : .info
}
@objc
func getToken(_ completion: Closure<[String: Any]>) {
do {
try serviceProvider.headerBiddingTokenReader.getToken({ token in
let tokenDict = (try? token.convertIntoDictionary()) ?? [:]
completion(tokenDict)
})
} catch {
completion([:])
}
}
}
@ -83,6 +99,16 @@ extension ServiceProviderObjCBridge {
}
}
extension ServiceProviderObjCBridge {
@objc var gameSessionId: String {
"\(serviceProvider.sessionInfoStorage.gameSessionID)"
}
@objc var sessionId: String {
serviceProvider.sessionInfoStorage.sessionID
}
}
private extension ServiceProviderObjCBridge {
func subscribeDelegate() {
@ -90,6 +116,22 @@ private extension ServiceProviderObjCBridge {
return delegate?.getDeviceInfo(extended: $0) ?? [:]
})
serviceProvider.setLegacyJSONSaverClosure { [weak delegate] in
delegate?.setValueToJSONStorage($0.1, for: $0.0)
}
serviceProvider.setLegacyJSONReaderClosure { [weak delegate] in
return delegate?.getValueFromJSONStorage(for: $0)
}
serviceProvider.jsonStorageObjCBridge.jsonStorageReaderContentClosure = { [weak delegate] in
delegate?.storageContent() ?? [:]
}
serviceProvider.setLegacyJSONKeyDeleteClosure { [weak delegate] in
delegate?.deleteKeyFromJSONStorage(for: $0)
}
serviceProvider.subscribeToPrivacyComplete { [weak delegate] privacyResponse in
delegate?.didReceivePrivacy(privacyResponse)
}

Просмотреть файл

@ -1,17 +1,9 @@
import Foundation
protocol PrivacyStateReader {
var privacyState: PrivacyState { get }
}
protocol PrivacyResponseSaver {
func save(response: PrivacyResponse)
}
final class SDKSettingsStorage: LoggerLevelReader,
LoggerSettingsReader,
NetworkSettingsProvider,
SDKGameIdProvider {
SDKGameSettingsProvider {
var metricSessionConfiguration: URLSessionConfiguration = .ephemeral
@ -21,15 +13,28 @@ final class SDKSettingsStorage: LoggerLevelReader,
var metricsResourceTypes: [Int] = [1] // corresponds to .networkLoad
@Atomic var allowDumpToFile: Bool = false
@Atomic var currentLevel: LogLevel = .fatal
@Atomic var currentInitConfig: SDKInitializerConfig = .init(gameID: "")
var allowDumpToFile: Bool {
get { _logsIntoFile.load() }
set { _logsIntoFile.mutate({ $0 = newValue }) }
}
@Atomic private var logsIntoFile: Bool = false
var currentLevel: LogLevel {
get { _logLevel.load() }
set { _logLevel.mutate({ $0 = newValue }) }
}
@Atomic private var logLevel: LogLevel = .fatal
@Atomic var currentInitConfig: SDKInitializerConfig = .init(gameID: "", isTestModeEnabled: true)
var logsFileURL: URL { filePaths.diagnosticDump }
let filePaths = FilePaths()
var gameID: String {
currentInitConfig.gameID
_currentInitConfig.load().gameID
}
var isTestModeEnabled: Bool {
_currentInitConfig.load().isTestModeEnabled
}
}

Просмотреть файл

@ -0,0 +1,32 @@
import Foundation
enum HeaderBiddingTokenType: Int, Codable {
case native = 0
case remote = 1
}
struct HeaderBiddingToken: DictionaryConvertible {
let value: String
let type: HeaderBiddingTokenType
let uuidString: String
let info: [String: Any]
let customPrefix: String
enum CodingKeys: CodingKey {
case value
case type
case uuidString
case info
case customPrefix
}
func convertIntoDictionary() throws -> [String: Any] {
var dict = [String: Any]()
dict.set(value, forCoding: CodingKeys.value)
dict.set(type.rawValue, forCoding: CodingKeys.type)
dict.set(uuidString, forCoding: CodingKeys.uuidString)
dict.set(info, forCoding: CodingKeys.info)
dict.set(customPrefix, forCoding: CodingKeys.customPrefix)
return dict
}
}

Просмотреть файл

@ -0,0 +1,37 @@
import Foundation
protocol HeaderBiddingTokenReader {
func getToken(_ completion: Closure<HeaderBiddingToken>) throws
}
final class HeaderBiddingTokenReaderBase: HeaderBiddingTokenReader {
private let config: Config
init(_ config: Config) {
self.config = config
}
func getToken(_ completion: Closure<HeaderBiddingToken>) throws {
var info = config.deviceInfoReader.getDeviceInfoBody(of: .extended)
let uniqueId = config.uniqueIdGenerator.uniqueID
if config.experiments.experiments?.scarHbStrategyType != .disabled {
info["tid"] = uniqueId
}
let tokenValue = try config.compressor.compressedInfoString(info)
let prefixedTokenValue = config.customPrefix + tokenValue
let token = HeaderBiddingToken(value: prefixedTokenValue,
type: .native,
uuidString: uniqueId,
info: info,
customPrefix: config.customPrefix)
completion(token)
}
struct Config {
let deviceInfoReader: DeviceInfoBodyStrategy
let compressor: StringCompressor
let customPrefix: String
let uniqueIdGenerator: UniqueGenerator
let experiments: ExperimentsReader
}
}

Просмотреть файл

@ -1,12 +1,12 @@
import Foundation
class UnityAdsServiceProvider {
// swiftlint:disable type_body_length
final class UnityAdsServiceProvider {
typealias TimeInfoReader = BootTimeReader & TimeZoneReader & TimeReader
var sdkStateStorage: SDKStateStorage
var skdSettingsStorage: SDKSettingsStorage
var deviceInfoReader: DeviceInfoReader & LegacyDeviceInfoReader
var allowedNetworkCodes = Array(200...299)
var legacyStateFactory = USRVInitializeStateFactory()
var sessionInfoStorage: SessionInfoReader
let performanceMeasurer: PerformanceMeasurer<String> // probably should use a struct to represent System?
@ -17,28 +17,65 @@ class UnityAdsServiceProvider {
var logger: Logger
private let networkServicesFactory: UnityAdsNetworkServicesFactory
private let timeReader: TimeReader
private let eventsNetworkServicesFactory: UnityAdsEventsNetworkServicesFactory
private let syncQueue: DispatchQueue = .init(label: "Sync.queue")
init(skdSettingsStorage: SDKSettingsStorage = .init()) {
timeReader = TimeReaderBase()
let jsonStorageObjCBridge = JSONStorageBridge()
let timeInfoReader: TimeInfoReader
let trackingStatusReader: TrackingStatusReader = TrackingStatusReaderBase()
let headerBiddingTokenReader: HeaderBiddingTokenReader
private let deviceInfoReaderBuilder: DeviceInfoBodyReaderBuilder
init(skdSettingsStorage: SDKSettingsStorage = .init(),
timeReader: TimeInfoReader = TimeReaderBase(),
telephonyProvider: TelephonyInfoProvider & CountryCodeProvider = TelephonyNetworkStatusProvider()) {
self.skdSettingsStorage = skdSettingsStorage
self.timeInfoReader = timeReader
let loggerStrategy = LoggerStrategy(settingsReader: skdSettingsStorage)
logger = LoggerWithGate(loggerLevelReader: skdSettingsStorage,
original: loggerStrategy)
deviceInfoReader = defaultDeviceInfoReader(withLogger: logger)
let fileStorage = SDKConfigurationFileStorage(filePaths: skdSettingsStorage.filePaths, logger: logger)
let fileStorage = SDKConfigurationFileStorage(filePaths: skdSettingsStorage.filePaths,
logger: logger)
let sdkConfigurationStorage = SDKConfigurationInMemoryStorage(fileStorage: fileStorage)
sdkStateStorage = SDKStateStorage(configProvider: sdkConfigurationStorage)
performanceMeasurer = .init(timeReader: timeReader)
sessionInfoStorage = SessionInfoStorage(settings: .defaultSettings(privateStorage: jsonStorageObjCBridge))
performanceMeasurer = .init(timeReader: timeInfoReader)
let eventsServicesConfig = UnityAdsEventsNetworkServicesFactory.Config(configProvider: sdkStateStorage,
metricsDataReader: sdkStateStorage,
retriesReader: sdkStateStorage,
logger: logger,
settingsReader: skdSettingsStorage,
sessionInfoReader: sessionInfoStorage)
eventsNetworkServicesFactory = UnityAdsEventsNetworkServicesFactory(config: eventsServicesConfig)
let builderConfig = DeviceInfoBodyReaderBuilder.Config(sessionInfoStorage: sessionInfoStorage,
trackingStatusReader: trackingStatusReader,
gameSettingsReader: skdSettingsStorage,
sdkStateStorage: sdkStateStorage,
persistenceStorage: jsonStorageObjCBridge,
logger: logger,
timeReader: timeReader,
telephonyInfoProvider: telephonyProvider,
performanceMeasurer: performanceMeasurer,
metricsSender: eventsNetworkServicesFactory.metricsSender)
deviceInfoReaderBuilder = DeviceInfoBodyReaderBuilder(baseConfig: builderConfig)
networkServicesFactory = .init(settingsProvider: skdSettingsStorage,
configurationProvider: sdkStateStorage,
deviceInfoReader: deviceInfoReader,
deviceInfoReaderProvider: deviceInfoReaderBuilder,
performanceMeasurer: performanceMeasurer,
logger: logger)
logger: logger,
metricSenderProvider: eventsNetworkServicesFactory)
let hbTokenConfig = HeaderBiddingTokenReaderBase.Config(
deviceInfoReader: deviceInfoReaderBuilder.deviceInfoBodyReader,
compressor: Base64GzipCompressor(dataCompressor: GZipCompressor()),
customPrefix: "1:",
uniqueIdGenerator: IdentifiersGeneratorBase(),
experiments: sdkConfigurationStorage)
headerBiddingTokenReader = HeaderBiddingTokenReaderBase(hbTokenConfig)
}
private var _sdkInitializer: SDKInitializer?
@ -46,13 +83,27 @@ class UnityAdsServiceProvider {
var sdkInitializer: SDKInitializer {
syncQueue.sync { getOrCreateInitializer() }
}
}
extension UnityAdsServiceProvider {
func updateConfiguration(_ config: UnityAdsConfig) {
sdkStateStorage.config = config
}
func setLegacyInfoClosure(_ closure: ClosureWithReturn<Bool, [String: Any]>?) {
deviceInfoReader.setLegacyInfoGetter(closure)
deviceInfoReaderBuilder.setLegacyInfoGetter(closure)
}
func setLegacyJSONReaderClosure(_ closure: ClosureWithReturn<String, Any?>?) {
jsonStorageObjCBridge.jsonStorageReaderClosure = closure
}
func setLegacyJSONSaverClosure(_ closure: Closure<(String, Any?)>?) {
jsonStorageObjCBridge.jsonStorageSaverClosure = closure
}
func setLegacyJSONKeyDeleteClosure(_ closure: Closure<String>?) {
jsonStorageObjCBridge.jsonStorageDeleteClosure = closure
}
}
@ -109,19 +160,25 @@ extension UnityAdsServiceProvider {
}
var unityAdsMetricsNativeNetwork: UnityAdsWebViewNetwork {
networkServicesFactory.unityAdsMetricsNativeNetwork
eventsNetworkServicesFactory.unityAdsMetricsNativeNetwork
}
}
extension UnityAdsServiceProvider {
private var initTaskRunner: Task {
TaskPerformanceDecorator(original: mainTask,
metricSender: networkServicesFactory.metricsSender,
metricSender: eventsNetworkServicesFactory.metricsSender,
performanceMeasurer: performanceMeasurer)
}
private var mainTask: PerformanceMeasurableTask {
StartInitTask(factory: initTaskFactory, sequence: sequence)
StartInitTask(factory: initTaskFactory,
sequence: sequence,
timeReader: timeInfoReader,
appStartTimeSaver: sdkStateStorage,
logger: logger,
settingProvider: skdSettingsStorage,
sessionInfoReader: sessionInfoStorage)
}
private var sequence: [InitTaskCategory] {
@ -137,17 +194,29 @@ extension UnityAdsServiceProvider {
var initTaskFactory: InitializationTaskFactoryStrategy {
.init(downloaderBuilder: networkServicesFactory.webViewDownloaderBuilder,
metricSenderProvider: networkServicesFactory,
metricSenderProvider: eventsNetworkServicesFactory,
networkSenderProvider: networkServicesFactory,
sdkStateStorage: sdkStateStorage,
performanceMeasurer: performanceMeasurer,
stateFactoryObjc: legacyStateFactory,
settingsProvider: skdSettingsStorage)
settingsProvider: skdSettingsStorage,
keyValueStorage: jsonStorageObjCBridge,
cleanupKeys: [JSONStorageKeys.GameSessionID],
deviceInfoReader: deviceInfoReaderBuilder.deviceInfoBodyReader)
}
}
private func defaultDeviceInfoReader(withLogger logger: Logger) -> DeviceInfoReader & LegacyDeviceInfoReader {
let webUserAgent = WebUserAgentReaderBase(lastKnownOSKey: Constants.UserDefaultsKeys.LastKnownSystemVersion,
userAgentValueKey: Constants.UserDefaultsKeys.LastKnownUserAgentKey)
return DeviceInfoReaderBase(logger: logger, userAgentReader: webUserAgent)
extension SessionInfoStorage.Settings {
static func defaultSettings(privateStorage: KeyValueStorage,
idGenerator: IdentifiersGenerator = IdentifiersGeneratorBase()) -> Self {
.init(privateStorage: privateStorage,
gameSessionIDKey: JSONStorageKeys.GameSessionID,
sessionIDKey: JSONStorageKeys.SessionID,
userIDKey: JSONStorageKeys.UserID,
idfiIDKey: JSONStorageKeys.IDFI,
auIDKey: JSONStorageKeys.AUID,
userNonBehavioralFlagKey: JSONStorageKeys.UserNonBehavioralValue,
idGenerator: idGenerator)
}
}

Просмотреть файл

@ -2,6 +2,7 @@ disabled_rules: # rule identifiers to exclude from running
- generic_type_name
- identifier_name
- discouraged_direct_init
- blanket_disable_command
# - type_name
opt_in_rules: # some rules are only opt-in
- empty_count
@ -26,14 +27,13 @@ opt_in_rules: # some rules are only opt-in
- multiline_function_chains
- multiline_parameters
- number_separator
- unused_import
- unused_capture_list
- unused_declaration
- unused_setter_value
- yoda_condition
analyzer_rules: # Rules run by `swiftlint analyze` (experimental)
- explicit_self
- unused_import
- unused_declaration
# configurable rules can be customized from this configuration file
# binary rules can set their severity level
nesting:

Просмотреть файл

@ -4,6 +4,7 @@
#import "USRVWebViewApp.h"
#import "USRVWebViewAsyncOperationStorage.h"
#import "USRVWebViewAsyncOperation.h"
#import "UADSServiceProviderContainer.h"
@class UnityAds;
@class USRVSdkProperties;
@ -18,7 +19,16 @@
[USRVWebViewApp setCurrentApp: nil];
[USRVWebViewAsyncOperationStorage.sharedInstance resetForTesting];
[USRVWebViewAsyncOperation signalLock];
[USRVInvocation setClassTable: [[USRVConfiguration new] getWebAppApiClassList]];
[self deleteConfigFile];
[UADSServiceProviderContainer sharedInstance].serviceProvider = [UADSServiceProvider new];
}
+ (void)deleteConfigFile {
NSString *fileName = [USRVSdkProperties getLocalConfigFilepath];
[[NSFileManager defaultManager] removeItemAtPath: fileName
error: nil];
}
@end

Просмотреть файл

@ -29,6 +29,7 @@ static NSString *const kUnityAdsWebViewMockURL = @"https://webview.unityads.unit
@implementation InitializeTests
- (void)setUp {
UADSServiceProviderContainer.sharedInstance.serviceProvider = [UADSServiceProvider new];
[self cleanupCache];
[super setUp];
}

Просмотреть файл

@ -40,13 +40,13 @@ final class InitializationPerformanceTest: SDKInitializerLegacyIntegrationTestsB
}
func test_get_token_during_swift_new_task_initializing() throws {
try run_get_token_during_initializing(with: ["s_init": true, "s_ntf": true],
try run_get_token_during_initializing(with: ["s_init": true, "s_ntf": true, "s_din": true],
metrics: ExpectedMetrics.SequentialFlow.HappyPath,
expectedDiagnostic: true)
}
func test_get_token_during_swift_parallel_initializing() throws {
try run_get_token_during_initializing(with: ["s_init": true, "s_pte": true],
try run_get_token_during_initializing(with: ["s_init": true, "s_pte": true, "s_din": true],
metrics: ExpectedMetrics.ParallelFlow.HappyPath,
expectedDiagnostic: true)
}
@ -59,7 +59,7 @@ final class InitializationPerformanceTest: SDKInitializerLegacyIntegrationTestsB
var metrics = metrics
metrics.append(contentsOf: [
.legacy(.nativeTokenAvailable),
.legacy(.latency(.intoCollection)),
.legacy(.latency(.infoCollection)),
.legacy(.latency(.infoCompression))])
try? runFlow(sdkMetrics: metrics,
experiments: experiments,
@ -81,7 +81,8 @@ final class InitializationPerformanceTest: SDKInitializerLegacyIntegrationTestsB
try? runFlow(sdkMetrics: ExpectedMetrics.LegacyFlow.HappyPath,
experiments: [:],
expectNetworkDiagnostic: false,
legacyFlow: true)
legacyFlow: true,
validateTimeStamp: false)
}
}
@ -99,7 +100,7 @@ final class InitializationPerformanceTest: SDKInitializerLegacyIntegrationTestsB
measure(metrics: metrics,
options: testOptions) {
try? runFlow(sdkMetrics: ExpectedMetrics.SequentialFlow.HappyPath,
experiments: ["s_init": true, "s_ntf": true],
experiments: ["s_init": true, "s_ntf": true, "s_din": true],
expectNetworkDiagnostic: true)
}
@ -109,7 +110,7 @@ final class InitializationPerformanceTest: SDKInitializerLegacyIntegrationTestsB
measure(metrics: metrics,
options: testOptions) {
try? runFlow(sdkMetrics: ExpectedMetrics.ParallelFlow.HappyPath,
experiments: ["s_init": true, "s_pte": true],
experiments: ["s_init": true, "s_pte": true, "s_din": true],
expectNetworkDiagnostic: true)
}
}
@ -133,6 +134,7 @@ extension InitializationPerformanceTest {
expectNetworkDiagnostic: Bool,
legacyFlow: Bool = false,
parallelToInit: VoidClosure? = nil,
validateTimeStamp: Bool = true,
line: UInt = #line,
file: StaticString = #file) throws {
let overrideJson = ["hash": configMockFactory.longWebViewDataDataHash]
@ -154,7 +156,8 @@ extension InitializationPerformanceTest {
expectedNumberOfRequests: responses.count,
multithreadCount: 1,
metrics: sdkMetrics,
expectDiagnostic: expectNetworkDiagnostic)
expectDiagnostic: expectNetworkDiagnostic,
validateStartTimeStamp: validateTimeStamp)
try executeTest(with: testConfig,
file: file,
line: line,

Просмотреть файл

@ -44,10 +44,6 @@
#import "UADSTsiMetric.h"
#import "UADSPrivacyMetrics.h"
#import "USRVApiSdk.h"
#import "UADSInstallationIdExtension.h"
#import "UADSPlist.h"
#import "UADSUnityPlayerPrefsStore.h"
#import "UADSUserDefaultsStore.h"
#import "USRVInitializeStateCreate.h"
#import "USRVInitializeStateReset.h"
#import "USRVInitializeStateLoadCache.h"
@ -58,7 +54,11 @@
#import "USRVInitializeStateLoadConfigFile.h"
#import "USRVInitializeStateLoadWeb.h"
#import "UADSInitializeEventsMetricSender.h"
#import "USRVJsonStorageAggregator.h"
#import "USRVStorageManager.h"
// TESTING SPECIFIC
#import "UnityAds+Testability.h"
#import "TestUtilities.h"
#import "USRVWebViewCallbackMock.h"
#import "UADSDeviceTestsHelper.h"

Просмотреть файл

@ -12,9 +12,9 @@
return self.expectedMode;
}
- (BOOL)userNonBehavioralFlag {
- (NSNumber *)userNonBehavioralFlag {
_userBehavioralCount += 1;
return self.expectedUserBehaviouralFlag ? : false;
return self.expectedUserBehaviouralFlag ? @(self.expectedUserBehaviouralFlag) : nil;
}
@end

Просмотреть файл

@ -44,6 +44,15 @@
errorPrivacy: self.privacyErrorMock];
}
- (void)test_game_disabled_error_doesnt_call_loader_and_saver {
[self runFlowWithPrivacyCodeAndValidate: 0
allowTrackingFlag: false
expectedSaverCalls: 0
privacyCalls: 1
loaderCalled: 0
errorPrivacy: uads_privacyGameDisabledError];
}
- (void)test_privacy_error_doesnt_block_loader_request_but_calls_saver {
[self runFlowWithPrivacyCodeAndValidate: 0
allowTrackingFlag: false

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше