diff --git a/Framework/Info.plist b/Framework/Info.plist
index b09c7d99..8104ce1c 100644
--- a/Framework/Info.plist
+++ b/Framework/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 5.9.0
+ 5.10.0
CFBundleSignature
????
CFBundleVersion
diff --git a/Podfile.lock b/Podfile.lock
index 056f105b..14bf8490 100644
--- a/Podfile.lock
+++ b/Podfile.lock
@@ -104,4 +104,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 578415250db59f137d92c9b1ce32646ae18974ba
-COCOAPODS: 1.13.0
+COCOAPODS: 1.15.2
diff --git a/Qonversion.podspec b/Qonversion.podspec
index de387663..4e78588e 100644
--- a/Qonversion.podspec
+++ b/Qonversion.podspec
@@ -3,7 +3,7 @@ Pod::Spec.new do |s|
idfa_exclude_files = ['Sources/Qonversion/IDFA']
s.name = 'Qonversion'
s.swift_version = '5.5'
- s.version = '5.9.0'
+ s.version = '5.10.0'
s.summary = 'qonversion.io'
s.description = <<-DESC
Deep Analytics for iOS Subscriptions
diff --git a/Sources/Qonversion/Public/QONConfiguration.m b/Sources/Qonversion/Public/QONConfiguration.m
index 9f71dce4..307cdbf0 100644
--- a/Sources/Qonversion/Public/QONConfiguration.m
+++ b/Sources/Qonversion/Public/QONConfiguration.m
@@ -9,7 +9,7 @@
#import "QONConfiguration.h"
#import "QNAPIConstants.h"
-static NSString *const kSDKVersion = @"5.9.0";
+static NSString *const kSDKVersion = @"5.10.0";
@interface QONConfiguration ()
diff --git a/Sources/Qonversion/Public/Qonversion.h b/Sources/Qonversion/Public/Qonversion.h
index 959fe78d..4f41ba26 100644
--- a/Sources/Qonversion/Public/Qonversion.h
+++ b/Sources/Qonversion/Public/Qonversion.h
@@ -128,7 +128,7 @@ static NSString *const QonversionApiErrorDomain = @"com.qonversion.io.api";
@param data Dictionary received by the provider
@param provider Attribution provider
*/
-- (void)attribution:(NSDictionary *)data fromProvider:(QONAttributionProvider)provider;
+- (void)attribution:(NSDictionary *)data fromProvider:(QONAttributionProvider)provider DEPRECATED_MSG_ATTRIBUTE("This function shouldn't be called anymore. All attribution logic continues to work as usual.");
/**
Check user entitlements
diff --git a/Sources/Qonversion/Public/Qonversion.m b/Sources/Qonversion/Public/Qonversion.m
index c1112d9f..4334bd68 100644
--- a/Sources/Qonversion/Public/Qonversion.m
+++ b/Sources/Qonversion/Public/Qonversion.m
@@ -138,7 +138,7 @@ - (void)setPromoPurchasesDelegate:(id)delegate {
}
- (void)attribution:(NSDictionary *)data fromProvider:(QONAttributionProvider)provider {
- [self.attributionManager addAttributionData:data fromProvider:provider];
+ // This function logic is deprecated and is not needed anymore
}
- (void)setUserProperty:(QONUserPropertyKey)key value:(NSString *)value {
@@ -258,7 +258,7 @@ - (instancetype)initWithCustomUserDefaults:(NSUserDefaults *)userDefaults {
_productCenterManager.remoteConfigManager = _remoteConfigManager;
_remoteConfigManager.productCenterManager = _productCenterManager;
-
+ _remoteConfigManager.userPropertiesManager = _propertiesManager;
_debugMode = NO;
}
diff --git a/Sources/Qonversion/Qonversion/Main/QNAttributionManager/QNAttributionManager.h b/Sources/Qonversion/Qonversion/Main/QNAttributionManager/QNAttributionManager.h
index 4d2fe199..94b018aa 100644
--- a/Sources/Qonversion/Qonversion/Main/QNAttributionManager/QNAttributionManager.h
+++ b/Sources/Qonversion/Qonversion/Main/QNAttributionManager/QNAttributionManager.h
@@ -4,7 +4,6 @@ NS_ASSUME_NONNULL_BEGIN
@interface QNAttributionManager : NSObject
-- (void)addAttributionData:(NSDictionary *)data fromProvider:(NSInteger)provider;
- (void)addAppleSearchAttributionData;
@end
diff --git a/Sources/Qonversion/Qonversion/Main/QNAttributionManager/QNAttributionManager.m b/Sources/Qonversion/Qonversion/Main/QNAttributionManager/QNAttributionManager.m
index 5f9aa2f5..a20a85d4 100644
--- a/Sources/Qonversion/Qonversion/Main/QNAttributionManager/QNAttributionManager.m
+++ b/Sources/Qonversion/Qonversion/Main/QNAttributionManager/QNAttributionManager.m
@@ -23,20 +23,6 @@ - (instancetype)init
return self;
}
-- (void)addAttributionData:(NSDictionary *)data fromProvider:(NSInteger)provider {
- double delayInSeconds = 5.0;
- dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
-
- dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
-
- [self.client attributionRequest:provider data:data completion:^(NSDictionary * _Nullable dict, NSError * _Nullable error) {
- if (dict && [dict respondsToSelector:@selector(valueForKey:)]) {
- QONVERSION_LOG(@"Attribution Request Log Response:\n%@", dict);
- }
- }];
- });
-}
-
- (void)addAppleSearchAttributionData {
double delayInSeconds = 5.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
diff --git a/Sources/Qonversion/Qonversion/Main/QNUserPropertiesManager/QNUserPropertiesManager.h b/Sources/Qonversion/Qonversion/Main/QNUserPropertiesManager/QNUserPropertiesManager.h
index 5b7b5a9b..a3ba2aa7 100644
--- a/Sources/Qonversion/Qonversion/Main/QNUserPropertiesManager/QNUserPropertiesManager.h
+++ b/Sources/Qonversion/Qonversion/Main/QNUserPropertiesManager/QNUserPropertiesManager.h
@@ -5,14 +5,17 @@
NS_ASSUME_NONNULL_BEGIN
+typedef void (^QONUserPropertiesEmptyCompletionHandler)(void) NS_SWIFT_NAME(Qonversion.UserPropertiesEmptyCompletionHandler);
+
@interface QNUserPropertiesManager : NSObject
@property (nonatomic, strong) QNProductCenterManager *productCenterManager;
@property (nonatomic, strong) QONUserPropertiesMapper *mapper;
- (void)setUserProperty:(NSString *)property value:(NSString *)value;
-
- (void)getUserProperties:(QONUserPropertiesCompletionHandler)completion;
+- (void)forceSendProperties:(QONUserPropertiesEmptyCompletionHandler)completion;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/Sources/Qonversion/Qonversion/Main/QNUserPropertiesManager/QNUserPropertiesManager.m b/Sources/Qonversion/Qonversion/Main/QNUserPropertiesManager/QNUserPropertiesManager.m
index f6b4ab93..6a95b294 100644
--- a/Sources/Qonversion/Qonversion/Main/QNUserPropertiesManager/QNUserPropertiesManager.m
+++ b/Sources/Qonversion/Qonversion/Main/QNUserPropertiesManager/QNUserPropertiesManager.m
@@ -26,6 +26,8 @@ @interface QNUserPropertiesManager()
@property (nonatomic) QNInMemoryStorage *inMemoryStorage;
+@property (nonatomic, strong) NSMutableArray *completionBlocks;
+
@property (nonatomic, assign, readwrite) BOOL sendingScheduled;
@property (nonatomic, assign, readwrite) BOOL updatingCurrently;
@property (nonatomic, assign, readwrite) NSUInteger retryDelay;
@@ -46,7 +48,9 @@ - (instancetype)init {
[_backgroundQueue setSuspended:NO];
_apiClient = [QNAPIClient shared];
_mapper = [QONUserPropertiesMapper new];
-
+
+ _completionBlocks = [NSMutableArray new];
+
_backgroundQueue.name = kBackgroundQueueName;
_device = QNDevice.current;
_retryDelay = kQPropertiesSendingPeriodInSeconds;
@@ -61,10 +65,8 @@ - (instancetype)init {
- (void)setUserProperty:(NSString *)property value:(NSString *)value {
if ([QNProperties checkProperty:property] && [QNProperties checkValue:value]) {
- [self runOnBackgroundQueue:^{
- [self->_inMemoryStorage storeObject:value forKey:property];
- [self sendPropertiesWithDelay:self.retryDelay];
- }];
+ [self.inMemoryStorage storeObject:value forKey:property];
+ [self sendPropertiesWithDelay:self.retryDelay];
}
}
@@ -86,6 +88,17 @@ - (void)enterBackground {
[self sendPropertiesInBackground];
}
+- (void)forceSendProperties:(QONUserPropertiesEmptyCompletionHandler)completion {
+ if (self.inMemoryStorage.storageDictionary.count == 0) {
+ completion();
+ return;
+ }
+
+ [self.completionBlocks addObject:completion];
+
+ [self sendProperties:YES];
+}
+
- (void)sendPropertiesWithDelay:(NSUInteger)delay {
if (!_sendingScheduled) {
_sendingScheduled = YES;
@@ -104,13 +117,17 @@ - (void)sendPropertiesInBackground {
}
- (void)sendProperties {
+ [self sendProperties:NO];
+}
+
+- (void)sendProperties:(BOOL)force {
if ([QNUtils isEmptyString:_apiClient.apiKey]) {
QONVERSION_ERROR(@"ERROR: apiKey cannot be nil or empty, set apiKey with launchWithKey:");
return;
}
@synchronized (self) {
- if (_updatingCurrently) {
+ if (_updatingCurrently && !force) {
return;
}
_updatingCurrently = YES;
@@ -129,12 +146,30 @@ - (void)sendProperties {
return;
}
+ self.inMemoryStorage.storageDictionary = @{};
__block __weak QNUserPropertiesManager *weakSelf = self;
[self.apiClient sendProperties:properties
- completion:^(NSDictionary * _Nullable dict, NSError * _Nullable error) {
+ completion:^(NSDictionary * _Nullable dict, NSError * _Nullable error) {
weakSelf.updatingCurrently = NO;
+ NSArray *completions = [weakSelf.completionBlocks copy];
+ for (QONUserPropertiesEmptyCompletionHandler storedCompletion in completions) {
+ storedCompletion();
+ }
+
+ [weakSelf.completionBlocks removeAllObjects];
+
if (error) {
+ // copy of an existing array to prevent erasing properties set while the current request is in progress
+ NSMutableDictionary *allProperties = [self.inMemoryStorage.storageDictionary mutableCopy];
+ for (NSString *key in properties.allKeys) {
+ if (!allProperties[key]) {
+ allProperties[key] = properties[key];
+ }
+ }
+
+ self.inMemoryStorage.storageDictionary = [allProperties copy];
+
if ([error.domain isEqualToString:QonversionApiErrorDomain] && error.code == QONAPIErrorInvalidClientUID) {
[weakSelf.productCenterManager launchWithCompletion:^(QONLaunchResult * _Nonnull result, NSError * _Nullable error) {
[weakSelf retryProperties];
@@ -145,7 +180,6 @@ - (void)sendProperties {
} else {
weakSelf.retryDelay = kQPropertiesSendingPeriodInSeconds;
weakSelf.retriesCounter = 0;
- [weakSelf clearProperties:properties];
}
}];
}];
@@ -153,12 +187,12 @@ - (void)sendProperties {
- (void)getUserProperties:(QONUserPropertiesCompletionHandler)completion {
[self.apiClient getProperties:^(NSArray * _Nullable array, NSError * _Nullable error) {
- if (error) {
- completion(nil, error);
- } else {
- QONUserProperties *properties = [self.mapper mapUserProperties:array];
- completion(properties, nil);
- }
+ if (error) {
+ completion(nil, error);
+ } else {
+ QONUserProperties *properties = [self.mapper mapUserProperties:array];
+ completion(properties, nil);
+ }
}];
}
@@ -170,18 +204,6 @@ - (void)retryProperties {
[self sendPropertiesWithDelay:self.retryDelay];
}
-- (void)clearProperties:(NSDictionary *)properties {
- [self runOnBackgroundQueue:^{
- if (!properties || ![properties respondsToSelector:@selector(valueForKey:)]) {
- return;
- }
-
- for (NSString *key in properties.allKeys) {
- [self->_inMemoryStorage removeObjectForKey:key];
- }
- }];
-}
-
- (BOOL)runOnBackgroundQueue:(void (^)(void))block {
if ([[NSOperationQueue currentQueue].name isEqualToString:kBackgroundQueueName]) {
QONVERSION_LOG(@"Already running in the background.");
diff --git a/Sources/Qonversion/Qonversion/Main/QONRemoteConfigManager/QONRemoteConfigManager.h b/Sources/Qonversion/Qonversion/Main/QONRemoteConfigManager/QONRemoteConfigManager.h
index 610e37e6..95d64d2a 100644
--- a/Sources/Qonversion/Qonversion/Main/QONRemoteConfigManager/QONRemoteConfigManager.h
+++ b/Sources/Qonversion/Qonversion/Main/QONRemoteConfigManager/QONRemoteConfigManager.h
@@ -10,7 +10,7 @@
#import "QONLaunchResult.h"
#import "QONExperiment.h"
-@class QONRemoteConfigService, QNProductCenterManager;
+@class QONRemoteConfigService, QNProductCenterManager, QNUserPropertiesManager;
NS_ASSUME_NONNULL_BEGIN
@@ -18,6 +18,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, strong) QONRemoteConfigService *remoteConfigService;
@property (nonatomic, strong) QNProductCenterManager *productCenterManager;
+@property (nonatomic, strong) QNUserPropertiesManager *userPropertiesManager;
- (void)userChangingRequestFailedWithError:(NSError *)error;
- (void)handlePendingRequests;
diff --git a/Sources/Qonversion/Qonversion/Main/QONRemoteConfigManager/QONRemoteConfigManager.m b/Sources/Qonversion/Qonversion/Main/QONRemoteConfigManager/QONRemoteConfigManager.m
index 820f8ec6..220407eb 100644
--- a/Sources/Qonversion/Qonversion/Main/QONRemoteConfigManager/QONRemoteConfigManager.m
+++ b/Sources/Qonversion/Qonversion/Main/QONRemoteConfigManager/QONRemoteConfigManager.m
@@ -14,6 +14,7 @@
#import "QNProductCenterManager.h"
#import "QONRemoteConfigLoadingState.h"
#import "QONRemoteConfigListRequestData.h"
+#import "QNUserPropertiesManager.h"
static NSString *const kEmptyContextKey = @"";
@@ -91,18 +92,22 @@ - (void)obtainRemoteConfigWithContextKey:(NSString * _Nullable)contextKey comple
}
loadingState.isInProgress = YES;
+
__block __weak QONRemoteConfigManager *weakSelf = self;
- [self.remoteConfigService loadRemoteConfig:contextKey completion:^(QONRemoteConfig * _Nullable remoteConfig, NSError * _Nullable error) {
- loadingState.isInProgress = NO;
- if (error) {
- [weakSelf executeRemoteConfigCompletionsWithContextKey:contextKey remoteConfig:nil error:error];
- completion(nil, error);
- return;
- }
-
- loadingState.loadedConfig = remoteConfig;
- [weakSelf executeRemoteConfigCompletionsWithContextKey:contextKey remoteConfig:remoteConfig error:nil];
- completion(remoteConfig, nil);
+
+ [self.userPropertiesManager forceSendProperties:^{
+ [weakSelf.remoteConfigService loadRemoteConfig:contextKey completion:^(QONRemoteConfig * _Nullable remoteConfig, NSError * _Nullable error) {
+ loadingState.isInProgress = NO;
+ if (error) {
+ [weakSelf executeRemoteConfigCompletionsWithContextKey:contextKey remoteConfig:nil error:error];
+ completion(nil, error);
+ return;
+ }
+
+ loadingState.loadedConfig = remoteConfig;
+ [weakSelf executeRemoteConfigCompletionsWithContextKey:contextKey remoteConfig:remoteConfig error:nil];
+ completion(remoteConfig, nil);
+ }];
}];
}
@@ -134,8 +139,12 @@ - (void)obtainRemoteConfigListWithContextKeys:(NSArray *)contextKeys
return;
}
- QONRemoteConfigListCompletionHandler completionWrapper = [self remoteConfigListCompletionWrapper:completion];
- [self.remoteConfigService loadRemoteConfigList:contextKeys includeEmptyContextKey:includeEmptyContextKey completion:completionWrapper];
+ __block __weak QONRemoteConfigManager *weakSelf = self;
+
+ [self.userPropertiesManager forceSendProperties:^{
+ QONRemoteConfigListCompletionHandler completionWrapper = [weakSelf remoteConfigListCompletionWrapper:completion];
+ [weakSelf.remoteConfigService loadRemoteConfigList:contextKeys includeEmptyContextKey:includeEmptyContextKey completion:completionWrapper];
+ }];
}
- (void)obtainRemoteConfigList:(QONRemoteConfigListCompletionHandler)completion {
@@ -147,8 +156,12 @@ - (void)obtainRemoteConfigList:(QONRemoteConfigListCompletionHandler)completion
return;
}
- QONRemoteConfigListCompletionHandler completionWrapper = [self remoteConfigListCompletionWrapper:completion];
- [self.remoteConfigService loadRemoteConfigList:completionWrapper];
+ __block __weak QONRemoteConfigManager *weakSelf = self;
+
+ [self.userPropertiesManager forceSendProperties:^{
+ QONRemoteConfigListCompletionHandler completionWrapper = [weakSelf remoteConfigListCompletionWrapper:completion];
+ [weakSelf.remoteConfigService loadRemoteConfigList:completionWrapper];
+ }];
}
- (void)attachUserToExperiment:(NSString *)experimentId groupId:(NSString *)groupId completion:(QONExperimentAttachCompletionHandler)completion {
diff --git a/fastlane/report.xml b/fastlane/report.xml
index 3a8f4d99..a012561d 100644
--- a/fastlane/report.xml
+++ b/fastlane/report.xml
@@ -5,12 +5,12 @@
-
+
-
+