diff --git a/AdjustAdobeExtension.podspec b/AdjustAdobeExtension.podspec index 7c18bbd..96b1c66 100644 --- a/AdjustAdobeExtension.podspec +++ b/AdjustAdobeExtension.podspec @@ -1,15 +1,15 @@ Pod::Spec.new do |s| s.name = 'AdjustAdobeExtension' - s.version = '1.0.4' + s.version = '1.1.0' s.summary = 'Adjust SDK extension for Adobe Experience Platform.' s.description = <<-DESC A leading attribution solution that brings the full power of mobile ad measurement to your campaigns. DESC s.homepage = 'https://github.com/adjust/ios_adobe_extension' s.license = { :type => 'MIT', :file => 'LICENSE' } - s.author = { 'Adjust SDK Team' => 'sdk@adjust.com' } - s.source = { :git => 'https://github.com/adjust/ios_adobe_extension.git', :tag => "v1.0.4" } + s.author = { 'Adjust' => 'sdk@adjust.com' } + s.source = { :git => 'https://github.com/adjust/ios_adobe_extension.git', :tag => "v1.1.0" } s.ios.deployment_target = '10.0' @@ -19,6 +19,6 @@ A leading attribution solution that brings the full power of mobile ad measureme s.pod_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64'} s.user_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64'} - s.dependency 'Adjust', '4.30.0' + s.dependency 'Adjust', '4.31.0' s.dependency 'ACPCore' end diff --git a/AdjustAdobeExtension/Classes/AdjustAdobeExtension.h b/AdjustAdobeExtension/Classes/AdjustAdobeExtension.h index 8796518..31a2c95 100644 --- a/AdjustAdobeExtension/Classes/AdjustAdobeExtension.h +++ b/AdjustAdobeExtension/Classes/AdjustAdobeExtension.h @@ -20,11 +20,22 @@ NS_ASSUME_NONNULL_BEGIN +extern NSString * const ADJAdobeExtensionLogTag; +extern NSString * const ADJAdobeExtensionSdkPrefix; + +// Action types +extern NSString * const ADJAdobeAdjustActionTrackEvent; +extern NSString * const ADJAdobeAdjustActionSetPushToken; + +// Adjust Event extern NSString * const ADJAdobeAdjustEventToken; extern NSString * const ADJAdobeAdjustEventCurrency; extern NSString * const ADJAdobeAdjustEventRevenue; -extern NSString * const ADJAdobeExtensionLogTag; -extern NSString * const ADJAdobeExtensionSdkPrefix; +extern NSString * const ADJAdobeAdjustEventCallbackParamPrefix; +extern NSString * const ADJAdobeAdjustEventPartnerParamPrefix; + +// Push token +extern NSString * const ADJAdobeAdjustPushToken; @interface AdjustAdobeExtension : ACPExtension diff --git a/AdjustAdobeExtension/Classes/AdjustAdobeExtension.m b/AdjustAdobeExtension/Classes/AdjustAdobeExtension.m index 5cf4930..a0c2079 100644 --- a/AdjustAdobeExtension/Classes/AdjustAdobeExtension.m +++ b/AdjustAdobeExtension/Classes/AdjustAdobeExtension.m @@ -11,23 +11,72 @@ #import "AdjustAdobeExtensionEventListener.h" NSString * const ADJAdobeExtensionLogTag = @"AdjustAdobeExtension"; -NSString * const ADJAdobeExtensionSdkPrefix = @"adobe_ext1.0.4"; +NSString * const ADJAdobeExtensionName = @"com.adjust.adobeextension"; +NSString * const ADJAdobeExtensionSdkPrefix = @"adobe_ext1.1.0"; +NSString * const ADJAdobeEventDataKeyAction = @"action"; +NSString * const ADJAdobeEventDataKeyContextData = @"contextdata"; +// Action types +NSString * const ADJAdobeAdjustActionTrackEvent = @"adj.trackEvent"; +NSString * const ADJAdobeAdjustActionSetPushToken = @"adj.setPushToken"; + +// Adjust Event NSString * const ADJAdobeAdjustEventToken = @"adj.eventToken"; NSString * const ADJAdobeAdjustEventCurrency = @"adj.currency"; NSString * const ADJAdobeAdjustEventRevenue = @"adj.revenue"; +NSString * const ADJAdobeAdjustEventCallbackParamPrefix = @"adj.event.callback."; +NSString * const ADJAdobeAdjustEventPartnerParamPrefix = @"adj.event.partner."; + +// Push token +NSString * const ADJAdobeAdjustPushToken = @"adj.pushToken"; +char * const kQUEUE_ID_SYNC = "com.adjust.AdjustAdobeExtension.sync_queue"; static AdjustAdobeExtensionConfig *_configInstance = nil; @interface AdjustAdobeExtension() @property (nonatomic, assign) BOOL sdkInitialized; @property (nonatomic, strong) NSMutableArray *receivedEvents; +@property (nonatomic, strong) dispatch_queue_t syncQueue; +@property (nonatomic,strong) NSNumber *numNan; +@property (nonatomic,strong) NSNumber *numPlusInf; +@property (nonatomic,strong) NSNumber *numMinusInf; @end @implementation AdjustAdobeExtension ++ (void)registerExtensionWithConfig:(AdjustAdobeExtensionConfig *)config { + if (config == nil) { + [ACPCore log:ACPMobileLogLevelError + tag:ADJAdobeExtensionLogTag + message:@"AdjustExtension registration error: config is nil!"]; + return; + } + + if (config.environment == nil || config.environment.length == 0) { + [ACPCore log:ACPMobileLogLevelError + tag:ADJAdobeExtensionLogTag + message:@"AdjustExtension registration error: Environment is empty! Use ADJEnvironmentSandbox or ADJEnvironmentProduction."]; + return; + } + + NSError *error = nil; + if ([ACPCore registerExtension:[AdjustAdobeExtension class] error:&error]) { + [ACPCore log:ACPMobileLogLevelDebug + tag:ADJAdobeExtensionLogTag + message:@"Successfully registered Adjust Extension"]; + _configInstance = config; + } else { + [ACPCore log:ACPMobileLogLevelError + tag:ADJAdobeExtensionLogTag + message:[NSString stringWithFormat: + @"An error occured while registering Adjust Extension: %@", + (error) ? [NSString stringWithFormat:@"Code: %ld, Domain: %@, Description: %@.", (long)error.code, error.domain, error.localizedDescription ] : + @"Unknown error."]]; + } +} + - (instancetype)init { self = [super init]; if (self == nil) { @@ -36,157 +85,188 @@ - (instancetype)init { _sdkInitialized = NO; _receivedEvents = [NSMutableArray array]; + _syncQueue = dispatch_queue_create(kQUEUE_ID_SYNC, DISPATCH_QUEUE_SERIAL); - NSError *error = nil; + double dblZero = 0.0; + double dblPlusOne = 1.0; + double dblMinusOne = -1.0; + _numPlusInf = [NSNumber numberWithDouble:dblPlusOne/dblZero]; + _numMinusInf = [NSNumber numberWithDouble:dblMinusOne/dblZero]; + _numNan = [NSNumber numberWithDouble:sqrt(dblMinusOne)]; + NSError *error = nil; // Shared State listener if ([self.api registerListener:[AdjustAdobeExtensionSharedStateListener class] - eventType:@"com.adobe.eventType.hub" - eventSource:@"com.adobe.eventSource.sharedState" + eventType:ADJAdobeEventTypeHub + eventSource:ADJAdobeEventSourceSharedState error:&error]) { - [ACPCore log:ACPMobileLogLevelDebug tag:ADJAdobeExtensionLogTag - message:@"successfully registered for Event Hub Shared State events"]; - } else if (error) { - [ACPCore log:ACPMobileLogLevelError + [ACPCore log:ACPMobileLogLevelDebug tag:ADJAdobeExtensionLogTag - message:[NSString stringWithFormat: - @"An error occured while" - " registering AdjustAdobeExtensionSharedStateListener, error code: %ld", - (long)[error code]]]; + message:@"Successfully registered Extension Listener for Event Hub Shared State events."]; } else { [ACPCore log:ACPMobileLogLevelError tag:ADJAdobeExtensionLogTag message:[NSString stringWithFormat: - @"An error occured while" - " registering AdjustAdobeExtensionSharedStateListener, without error code"]]; + @"An error occured while registering Extension Listener" + " for Event Hub Shared State events. %@", + (error) ? [NSString stringWithFormat:@"Code: %ld, Domain: %@, Description: %@.", (long)error.code, error.domain, error.localizedDescription ] : + @"Unknown error."]]; } + // Events listener if ([self.api registerListener:[AdjustAdobeExtensionEventListener class] - eventType:@"com.adobe.eventType.generic.track" - eventSource:@"com.adobe.eventSource.requestContent" + eventType:ADJAdobeEventTypeGenericTrack + eventSource:ADJAdobeEventSourceRequestContent error:&error]) { - [ACPCore log:ACPMobileLogLevelDebug tag:ADJAdobeExtensionLogTag - message:@"Successfully registered for Extension Request Content events"]; - } else if (error) { - [ACPCore log:ACPMobileLogLevelError tag:ADJAdobeExtensionLogTag - message:[NSString stringWithFormat: - @"There was an error registering ExtensionListener" - " for Extension Request Content events: %@", - error.localizedDescription ?: @"unknown"]]; + [ACPCore log:ACPMobileLogLevelDebug + tag:ADJAdobeExtensionLogTag + message:@"Successfully registered Extension Listener for Extension Request Content events"]; } else { - [ACPCore log:ACPMobileLogLevelError tag:ADJAdobeExtensionLogTag + [ACPCore log:ACPMobileLogLevelError + tag:ADJAdobeExtensionLogTag message:[NSString stringWithFormat: - @"There was an error registering ExtensionListener" - " for Extension Request Content events, without error code"]]; + @"An error occured while registering Extension Listener" + " for Extension Request Content events: %@", + (error) ? [NSString stringWithFormat:@"Code: %ld, Domain: %@, Description: %@.", (long)error.code, error.domain, error.localizedDescription ] : + @"Unknown error."]]; } return self; } -- (void)handleEventData:(nullable NSDictionary *)eventData { - if (eventData == nil) { - return; - } - - if (!self.sdkInitialized) { - [self.receivedEvents addObject:eventData]; - return; - } - - NSDictionary *contextdata = eventData[@"contextdata"]; - if (contextdata == nil) { - return; - } +- (void)setupAdjustWithAppToken:(NSString *)appToken trackAttribution:(BOOL)trackAttribution { + dispatch_async(self.syncQueue, ^{ + if (!_configInstance) { + [ACPCore log:ACPMobileLogLevelError + tag:ADJAdobeExtensionLogTag + message:@"Extension should be registered first!"]; + return; + } - NSString *adjEventToken = contextdata[ADJAdobeAdjustEventToken]; + if (self.sdkInitialized == YES) { + return; + } - if (adjEventToken == nil) { - return; - } + self.sdkInitialized = YES; + + _configInstance.shouldTrackAttribution = trackAttribution; + ADJConfig *adjustConfig = [ADJConfig configWithAppToken:appToken + environment:_configInstance.environment]; + [adjustConfig setSdkPrefix:ADJAdobeExtensionSdkPrefix]; + [adjustConfig setDelegate:self]; + + switch ([ACPCore logLevel]) { + case ACPMobileLogLevelError: + [adjustConfig setLogLevel:ADJLogLevelError]; + break; + case ACPMobileLogLevelWarning: + [adjustConfig setLogLevel:ADJLogLevelWarn]; + break; + case ACPMobileLogLevelDebug: + [adjustConfig setLogLevel:ADJLogLevelDebug]; + break; + case ACPMobileLogLevelVerbose: + [adjustConfig setLogLevel:ADJLogLevelVerbose]; + break; + } - ADJEvent *event = [ADJEvent eventWithEventToken:adjEventToken]; - NSString *currency = contextdata[ADJAdobeAdjustEventCurrency]; - NSNumber *revenue = contextdata[ADJAdobeAdjustEventRevenue]; + [Adjust appDidLaunch:adjustConfig]; - if (currency && revenue) { - [event setRevenue:[revenue doubleValue] currency:currency]; - } + for (NSDictionary *event in self.receivedEvents) { + [self processEvent:event]; + } + [self.receivedEvents removeAllObjects]; + }); +} - [Adjust trackEvent:event]; +- (void)handleEventData:(nullable NSDictionary *)eventData { + dispatch_async(self.syncQueue, ^{ + if (!self.sdkInitialized) { + [self.receivedEvents addObject:eventData]; + } else { + [self processEvent:eventData]; + } + }); } -- (void)setupAdjustWithAppToken:(NSString *)appToken trackAttribution:(BOOL)trackAttribution { - if (!_configInstance) { +- (void)processEvent:(nullable NSDictionary *)eventData { + if (eventData == nil) { [ACPCore log:ACPMobileLogLevelError tag:ADJAdobeExtensionLogTag - message:@"Extension should be registered first"]; + message:@"Extension event error: eventData is nil!"]; return; } - if (self.sdkInitialized) { + NSString *action = eventData[ADJAdobeEventDataKeyAction]; + NSDictionary *contextdata = eventData[ADJAdobeEventDataKeyContextData]; + if (contextdata == nil) { + [ACPCore log:ACPMobileLogLevelError + tag:ADJAdobeExtensionLogTag + message:@"Extension event error: contextdata is nil!"]; return; } - _configInstance.shouldTrackAttribution = trackAttribution; - - ADJConfig *adjustConfig = [ADJConfig configWithAppToken:appToken - environment:_configInstance.environment]; - [adjustConfig setSdkPrefix:ADJAdobeExtensionSdkPrefix]; - [adjustConfig setDelegate:self]; - - switch ([ACPCore logLevel]) { - case ACPMobileLogLevelError: - [adjustConfig setLogLevel:ADJLogLevelError]; - break; - case ACPMobileLogLevelWarning: - [adjustConfig setLogLevel:ADJLogLevelWarn]; - break; - case ACPMobileLogLevelDebug: - [adjustConfig setLogLevel:ADJLogLevelDebug]; - break; - case ACPMobileLogLevelVerbose: - [adjustConfig setLogLevel:ADJLogLevelVerbose]; - break; + if (action.length > 0 && + [action compare:ADJAdobeAdjustActionSetPushToken options:NSCaseInsensitiveSearch] == NSOrderedSame) { + [self setPushToken:contextdata]; + } else { + [self trackEvent:contextdata]; } +} - [Adjust appDidLaunch:adjustConfig]; - - self.sdkInitialized = YES; - [self dumpReceivedEvents]; +- (void)setPushToken:(NSDictionary *)contextData { + NSString *pushToken = contextData[ADJAdobeAdjustPushToken]; + if (pushToken != nil && pushToken.length > 0) { + [Adjust setPushToken:pushToken]; + } else { + [ACPCore log:ACPMobileLogLevelError + tag:ADJAdobeExtensionLogTag + message:@"PushToken is nil or zero-length!"]; + } } -- (void)dumpReceivedEvents { - if (self.receivedEvents.count <= 0) { +- (void)trackEvent:(NSDictionary *)contextData { + NSString *adjEventToken = contextData[ADJAdobeAdjustEventToken]; + if (adjEventToken == nil) { return; } - // dump events received before initialization - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ - for (NSDictionary *event in self.receivedEvents) { - [self handleEventData:event]; + ADJEvent *event = [ADJEvent eventWithEventToken:adjEventToken]; + NSString *currency = contextData[ADJAdobeAdjustEventCurrency]; + NSString *revenue = contextData[ADJAdobeAdjustEventRevenue]; + + // Revenue data + if (currency != nil && currency.length > 0 && revenue != nil && revenue.length > 0) { + NSNumber *numRevenue = [NSNumber numberWithDouble:[revenue doubleValue]]; + if ([numRevenue isEqualToNumber:self.numNan] || + [numRevenue isEqualToNumber:self.numPlusInf] || + [numRevenue isEqualToNumber:self.numMinusInf]) { + [ACPCore log:ACPMobileLogLevelError + tag:ADJAdobeExtensionLogTag + message:@"Revenue number is malformed!"]; + return; + } else { + [event setRevenue:[numRevenue doubleValue] currency:currency]; } - [self.receivedEvents removeAllObjects]; - }); -} + } -+ (void)registerExtensionWithConfig:(AdjustAdobeExtensionConfig *)config { - NSError *error = nil; - if ([ACPCore registerExtension:[AdjustAdobeExtension class] error:&error]) { - [ACPCore log:ACPMobileLogLevelDebug tag:ADJAdobeExtensionLogTag - message:@"Successfully registered AdjustExtension"]; - _configInstance = config; - } else if (error) { - [ACPCore log:ACPMobileLogLevelError tag:ADJAdobeExtensionLogTag - message:[NSString stringWithFormat:@"Error registering AdjustExtension: %@ %d", - [error domain], (int)[error code]]]; - } else { - [ACPCore log:ACPMobileLogLevelError tag:ADJAdobeExtensionLogTag - message:[NSString stringWithFormat:@"Error registering AdjustExtension, without error code"]]; + // Callback / Partner Params + NSArray *allKeys = contextData.allKeys; + for (NSString *key in allKeys) { + if ([key hasPrefix:ADJAdobeAdjustEventCallbackParamPrefix] == YES) { + NSString *adjKey = [key substringFromIndex:ADJAdobeAdjustEventCallbackParamPrefix.length]; + [event addCallbackParameter:adjKey value:[contextData valueForKey:key]]; + } else if ([key hasPrefix:ADJAdobeAdjustEventPartnerParamPrefix] == YES) { + NSString *adjKey = [key substringFromIndex:ADJAdobeAdjustEventPartnerParamPrefix.length]; + [event addPartnerParameter:adjKey value:[contextData valueForKey:key]]; + } } + + [Adjust trackEvent:event]; } - (nullable NSString *)name { - return @"com.adjust.adobeextension"; + return ADJAdobeExtensionName; } - (nullable NSString *)version { diff --git a/AdjustAdobeExtension/Classes/AdjustAdobeExtensionConfig.h b/AdjustAdobeExtension/Classes/AdjustAdobeExtensionConfig.h index 9d35762..5dd648c 100644 --- a/AdjustAdobeExtension/Classes/AdjustAdobeExtensionConfig.h +++ b/AdjustAdobeExtension/Classes/AdjustAdobeExtensionConfig.h @@ -13,7 +13,6 @@ #import #endif - typedef void (^CallbackAttributionChangedBlock)(ADJAttribution * _Nullable attribution); typedef BOOL (^CallbackDeeplinkResponseBlock)(NSURL * _Nullable deeplink); diff --git a/AdjustAdobeExtension/Classes/AdjustAdobeExtensionEventListener.h b/AdjustAdobeExtension/Classes/AdjustAdobeExtensionEventListener.h index c1de06f..8e261bb 100644 --- a/AdjustAdobeExtension/Classes/AdjustAdobeExtensionEventListener.h +++ b/AdjustAdobeExtension/Classes/AdjustAdobeExtensionEventListener.h @@ -12,6 +12,9 @@ NS_ASSUME_NONNULL_BEGIN +extern NSString * const ADJAdobeEventTypeGenericTrack; +extern NSString * const ADJAdobeEventSourceRequestContent; + @interface AdjustAdobeExtensionEventListener : ACPExtensionListener - (void)hear:(nonnull ACPExtensionEvent *)event; diff --git a/AdjustAdobeExtension/Classes/AdjustAdobeExtensionSharedStateListener.h b/AdjustAdobeExtension/Classes/AdjustAdobeExtensionSharedStateListener.h index 9936045..af4f55b 100644 --- a/AdjustAdobeExtension/Classes/AdjustAdobeExtensionSharedStateListener.h +++ b/AdjustAdobeExtension/Classes/AdjustAdobeExtensionSharedStateListener.h @@ -12,6 +12,9 @@ NS_ASSUME_NONNULL_BEGIN +extern NSString * const ADJAdobeEventTypeHub; +extern NSString * const ADJAdobeEventSourceSharedState; + @interface AdjustAdobeExtensionSharedStateListener : ACPExtensionListener - (void)hear:(ACPExtensionEvent *)event; diff --git a/AdjustAdobeExtension/Classes/AdjustAdobeExtensionSharedStateListener.m b/AdjustAdobeExtension/Classes/AdjustAdobeExtensionSharedStateListener.m index c3ddd0b..eebbc02 100644 --- a/AdjustAdobeExtension/Classes/AdjustAdobeExtensionSharedStateListener.m +++ b/AdjustAdobeExtension/Classes/AdjustAdobeExtensionSharedStateListener.m @@ -11,6 +11,8 @@ #import NSString * const ADJAdobeModuleConfiguration = @"com.adobe.module.configuration"; +NSString * const ADJAdobeEventTypeHub = @"com.adobe.eventType.hub"; +NSString * const ADJAdobeEventSourceSharedState = @"com.adobe.eventSource.sharedState"; NSString * const ADJConfigurationAppToken = @"adjustAppToken"; NSString * const ADJConfigurationTrackAttribution = @"adjustTrackAttribution"; @@ -21,6 +23,9 @@ - (void)hear:(ACPExtensionEvent *)event { NSDictionary *eventData = [event eventData]; if (!eventData) { + [ACPCore log:ACPMobileLogLevelError + tag:ADJAdobeExtensionLogTag + message:@"Extension eventData is nil!"]; return; } if (![eventData[@"stateowner"] isEqualToString:ADJAdobeModuleConfiguration]) { @@ -28,34 +33,45 @@ - (void)hear:(ACPExtensionEvent *)event { } NSError *error = nil; - NSDictionary *configSharedState = - [self.extension.api getSharedEventState:ADJAdobeModuleConfiguration event:event error:&error]; + NSDictionary *configSharedState = [self.extension.api getSharedEventState:ADJAdobeModuleConfiguration + event:event + error:&error]; + if (configSharedState == nil || error) { + [ACPCore log:ACPMobileLogLevelError + tag:ADJAdobeExtensionLogTag + message:[NSString stringWithFormat: + @"An error occured while calling getSharedEventState: %@", + (error) ? [NSString stringWithFormat:@"Code: %ld, Domain: %@, Description: %@.", (long)error.code, error.domain, error.localizedDescription ] : + @"Unknown error."]]; + return; + } - if (error) { + NSString *adjustAppToken = [configSharedState objectForKey:ADJConfigurationAppToken]; + if (adjustAppToken == nil) { [ACPCore log:ACPMobileLogLevelError tag:ADJAdobeExtensionLogTag - message:[NSString stringWithFormat:@"Error on getSharedEventState %@:%zd.", - [error domain], - [error code]]]; + message:@"Extension module configuration error: Adjust App Token is nil!"]; return; } - NSString *adjustAppToken = [configSharedState objectForKey:ADJConfigurationAppToken]; id adjustTrackAttribution = [configSharedState objectForKey:ADJConfigurationTrackAttribution]; - - if (adjustAppToken == nil || adjustTrackAttribution == nil) { + if (adjustTrackAttribution == nil) { + [ACPCore log:ACPMobileLogLevelError + tag:ADJAdobeExtensionLogTag + message:@"Extension module configuration error: Adjust Trak Attribution is nil!"]; return; } if (![self.extension isKindOfClass:[AdjustAdobeExtension class]]) { + [ACPCore log:ACPMobileLogLevelError + tag:ADJAdobeExtensionLogTag + message:@"Extension type is not AdjustAdobeExtension!"]; return; } AdjustAdobeExtension *adjExt = (AdjustAdobeExtension *)[self extension]; - BOOL shouldTrackAttribution = - [adjustTrackAttribution isKindOfClass:[NSNumber class]] - && [adjustTrackAttribution integerValue] == 1; + [adjustTrackAttribution isKindOfClass:[NSNumber class]] && [adjustTrackAttribution integerValue] == 1; [adjExt setupAdjustWithAppToken:adjustAppToken trackAttribution:shouldTrackAttribution]; } diff --git a/CHANGELOG.md b/CHANGELOG.md index 72eb28f..a9d891e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +### Version 1.1.0 (21st July 2022) +#### Added +- Added ability to send event callback and partner parameters. +- Added ability to send push token. + +#### Native iOS SDK +- [iOS@v4.31.0](https://github.com/adjust/ios_sdk/tree/v4.31.0) + +--- + ### Version 1.0.4 (9th May 2022) #### Added - Added Swift Package Manager support. diff --git a/Example/AdjustAdobeExtension/ADJAppDelegate.m b/Example/AdjustAdobeExtension/ADJAppDelegate.m index 6b78a7a..b1523e1 100644 --- a/Example/AdjustAdobeExtension/ADJAppDelegate.m +++ b/Example/AdjustAdobeExtension/ADJAppDelegate.m @@ -25,7 +25,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( [ACPCore start:^{ [ACPCore lifecycleStart:nil]; }]; - + return YES; } diff --git a/Example/AdjustAdobeExtension/ADJViewController.m b/Example/AdjustAdobeExtension/ADJViewController.m index 1bd9e34..ebaf6c5 100644 --- a/Example/AdjustAdobeExtension/ADJViewController.m +++ b/Example/AdjustAdobeExtension/ADJViewController.m @@ -19,14 +19,40 @@ @implementation ADJViewController - (void)viewDidLoad { [super viewDidLoad]; - [ACPCore trackAction:@"TestAction" data:@{@"a": @"b", - ADJAdobeAdjustEventToken: @"g3mfiw"}]; - [ACPCore trackAction:@"TestActionRevenue" data:@{@"a": @"b", - ADJAdobeAdjustEventToken: @"a4fd35", - ADJAdobeAdjustEventRevenue: @"1.0", - ADJAdobeAdjustEventCurrency: @"EUR"}]; - [ACPCore trackState:@"TestState" data:@{@"a": @"b"}]; - + // Track simple event + NSMutableDictionary * dataDict = [NSMutableDictionary dictionary]; + [dataDict setValue:@"g3mfiw" forKey:ADJAdobeAdjustEventToken]; + [ACPCore trackAction:ADJAdobeAdjustActionTrackEvent data:dataDict]; + + [dataDict removeAllObjects]; + + // Track Revenue event + [dataDict setValue:@"a4fd35" forKey:ADJAdobeAdjustEventToken]; + [dataDict setValue:@"1.0" forKey:ADJAdobeAdjustEventRevenue]; + [dataDict setValue:@"EUR" forKey:ADJAdobeAdjustEventCurrency]; + [ACPCore trackAction:ADJAdobeAdjustActionTrackEvent data:dataDict]; + + [dataDict removeAllObjects]; + + // Track event with Callback parameters + [dataDict setValue:@"34vgg9" forKey:ADJAdobeAdjustEventToken]; + [dataDict setValue:@"value1" forKey:[ADJAdobeAdjustEventCallbackParamPrefix stringByAppendingString:@"key1"]]; + [dataDict setValue:@"value2" forKey:[ADJAdobeAdjustEventCallbackParamPrefix stringByAppendingString:@"key2"]]; + [ACPCore trackAction:ADJAdobeAdjustActionTrackEvent data:dataDict]; + + [dataDict removeAllObjects]; + + // Track event with Partner parameters + [dataDict setValue:@"w788qs" forKey:ADJAdobeAdjustEventToken]; + [dataDict setValue:@"value1" forKey:[ADJAdobeAdjustEventPartnerParamPrefix stringByAppendingString:@"key1"]]; + [dataDict setValue:@"value2" forKey:[ADJAdobeAdjustEventPartnerParamPrefix stringByAppendingString:@"key2"]]; + [ACPCore trackAction:ADJAdobeAdjustActionTrackEvent data:dataDict]; + + [dataDict removeAllObjects]; + + // Set Push Token + [dataDict setValue:@"your_app_push_token" forKey:ADJAdobeAdjustPushToken]; + [ACPCore trackAction:ADJAdobeAdjustActionSetPushToken data:dataDict]; } - (void)didReceiveMemoryWarning { diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 3773ed7..20c580a 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -12,12 +12,12 @@ PODS: - ACPUserProfile/xcframeworks (= 2.2.0) - ACPUserProfile/xcframeworks (2.2.0): - ACPCore (>= 2.9.0) - - Adjust (4.29.7): - - Adjust/Core (= 4.29.7) - - Adjust/Core (4.29.7) - - AdjustAdobeExtension (1.0.3): + - Adjust (4.31.0): + - Adjust/Core (= 4.31.0) + - Adjust/Core (4.31.0) + - AdjustAdobeExtension (1.1.0): - ACPCore - - Adjust (= 4.29.7) + - Adjust (= 4.31.0) DEPENDENCIES: - ACPCore (~> 2.0) @@ -40,8 +40,8 @@ SPEC CHECKSUMS: ACPCore: f6c5ec32fe162e61a18bec2ae79c8c5a48362a46 ACPGriffon: 5b46aa44e9667b1ff5d35aa37a84992b58d2aadf ACPUserProfile: 34bd51d00a2ae1add3d04eab0a313b73cb01b904 - Adjust: 91a06a01e4bb35b432e26b5d5bb8995b95fc381c - AdjustAdobeExtension: 97a4e60f2aab26c4c0093bca6588e4992286d44e + Adjust: 8d4c9da5bb1d161db5d35dd091e34a641c9511ef + AdjustAdobeExtension: 166465857cb0c62d34563d91bfd702e762eac6f1 PODFILE CHECKSUM: 9d0e231ac981dcf17cb67e756778f0a8f6f6332c diff --git a/Package.swift b/Package.swift index e6fa737..830df5f 100644 --- a/Package.swift +++ b/Package.swift @@ -15,7 +15,7 @@ let package = Package( .package( name: "Adjust", url: "https://github.com/adjust/ios_sdk.git", - from: "4.30.0" + from: "4.31.0" ), ], targets: [ @@ -29,22 +29,22 @@ let package = Package( ), .binaryTarget( name: "ACPCore", - url: "https://github.com/adjust/ios_adobe_extension/releases/download/v1.0.4/ACPCore.xcframework-2.9.5.zip", + url: "https://github.com/adjust/ios_adobe_extension/releases/download/v1.1.0/ACPCore.xcframework-2.9.5.zip", checksum: "1feb78e211bfc1d4e52e499323772f66c3eaab56434342465f7ee8a036a51fa9" ), .binaryTarget( name: "ACPIdentity", - url: "https://github.com/adjust/ios_adobe_extension/releases/download/v1.0.4/ACPIdentity.xcframework-2.5.2.zip", + url: "https://github.com/adjust/ios_adobe_extension/releases/download/v1.1.0/ACPIdentity.xcframework-2.5.2.zip", checksum: "9a4843568ad0832e575dbecb2524265bbfd9baf2557bc972f78b4359d70f8bc0" ), .binaryTarget( name: "ACPLifecycle", - url: "https://github.com/adjust/ios_adobe_extension/releases/download/v1.0.4/ACPLifecycle.xcframework-2.2.1.zip", + url: "https://github.com/adjust/ios_adobe_extension/releases/download/v1.1.0/ACPLifecycle.xcframework-2.2.1.zip", checksum: "02d7b6a1c615d9f5b222d58a95a0ded1ce4b1dac60fbecc9858d9e8dcd74c2eb" ), .binaryTarget( name: "ACPSignal", - url: "https://github.com/adjust/ios_adobe_extension/releases/download/v1.0.4/ACPSignal.xcframework-2.2.0.zip", + url: "https://github.com/adjust/ios_adobe_extension/releases/download/v1.1.0/ACPSignal.xcframework-2.2.0.zip", checksum: "2f7c9db8e8163fe2c5aa3e89fce485075b32751e65402f10a69eea674dd965a5" ) ] diff --git a/README.md b/README.md index 2cf6c80..aa5e2c3 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,48 @@ # Adjust Extension for Adobe Experience SDK -## Add the Adjust Extension to your project +This is the iOS Adobe Mobile Extension of Adjust™. You can read more about Adjust™ at [adjust.com]. -### Cocoapods integration +## Table of contents -If you're using [CocoaPods](http://cocoapods.org), add the following line to your `Podfile` and continue from [this step](#sdk-integrate): +### [Quick start](#iae-quick-start) + * [Example app](#iae-example-app) + * [Add the Adjust Extension to your project](#iae-sdk-add) + * [Cocoapods integration](#iae-sdk-add-cocoapods) + * [Swift Package Manager integration](#iae-sdk-add-spm) + * [Add iOS frameworks](#iae-sdk-frameworks) + * [Integrate the Adjust Extension into your app](#iae-sdk-integrate) + * [Basic setup](#iae-basic-setup) + * [Attribution](#iae-attribution) + +### [Events tracking](#iae-tracking) + * [Track event](#iae-track-event) + * [Track revenue](#iae-track-event-revenue) + * [Callback parameters](#iae-event-callback-parameters) + * [Partner parameters](#iae-event-partner-parameters) + +### [Additional features](#iae-additional-features) + * [Attribution callback](#iae-attribution-callback) + * [Deferred deep linking callback](#iae-deferred-deep-linking-callback) + * [Push token (uninstall tracking)](#iae-push-token) + +## Quick start + +### Example app + +There is an example app inside the [Example](Example/) directory. +Please run `pod install` in this folder to build the example application dependencies and then open `AdjustAdobeExtension.xcworkspace` to test the example application. + +## Add the Adjust Extension to your project + +### Cocoapods integration + +If you're using [CocoaPods](http://cocoapods.org), add the following line to your `Podfile` and continue from [this step](#iae-sdk-integrate): ```ruby pod 'AdjustAdobeExtension' ``` -### Swift Package Manager integration +### Swift Package Manager integration If you are using Swift Package Manager, add Adjust Extension for Adobe Experience SDK using the following Github repo link: @@ -23,7 +55,7 @@ Due to a missing SPM support in Adobe ACP SDKs, all required Adobe frameworks ar In the `Frameworks, Libraries, and Embedded Content` section of your App target's `General` tab, add the following frameworks and libraries required for Adobe frameworks: `UIKit`, `SystemConfiguration`, `WebKit`, `UserNotifications`, `libsqlite3.0`, `libc++`, `libz`. -## Add iOS frameworks +## Add iOS frameworks Adjust SDK is able to get additional information in case you link additional iOS frameworks to your app. Please, add following frameworks in case you want to enable Adjust SDK features based on their presence in your app and mark them as optional: @@ -33,7 +65,7 @@ Adjust SDK is able to get additional information in case you link additional iOS - `StoreKit.framework` - This framework is needed for access to `SKAdNetwork` framework and for Adjust SDK to handle communication with it automatically in iOS 14 or later. - `AppTrackingTransparency.framework` - This framework is needed in iOS 14 and later for SDK to be able to wrap user's tracking consent dialog and access to value of the user's consent to be tracked or not. -## Integrate the Adjust Extension into your app +## Integrate the Adjust Extension into your app Add the following import statement: @@ -47,20 +79,22 @@ Add the following import statement: import AdjustAdobeExtension ``` -## Basic setup +## Basic setup -You don't need to start the Adjust Adjust Extension manually. First, set the configuration in [Launch dashboard](https://launch.adobe.com/) and initialize `ACPCore`, then register the Adjust Extension: +You don't need to start the Adjust Extension manually. First, set the configuration in [Launch dashboard](https://launch.adobe.com/) and initialize `ACPCore`, then register the Adjust Extension: ```objc // Objective-C - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [ACPCore configureWithAppId:@"..."]; - - // ... + [ACPCore setLogLevel:ACPMobileLogLevelVerbose]; + [ACPCore configureWithAppId:@"{your_adobe_app_id}"]; AdjustAdobeExtensionConfig *config = [AdjustAdobeExtensionConfig configWithEnvironment:ADJEnvironmentSandbox]; [AdjustAdobeExtension registerExtensionWithConfig:config]; - + + [ACPCore start:^{ + [ACPCore lifecycleStart:nil]; + }]; return YES; } ``` @@ -68,78 +102,228 @@ You don't need to start the Adjust Adjust Extension manually. First, set the con ```swift // Swift func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - - ACPCore.configure(withAppId: "...") - - // ... + ACPCore.setLogLevel(ACPMobileLogLevel.verbose) + ACPCore.configure(withAppId: "{your_adobe_app_id}") if let config = AdjustAdobeExtensionConfig.init(environment: ADJEnvironmentSandbox) { AdjustAdobeExtension.register(with: config) } + ACPCore.start { + ACPCore.lifecycleStart(nil) + } return true } ``` -### Delegates callback +Replace `{your_adobe_app_id}` with your app id from Adobe Launch. + +Next, you must set the `environment` to either sandbox or production mode: + +```objc +ADJEnvironmentSandbox +ADJEnvironmentProduction +``` + +**Important:** Set the value to `ADJEnvironmentSandbox` if (and only if) you or someone else is testing your app. Make sure to set the environment to `ADJEnvironmentProduction` before you publish the app. Set it back to `ADJEnvironmentSandbox` if you start developing and testing it again. + +We use this environment to distinguish between real traffic and test traffic from test devices. Keeping the environment updated according to your current status is very important! + +## Attribution + +The option to share attribution data with Adobe is in the Launch dashboard under the extensions configuration and is on by default. Adjust tracks the action name `Adjust Campaign Data Received` with the following attribution information from Adjust: + +* `Adjust Network` +* `Adjust Campaign` +* `Adjust AdGroup` +* `Adjust Creative` + +## Events tracking -Optional: If you want to receive the attribution and deep link delegates callback, you can register for it in `AdjustAdobeExtensionConfig`: +### Track event + +You can use Adobe `[ACPCore trackAction:]` API for [`event tracking`](https://docs.adjust.com/en/event-tracking). Suppose you want to track every tap on a button. To do so, you'll create a new event token in your [dashboard](https://dash.adjust.com/). Let's say that the event token is `abc123`. In your button's press handling method, add the following lines to track the click: ```objc // Objective-C -[config callbackDeeplinkResponse:^BOOL(NSURL * _Nullable deeplink) { - // deep link response received +NSMutableDictionary * dataDict = [NSMutableDictionary dictionary]; +[dataDict setValue:@"abc123" forKey:ADJAdobeAdjustEventToken]; +[ACPCore trackAction:ADJAdobeAdjustActionTrackEvent data:dataDict]; +``` - // Apply your logic to determine whether the Adjust SDK should try to open the deep link - return YES; - // or - // return NO; -}]; +```swift +// Swift +var dataDict: Dictionary = [String : String]() +dataDict[ADJAdobeAdjustEventToken] = "abc123" +ACPCore.trackAction(ADJAdobeAdjustActionTrackEvent, data: dataDict) +``` -[config callbackAttributionChanged:^(ADJAttribution * _Nullable attribution) { - // attribution response received -}]; +### Track revenue + +If your users can generate revenue by tapping on advertisements or making in-app purchases, you can track those revenues too with events. Let's say a tap is worth one Euro cent. You can track the revenue event like this: + +```objc +// Objective-C +NSMutableDictionary * dataDict = [NSMutableDictionary dictionary]; +[dataDict setValue:@"abc123" forKey:ADJAdobeAdjustEventToken]; +[dataDict setValue:@"0.01" forKey:ADJAdobeAdjustEventRevenue]; +[dataDict setValue:@"EUR" forKey:ADJAdobeAdjustEventCurrency]; +[ACPCore trackAction:ADJAdobeAdjustActionTrackEvent data:dataDict]; ``` ```swift // Swift -config.callbackDeeplinkResponse { (deeplink : URL?) in - // deep link response received +var dataDict: Dictionary = [String : String]() +dataDict[ADJAdobeAdjustEventToken] = "abc123" +dataDict[ADJAdobeAdjustEventRevenue] = "0.01" +dataDict[ADJAdobeAdjustEventCurrency] = "EUR" +ACPCore.trackAction(ADJAdobeAdjustActionTrackEvent, data: dataDict) +``` - // Apply your logic to determine whether the Adjust SDK should try to open the deep link - return true; - // or - // return false; -} +### Callback parameters + +You can register a callback URL for your events in your [dashboard](https://dash.adjust.com/). We will send a GET request to that URL whenever the event is tracked. You can add callback parameters to that event by adding them as key value pair to the context data map before tracking it. We will then append these parameters to your callback URL. + +For example, suppose you have registered the URL `https://www.mydomain.com/callback` then track an event like this: + +```objc +// Objective-C +NSMutableDictionary * dataDict = [NSMutableDictionary dictionary]; +[dataDict setValue:@"abc123" forKey:ADJAdobeAdjustEventToken]; +[dataDict setValue:@"value1" forKey:[ADJAdobeAdjustEventCallbackParamPrefix stringByAppendingString:@"key1"]]; +[dataDict setValue:@"value2" forKey:[ADJAdobeAdjustEventCallbackParamPrefix stringByAppendingString:@"key2"]]; +[ACPCore trackAction:ADJAdobeAdjustActionTrackEvent data:dataDict]; +``` + +```swift +// Swift +var dataDict: Dictionary = [String : String]() +dataDict[ADJAdobeAdjustEventToken] = "abc123" +dataDict[ADJAdobeAdjustEventCallbackParamPrefix.appending("key1")] = "value1" +dataDict[ADJAdobeAdjustEventCallbackParamPrefix.appending("key2")] = "value2" +ACPCore.trackAction(ADJAdobeAdjustActionTrackEvent, data: dataDict) +``` + +In that case we would track the event and send a request to: + +``` +http://www.mydomain.com/callback?key=value&foo=bar +``` + +It should be mentioned that we support a variety of placeholders like `{idfa}` that can be used as parameter values. In the resulting callback this placeholder would be replaced with the ID for Advertisers of the current device. Also note that we don't store any of your custom parameters, but only append them to your callbacks, thus without a callback they will not be saved nor sent to you. + +You can read more about using URL callbacks, including a full list of available values, in our [callbacks guide](https://docs.adjust.com/en/callbacks). + +### Partner parameters + +You can also add parameters to be transmitted to network partners, which have been activated in your Adjust dashboard. + +You can add partner parameters to that event by adding them as key value pair to the context data map before tracking it. + +```objc +// Objective-C +NSMutableDictionary * dataDict = [NSMutableDictionary dictionary]; +[dataDict setValue:@"abc123" forKey:ADJAdobeAdjustEventToken]; +[dataDict setValue:@"value1" forKey:[ADJAdobeAdjustEventPartnerParamPrefix stringByAppendingString:@"key1"]]; +[dataDict setValue:@"value2" forKey:[ADJAdobeAdjustEventPartnerParamPrefix stringByAppendingString:@"key2"]]; +[ACPCore trackAction:ADJAdobeAdjustActionTrackEvent data:dataDict]; +``` + +```swift +// Swift +var dataDict: Dictionary = [String : String]() +dataDict[ADJAdobeAdjustEventToken] = "abc123" +dataDict[ADJAdobeAdjustEventPartnerParamPrefix.appending("key1")] = "value1" +dataDict[ADJAdobeAdjustEventPartnerParamPrefix.appending("key2")] = "value2" +ACPCore.trackAction(ADJAdobeAdjustActionTrackEvent, data: dataDict) +``` + +You can read more about special partners and these integrations in our [guide to special partners](https://docs.adjust.com/en/special-partners). + +## Additional features + +Once you have integrated the Adjust Extension for Adobe Experience SDK into your project, you can take advantage of the following features: + +### Attribution callback + +You can register a callback code block to be notified of tracker attribution changes. Due to the different sources we consider for attribution, we cannot provide this information synchronously. + +Please see our [attribution data policies](https://github.com/adjust/sdks/blob/master/doc/attribution-data.md) for more information. + +With the extension config instance, add the attribution callback before you start the SDK: + +```objc +// Objective-C +AdjustAdobeExtensionConfig *config = [AdjustAdobeExtensionConfig configWithEnvironment:ADJEnvironmentSandbox]; +[config callbackAttributionChanged:^(ADJAttribution * _Nullable attribution) { + // Attribution response received +}]; +[AdjustAdobeExtension registerExtensionWithConfig:config]; +``` -config.callbackAttributionChanged { (attribution : ADJAttribution?) in - // attribution response received +```swift +// Swift +if let config = AdjustAdobeExtensionConfig.init(environment: ADJEnvironmentSandbox) { + config.callbackAttributionChanged { (attribution : ADJAttribution?) in + // Attribution response received + } + AdjustAdobeExtension.register(with: config) } ``` -## Tracking events +The code block is called after the SDK receives the final attribution data. Within the block body, you'll have an access to the `attribution` parameter. + +### Deferred deep linking callback + +The Adjust SDK opens the deferred deep link by default. There is no extra configuration needed. But if you wish to control whether the Adjust SDK will open the deferred deep link or not, you can do it with a callback code block in the config object. -Any event (action or state) tracked using `ACPCore` is tracked by Adjust if it contains the `ADJAdobeAdjustEventCurrency` constant as a key: +With the extension config instance, add the deferred deep linking callback block before you start the SDK: ```objc // Objective-C -[ACPCore trackAction:@"TestAction" data:@{@"a": @"b", ADJAdobeAdjustEventToken: @"abc123"}]; -[ACPCore trackState:@"TestState" data:@{@"a": @"b"}]; // will *not* be tracked by Adjust +AdjustAdobeExtensionConfig *config = [AdjustAdobeExtensionConfig configWithEnvironment:ADJEnvironmentSandbox]; +[config callbackDeeplinkResponse:^BOOL(NSURL * _Nullable deeplink) { + // Deep link response received + // Apply your logic to determine whether the Adjust SDK should try to open the deep link + return YES; + // or + // return NO; +}]; +[AdjustAdobeExtension registerExtensionWithConfig:config]; ``` ```swift // Swift -ACPCore.trackAction("TestAction", data: ["a": "b", ADJAdobeAdjustEventToken: "abc123"]) -ACPCore.trackState("TestState", data: ["a": "b"]) // will *not* be tracked by Adjust +if let config = AdjustAdobeExtensionConfig.init(environment: ADJEnvironmentSandbox) { + config.callbackDeeplinkResponse { (deeplink : URL?) in + // Deep link response received + // Apply your logic to determine whether the Adjust SDK should try to open the deep link + return true; + // or + // return false; + } + AdjustAdobeExtension.register(with: config) +} ``` -If the event contains the constants `ADJAdobeAdjustEventCurrency` and `ADJAdobeAdjustEventRevenue` as keys, the event is tracked with this information as well. +After the Adjust SDK receives the deep link information from our backend, the SDK will deliver you its content via the callback block and expect the boolean return value from you. This return value represents your decision on whether or not the Adjust SDK should open the deep link or not. -## Attribution +### Push token (uninstall tracking) -The option to share attribution data with Adobe is in the Launch dashboard under the extensions configuration and is on by default. Adjust tracks the action name `Adjust Campaign Data Received` with the following attribution information from Adjust: +Push tokens are used for Audience Builder and client callbacks; they are also required for uninstall and reinstall tracking. -* `Adjust Network` -* `Adjust Campaign` -* `Adjust AdGroup` -* `Adjust Creative` +To send us the APNs push notification token, add the following call to Adjust once you have obtained your token (or whenever its value changes): + +```objc +// Objective-C +NSMutableDictionary * dataDict = [NSMutableDictionary dictionary]; +[dataDict setValue:@"your_app_push_token" forKey:ADJAdobeAdjustPushToken]; +[ACPCore trackAction:ADJAdobeAdjustActionSetPushToken data:dataDict]; +``` + +```swift +// Swift +var dataDict: Dictionary = [String : String]() +dataDict[ADJAdobeAdjustPushToken] = "your_app_push_token" +ACPCore.trackAction(ADJAdobeAdjustActionSetPushToken, data: dataDict) +``` diff --git a/VERSION b/VERSION index ee90284..9084fa2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.4 +1.1.0