diff --git a/Adjust.podspec b/Adjust.podspec index e6f4ab7c2..8d6c583f0 100644 --- a/Adjust.podspec +++ b/Adjust.podspec @@ -1,11 +1,11 @@ Pod::Spec.new do |s| s.name = "Adjust" - s.version = "4.0.6" + s.version = "4.0.7" s.summary = "This is the iOS SDK of adjust. You can read more about it at http://adjust.com." s.homepage = "http://adjust.com" s.license = { :type => 'MIT', :file => 'MIT-LICENSE' } s.author = { "Christian Wellenbrock" => "welle@adjust.com" } - s.source = { :git => "https://github.com/adjust/ios_sdk.git", :tag => "v4.0.6" } + s.source = { :git => "https://github.com/adjust/ios_sdk.git", :tag => "v4.0.7" } s.platform = :ios, '4.3' s.framework = 'SystemConfiguration' s.weak_framework = 'AdSupport', 'iAd' diff --git a/Adjust/ADJAttribution.m b/Adjust/ADJAttribution.m index d82d59661..aaa58571f 100644 --- a/Adjust/ADJAttribution.m +++ b/Adjust/ADJAttribution.m @@ -92,7 +92,7 @@ - (NSDictionary *)dictionary { - (NSString *)description { return [NSString stringWithFormat:@"tt:%@ tn:%@ net:%@ cam:%@ adg:%@ cre:%@", self.trackerToken, self.trackerName, self.network, self.campaign, - self.adgroup, self.campaign]; + self.adgroup, self.creative]; } diff --git a/Adjust/ADJUtil.m b/Adjust/ADJUtil.m index fd62fa402..40fe851ac 100644 --- a/Adjust/ADJUtil.m +++ b/Adjust/ADJUtil.m @@ -16,7 +16,7 @@ #include static NSString * const kBaseUrl = @"https://app.adjust.com"; -static NSString * const kClientSdk = @"ios4.0.6"; +static NSString * const kClientSdk = @"ios4.0.7"; static NSString * const kDateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'Z"; static NSDateFormatter *dateFormat; diff --git a/AdjustTests/ADJActivityHandlerTests.m b/AdjustTests/ADJActivityHandlerTests.m index aea491bfa..f82a3b7cb 100644 --- a/AdjustTests/ADJActivityHandlerTests.m +++ b/AdjustTests/ADJActivityHandlerTests.m @@ -121,7 +121,7 @@ - (void)testFirstRun ADJActivityPackage *activityPackage = (ADJActivityPackage *) self.packageHandlerMock.packageQueue[0]; // check the Sdk version is being tested - XCTAssertEqual(@"ios4.0.6", activityPackage.clientSdk, @"%@", activityPackage.extendedString); + XCTAssertEqual(@"ios4.0.7", activityPackage.clientSdk, @"%@", activityPackage.extendedString); // check the server url XCTAssertEqual(@"https://app.adjust.com", ADJUtil.baseUrl); @@ -1069,7 +1069,7 @@ - (void)testAttribution { // check the first attribution is written XCTAssert([self.loggerMock containsMessage:ADJLogLevelDebug - beginsWith:@"Wrote Attribution: tt:trackerTokenValue tn:trackerNameValue net:networkValue cam:campaignValue adg:adgroupValue cre:campaignValue"], @"%@", self.loggerMock); + beginsWith:@"Wrote Attribution: tt:trackerTokenValue tn:trackerNameValue net:networkValue cam:campaignValue adg:adgroupValue cre:creativeValue"], @"%@", self.loggerMock); // change values of the same attribution attribution.trackerName = @"trackerNameValueNew"; @@ -1087,7 +1087,7 @@ - (void)testAttribution { // check the second attribution is written XCTAssert([self.loggerMock containsMessage:ADJLogLevelDebug - beginsWith:@"Wrote Attribution: tt:trackerTokenValueNew tn:trackerNameValueNew net:networkValueNew cam:campaignValueNew adg:adgroupValueNew cre:campaignValueNew"], @"%@", self.loggerMock); + beginsWith:@"Wrote Attribution: tt:trackerTokenValueNew tn:trackerNameValueNew net:networkValueNew cam:campaignValueNew adg:adgroupValueNew cre:creativeValueNew"], @"%@", self.loggerMock); // build a json dictionary equal to the updated Attribution NSMutableDictionary * newJsonDictionary = [[NSMutableDictionary alloc] init]; @@ -1107,7 +1107,7 @@ - (void)testAttribution { // check the same attribution is not written again XCTAssertFalse([self.loggerMock containsMessage:ADJLogLevelDebug - beginsWith:@"Wrote Attribution: tt:trackerTokenValueNew tn:trackerNameValueNew net:networkValueNew cam:campaignValueNew adg:adgroupValueNew cre:campaignValueNew"], @"%@", self.loggerMock); + beginsWith:@"Wrote Attribution: tt:trackerTokenValueNew tn:trackerNameValueNew net:networkValueNew cam:campaignValueNew adg:adgroupValueNew cre:creativeValueNew"], @"%@", self.loggerMock); [activityHandler setAskingAttribution:NO]; @@ -1158,7 +1158,7 @@ - (void)testAttribution { // check new attribution after restart XCTAssert([self.loggerMock containsMessage:ADJLogLevelDebug - beginsWith:@"Wrote Attribution: tt:trackerTokenValue tn:trackerNameValue net:networkValue cam:campaignValue adg:adgroupValue cre:campaignValue"], @"%@", self.loggerMock); + beginsWith:@"Wrote Attribution: tt:trackerTokenValue tn:trackerNameValue net:networkValue cam:campaignValue adg:adgroupValue cre:creativeValue"], @"%@", self.loggerMock); [newActivityHandler setAskingAttribution:YES]; diff --git a/README.md b/README.md index 5cb80e237..66ec14665 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ If you're using [CocoaPods][cocoapods], you can add the following line to your `Podfile` and continue with [step 3](#step3): ```ruby -pod 'Adjust', :git => 'git://github.com/adjust/ios_sdk.git', :tag => 'v4.0.6' +pod 'Adjust', :git => 'git://github.com/adjust/ios_sdk.git', :tag => 'v4.0.7' ``` ### 1. Get the SDK diff --git a/VERSION b/VERSION index d13e837c8..43beb4001 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.0.6 +4.0.7 diff --git a/doc/criteo.md b/doc/criteo.md new file mode 100644 index 000000000..1625f58e7 --- /dev/null +++ b/doc/criteo.md @@ -0,0 +1,95 @@ +## Criteo plugin + +Integrate adjust with Criteo events by following these steps: + +1. Locate the `plugin` folder inside the downloaded archive from our [releases page](https://github.com/adjust/ios_sdk/releases). + +2. Drag the `ADJCriteo.h` and `ADJCriteo.m` files into the `Adjust` folder inside your project. + +3. In the dialog `Choose options for adding these files` make sure to check the checkbox +to `Copy items if needed` and select the radio button to `Create groups`. + +Now you can integrate each of the different Criteo events, like in the following examples: + +### View Homepage + +```objc +ADJEvent *event = [ADJEvent eventWithEventToken:@"{viewHomepageEventToken}"]; + +[Adjust trackEvent:event]; +``` + +### View Search + +```objc +#import "ADJCriteo.h" + +ADJEvent *event = [ADJEvent eventWithEventToken:@"{viewSearchEventToken}"]; + +[ADJCriteo injectViewSearchIntoEvent:event checkInDate:@"2015-01-01" checkOutDate:@"2015-01-07"]; + +[Adjust trackEvent:event]; +``` + +### View Listing + +```objc +#import "ADJCriteo.h" + +ADJEvent *event = [ADJEvent eventWithEventToken:@"{viewListingEventToken}"]; + +ADJCriteoProduct *product1 = [ADJCriteoProduct productWithId:@"productId1" price:100.0 quantity:1]; +ADJCriteoProduct *product2 = [ADJCriteoProduct productWithId:@"productId2" price:77.7 quantity:3]; +ADJCriteoProduct *product3 = [ADJCriteoProduct productWithId:@"productId3" price:50 quantity:2]; +NSArray *products = @[product1, product2, product3]; + +[ADJCriteo injectViewListingIntoEvent:event products:products customerId:@"customerId1"]; + +[Adjust trackEvent:event]; +``` + +### View Product + +```objc +#import "ADJCriteo.h" + +ADJEvent *event = [ADJEvent eventWithEventToken:@"{viewProductEventToken}"]; + +[ADJCriteo injectViewProductIntoEvent:event productId:@"productId1" customerId:@"customerId1"]; + +[Adjust trackEvent:event]; +``` + +### Cart + +```objc +#import "ADJCriteo.h" + +ADJEvent *event = [ADJEvent eventWithEventToken:@"{cartEventToken}"]; + +ADJCriteoProduct *product1 = [ADJCriteoProduct productWithId:@"productId1" price:100.0 quantity:1]; +ADJCriteoProduct *product2 = [ADJCriteoProduct productWithId:@"productId2" price:77.7 quantity:3]; +ADJCriteoProduct *product3 = [ADJCriteoProduct productWithId:@"productId3" price:50 quantity:2]; +NSArray *products = @[product1, product2, product3]; + +[ADJCriteo injectCartIntoEvent:event products:products customerId:@"customerId1"]; + +[Adjust trackEvent:event]; +``` + +### Transaction confirmation + +```objc +#import "ADJCriteo.h" + +ADJEvent *event = [ADJEvent eventWithEventToken:@"{transactionConfirmedEventToken}"]; + +ADJCriteoProduct *product1 = [ADJCriteoProduct productWithId:@"productId1" price:100.0 quantity:1]; +ADJCriteoProduct *product2 = [ADJCriteoProduct productWithId:@"productId2" price:77.7 quantity:3]; +ADJCriteoProduct *product3 = [ADJCriteoProduct productWithId:@"productId3" price:50 quantity:2]; +NSArray *products = @[product1, product2, product3]; + +[ADJCriteo injectTransactionConfirmedIntoEvent:event products:products customerId:@"customerId1"]; + +[Adjust trackEvent:event]; +``` diff --git a/doc/migrate.md b/doc/migrate.md index ce41a878a..6c070ec1e 100644 --- a/doc/migrate.md +++ b/doc/migrate.md @@ -1,4 +1,4 @@ -## Migrate your adjust SDK for iOS to v4.0.6 from v3.4.0 +## Migrate your adjust SDK for iOS to v4.0.7 from v3.4.0 ### Initial setup diff --git a/example/example.xcodeproj/project.pbxproj b/example/example.xcodeproj/project.pbxproj index 47ae34035..ee1507e9d 100644 --- a/example/example.xcodeproj/project.pbxproj +++ b/example/example.xcodeproj/project.pbxproj @@ -33,6 +33,8 @@ 960FCF8D1A1B9B7D00282BD4 /* ADJAttributionHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 960FCF761A1B9B7D00282BD4 /* ADJAttributionHandler.m */; }; 960FCF8E1A1B9B7D00282BD4 /* ADJAttribution.m in Sources */ = {isa = PBXBuildFile; fileRef = 960FCF781A1B9B7D00282BD4 /* ADJAttribution.m */; }; 960FCF8F1A1B9B7D00282BD4 /* ADJConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 960FCF7A1A1B9B7D00282BD4 /* ADJConfig.m */; }; + 96332C8F1A891E1100D38FAF /* AdSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 96332C8E1A891E1100D38FAF /* AdSupport.framework */; }; + 96332C911A891E1800D38FAF /* iAd.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 96332C901A891E1800D38FAF /* iAd.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -97,6 +99,8 @@ 960FCF781A1B9B7D00282BD4 /* ADJAttribution.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ADJAttribution.m; sourceTree = ""; }; 960FCF791A1B9B7D00282BD4 /* ADJConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ADJConfig.h; sourceTree = ""; }; 960FCF7A1A1B9B7D00282BD4 /* ADJConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ADJConfig.m; sourceTree = ""; }; + 96332C8E1A891E1100D38FAF /* AdSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdSupport.framework; path = System/Library/Frameworks/AdSupport.framework; sourceTree = SDKROOT; }; + 96332C901A891E1800D38FAF /* iAd.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = iAd.framework; path = System/Library/Frameworks/iAd.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -104,6 +108,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 96332C911A891E1800D38FAF /* iAd.framework in Frameworks */, + 96332C8F1A891E1100D38FAF /* AdSupport.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -120,6 +126,8 @@ 960FCF171A1B9ACE00282BD4 = { isa = PBXGroup; children = ( + 96332C901A891E1800D38FAF /* iAd.framework */, + 96332C8E1A891E1100D38FAF /* AdSupport.framework */, 960FCF221A1B9ACE00282BD4 /* example */, 960FCF3C1A1B9ACE00282BD4 /* exampleTests */, 960FCF211A1B9ACE00282BD4 /* Products */, diff --git a/plugin/ADJCriteo.h b/plugin/ADJCriteo.h new file mode 100644 index 000000000..cbe31333c --- /dev/null +++ b/plugin/ADJCriteo.h @@ -0,0 +1,50 @@ +// +// ADJCriteoEvents.h +// +// +// Created by Pedro Filipe on 06/02/15. +// +// + +#import +#import "ADJEvent.h" + +@interface ADJCriteoProduct : NSObject + +@property (nonatomic, assign) float criteoPrice; +@property (nonatomic, assign) NSUInteger criteoQuantity; +@property (nonatomic, copy) NSString *criteoProductID; + +- (id) initWithId:(NSString*)productId + price:(float)price + quantity:(NSUInteger)quantity; + ++ (ADJCriteoProduct *)productWithId:(NSString*)productId + price:(float)price + quantity:(NSUInteger)quantity; + +@end + +@interface ADJCriteo : NSObject + ++ (void)injectViewSearchIntoEvent:(ADJEvent *)event + checkInDate:(NSString *)din + checkOutDate:(NSString *)dout; + ++ (void)injectViewListingIntoEvent:(ADJEvent *)event + products:(NSArray *)products + customerId:(NSString *)customerId; + ++ (void)injectViewProductIntoEvent:(ADJEvent *)event + productId:(NSString *)productId + customerId:(NSString *)customerId; + ++ (void)injectCartIntoEvent:(ADJEvent *)event + products:(NSArray *)products + customerId:(NSString *)customerId; + ++ (void)injectTransactionConfirmedIntoEvent:(ADJEvent *)event + products:(NSArray *)products + customerId:(NSString *)customerId; + +@end diff --git a/plugin/ADJCriteo.m b/plugin/ADJCriteo.m new file mode 100644 index 000000000..1c2e89e7b --- /dev/null +++ b/plugin/ADJCriteo.m @@ -0,0 +1,181 @@ +// +// ADJCriteoEvents.m +// +// +// Created by Pedro Filipe on 06/02/15. +// +// + +#import "ADJCriteo.h" +#import "Adjust.h" + +@implementation ADJCriteoProduct + +- (id) initWithId:(NSString *)productId + price:(float)price + quantity:(NSUInteger)quantity +{ + self = [super init]; + if (self == nil) return nil; + + self.criteoProductID = productId; + self.criteoPrice = price; + self.criteoQuantity = quantity; + + return self; +} + ++ (ADJCriteoProduct *) productWithId:(NSString *)productId + price:(float)price + quantity:(NSUInteger)quantity +{ + return [[ADJCriteoProduct alloc] initWithId:productId price:price quantity:quantity]; +} + +@end + +@implementation ADJCriteo + + ++ (void)injectViewSearchIntoEvent:(ADJEvent *)event + checkInDate:(NSString *)din + checkOutDate:(NSString *)dout +{ + [event addPartnerParameter:@"din" value:din]; + [event addPartnerParameter:@"dout" value:dout]; +} + ++ (void)injectViewListingIntoEvent:(ADJEvent *)event + products:(NSArray *)products + customerId:(NSString *)customerId +{ + [event addPartnerParameter:@"customer_id" value:customerId]; + + NSString * jsonProducts = [ADJCriteo createCriteoVLFromProducts:products]; + [event addPartnerParameter:@"criteo_p" value:jsonProducts]; +} + ++ (void)injectViewProductIntoEvent:(ADJEvent *)event + productId:(NSString *)productId + customerId:(NSString *)customerId +{ + [event addPartnerParameter:@"customer_id" value:customerId]; + [event addPartnerParameter:@"criteo_p" value:productId]; +} + ++ (void)injectCartIntoEvent:(ADJEvent *)event + products:(NSArray *)products + customerId:(NSString *)customerId +{ + [event addPartnerParameter:@"customer_id" value:customerId]; + + NSString * jsonProducts = [ADJCriteo createCriteoVBFromProducts:products]; + [event addPartnerParameter:@"criteo_p" value:jsonProducts]; +} + ++ (void)injectTransactionConfirmedIntoEvent:(ADJEvent *)event + products:(NSArray *)products + customerId:(NSString *)customerId +{ + [event addPartnerParameter:@"customer_id" value:customerId]; + + NSString * jsonProducts = [ADJCriteo createCriteoVBFromProducts:products]; + [event addPartnerParameter:@"criteo_p" value:jsonProducts]; +} + ++ (NSString*) createCriteoVBFromProducts:(NSArray*) products +{ + NSMutableString* criteoVBValue = [NSMutableString stringWithString:@"["]; + for (ADJCriteoProduct *product in products) + { + NSString* productString = [NSString stringWithFormat:@"{\"i\":\"%@\",\"pr\":%f,\"q\":%lu}", + [product criteoProductID], + [product criteoPrice], + (unsigned long)[product criteoQuantity]]; + + [criteoVBValue appendString:productString]; + if (product != [products lastObject]) + { + [criteoVBValue appendString:@","]; + } + } + [criteoVBValue appendString:@"]"]; + return [criteoVBValue stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; +} + ++ (NSString*) createCriteoVLFromProducts:(NSArray*) products +{ +#ifdef DEBUG + if ([products count] > 3) + NSLog(@"Warning : VL Events should only have at most 3 objects, discarding the rest"); +#endif + NSUInteger numberOfProducts = 0; + NSMutableString* criteoVBValue = [NSMutableString stringWithString:@"["]; + + for (ADJCriteoProduct *product in products) + { + NSString* productString = [NSString stringWithFormat:@"\"%@\"", [product criteoProductID]]; + + [criteoVBValue appendString:productString]; + ++numberOfProducts; + + if (product != [products lastObject] && numberOfProducts < 3) + { + [criteoVBValue appendString:@","]; + } + if (numberOfProducts >= 3) + break; + } + [criteoVBValue appendString:@"]"]; + return [criteoVBValue stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; +} + ++ (NSString*) createCriteoVBFromProductsDictionary:(NSArray*) products +{ + NSMutableString* criteoVBValue = [NSMutableString stringWithString:@"["]; + for (NSDictionary* product in products) + { + NSString* productString = [NSString stringWithFormat:@"{\"i\":\"%@\",\"pr\":%f,\"q\":%lu}", + [product objectForKey:@"productID"], + [[product objectForKey:@"price"] floatValue], + [[product objectForKey:@"quantity"] integerValue]]; + + [criteoVBValue appendString:productString]; + if (product != [products lastObject]) + { + [criteoVBValue appendString:@","]; + } + } + [criteoVBValue appendString:@"]"]; + return [criteoVBValue stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; +} + ++ (NSString*) createCriteoVLFromProductsArray:(NSArray*) products +{ +#ifdef DEBUG + if ([products count] > 3) + NSLog(@"Warning : VL Events should only have at most 3 objects, discarding the rest"); +#endif + NSUInteger numberOfProducts = 0; + + NSMutableString* criteoVBValue = [NSMutableString stringWithString:@"["]; + for (NSString* product in products) + { + NSString* productString = [NSString stringWithFormat:@"\"%@\"", product]; + + [criteoVBValue appendString:productString]; + ++numberOfProducts; + + if (product != [products lastObject] && numberOfProducts < 3) + { + [criteoVBValue appendString:@","]; + } + if (numberOfProducts >= 3) + break; + + } + [criteoVBValue appendString:@"]"]; + return [criteoVBValue stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; +} + +@end