diff --git a/demo-ios-objc/demo-ios-objc/AppDelegate.h b/demo-ios-objc/demo-ios-objc/AppDelegate.h index ab0b3ee..9e52cfb 100644 --- a/demo-ios-objc/demo-ios-objc/AppDelegate.h +++ b/demo-ios-objc/demo-ios-objc/AppDelegate.h @@ -11,5 +11,8 @@ extern OptableSDK *OPTABLE; +/// SHA256 of the Email, normalized the way the SDK does it for OptableSDKIdentifierType_EmailAddress: whitespace removed, lowercased. +NSString *OptableDemoSHA256Email(NSString *email); + @interface AppDelegate : UIResponder @end diff --git a/demo-ios-objc/demo-ios-objc/AppDelegate.m b/demo-ios-objc/demo-ios-objc/AppDelegate.m index 12e850e..e4b72d8 100644 --- a/demo-ios-objc/demo-ios-objc/AppDelegate.m +++ b/demo-ios-objc/demo-ios-objc/AppDelegate.m @@ -14,9 +14,24 @@ @import PrebidMobile; @import GoogleMobileAds; +#import OptableSDK *OPTABLE = nil; +NSString *OptableDemoSHA256Email(NSString *email) { + NSArray *parts = [email componentsSeparatedByCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet]; + NSData *data = [[[parts componentsJoinedByString:@""] lowercaseString] dataUsingEncoding:NSUTF8StringEncoding]; + + unsigned char digest[CC_SHA256_DIGEST_LENGTH]; + CC_SHA256(data.bytes, (CC_LONG)data.length, digest); + + NSMutableString *hex = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2]; + for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) { + [hex appendFormat:@"%02x", digest[i]]; + } + return hex; +} + @interface AppDelegate () @end diff --git a/demo-ios-objc/demo-ios-objc/Base.lproj/Main.storyboard b/demo-ios-objc/demo-ios-objc/Base.lproj/Main.storyboard index e4b95bd..e85759e 100644 --- a/demo-ios-objc/demo-ios-objc/Base.lproj/Main.storyboard +++ b/demo-ios-objc/demo-ios-objc/Base.lproj/Main.storyboard @@ -51,8 +51,44 @@ + + + + + + + + + + - + @@ -68,8 +104,17 @@ - + + + + + + + + + + @@ -81,6 +126,7 @@ + diff --git a/demo-ios-objc/demo-ios-objc/GAMBannerViewController.m b/demo-ios-objc/demo-ios-objc/GAMBannerViewController.m index dbdc66f..17c5f95 100644 --- a/demo-ios-objc/demo-ios-objc/GAMBannerViewController.m +++ b/demo-ios-objc/demo-ios-objc/GAMBannerViewController.m @@ -65,9 +65,10 @@ - (IBAction)loadBannerWithTargeting:(id)sender { [OPTABLE witnessWithEvent: @"GAMBannerViewController.loadBannerClicked" properties: @{ @"example": @"value" } error: &error]; + // `id` and `neighbors` are sent as given, so Email addresses and phone numbers must be hashed first. [OPTABLE profileWithTraits: @{ @"example": @"value", @"anotherExample": @123, @"thirdExample": @YES } - id: NULL - neighbors: NULL + id: @"c:demo-user-1234" + neighbors: @[[@"e:" stringByAppendingString:OptableDemoSHA256Email(@"some.email@address.com")]] error: &error]; } @@ -85,9 +86,10 @@ - (IBAction)loadBannerWithTargetingFromCache:(id)sender { [OPTABLE witnessWithEvent: @"GAMBannerViewController.loadBannerClicked" properties: @{ @"example": @"value" } error: &error]; + // `id` and `neighbors` are sent as given, so Email addresses and phone numbers must be hashed first. [OPTABLE profileWithTraits: @{ @"example": @"value", @"anotherExample": @123, @"thirdExample": @YES } - id: NULL - neighbors: NULL + id: @"c:demo-user-1234" + neighbors: @[[@"e:" stringByAppendingString:OptableDemoSHA256Email(@"some.email@address.com")]] error: &error]; } diff --git a/demo-ios-objc/demo-ios-objc/IdentifyViewController.h b/demo-ios-objc/demo-ios-objc/IdentifyViewController.h index 055e2ea..2fb4c0a 100644 --- a/demo-ios-objc/demo-ios-objc/IdentifyViewController.h +++ b/demo-ios-objc/demo-ios-objc/IdentifyViewController.h @@ -12,6 +12,9 @@ @property (weak, nonatomic) IBOutlet UITextField *identifyInput; @property (weak, nonatomic) IBOutlet UIButton *identifyButton; @property (weak, nonatomic) IBOutlet UITextView *identifyOutput; +@property (weak, nonatomic) IBOutlet UITextField *identifyURLInput; - (IBAction)dispatchIdentify:(id)sender; +- (IBAction)dispatchIdentifyHashed:(id)sender; +- (IBAction)dispatchIdentifyFromURL:(id)sender; @end diff --git a/demo-ios-objc/demo-ios-objc/IdentifyViewController.m b/demo-ios-objc/demo-ios-objc/IdentifyViewController.m index bd062e7..772685d 100644 --- a/demo-ios-objc/demo-ios-objc/IdentifyViewController.m +++ b/demo-ios-objc/demo-ios-objc/IdentifyViewController.m @@ -24,6 +24,10 @@ - (void)viewDidLoad { [super viewDidLoad]; _identifyInput.delegate = self; + _identifyURLInput.delegate = self; + + // A link like the ones in a newsletter Email, carrying the SHA256 of the reader's Email address + _identifyURLInput.text = [NSString stringWithFormat:@"https://example.com/newsletter?oeid=%@", OptableDemoSHA256Email(@"some.email@address.com")]; OptableSDKDelegate *delegate = (OptableSDKDelegate *)OPTABLE.delegate; delegate.identifyOutput = self.identifyOutput; @@ -71,6 +75,34 @@ - (IBAction)dispatchIdentify:(id)sender { }]; } +// dispatchIdentifyHashed: sends the Email as a SHA256 hashed in the app, as you would with a hash from your own backend. +// OptableSDKIdentifierType_HEM sends it as given; passing a hash as OptableSDKIdentifierType_EmailAddress would hash it a second time. +- (IBAction)dispatchIdentifyHashed:(id)sender { + [self.view endEditing: TRUE]; + + // An empty Email would otherwise be sent as the SHA256 of an empty string + NSString *email = [_identifyInput.text ?: @"" stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet]; + if (email.length == 0) { + _identifyOutput.text = @"Enter an Email to send it hashed."; + return; + } + + NSError *error = nil; + [OPTABLE identify: @[ + [OptableSDKIdentifier identifierWithType:OptableSDKIdentifierType_HEM value:OptableDemoSHA256Email(email)], + ] + error: &error]; +} + +// dispatchIdentifyFromURL: passes a link to tryIdentifyFromURL:, as you would from a universal link handler. +// If the link has an `oeid` query parameter holding a SHA256, the SDK calls /identify with it; otherwise it does nothing. +- (IBAction)dispatchIdentifyFromURL:(id)sender { + [self.view endEditing: TRUE]; + + NSError *error = nil; + [OPTABLE tryIdentifyFromURL: _identifyURLInput.text ?: @"" error: &error]; +} + // MARK: - UITextFieldDelegate - (BOOL)textFieldShouldReturn:(UITextField *)textField { [textField resignFirstResponder]; diff --git a/demo-ios-objc/demo-ios-objc/PrebidBannerViewController.m b/demo-ios-objc/demo-ios-objc/PrebidBannerViewController.m index 73842ce..6ba4ede 100644 --- a/demo-ios-objc/demo-ios-objc/PrebidBannerViewController.m +++ b/demo-ios-objc/demo-ios-objc/PrebidBannerViewController.m @@ -74,9 +74,10 @@ - (IBAction)loadBannerWithTargeting:(id)sender { [OPTABLE witnessWithEvent: @"PrebidBannerViewController.loadBannerClicked" properties: @{ @"example": @"value" } error: &error]; + // `id` and `neighbors` are sent as given, so Email addresses and phone numbers must be hashed first. [OPTABLE profileWithTraits: @{ @"example": @"value", @"anotherExample": @123, @"thirdExample": @YES } - id: NULL - neighbors: NULL + id: @"c:demo-user-1234" + neighbors: @[[@"e:" stringByAppendingString:OptableDemoSHA256Email(@"some.email@address.com")]] error: &error]; } @@ -94,9 +95,10 @@ - (IBAction)loadBannerWithTargetingFromCache:(id)sender { [OPTABLE witnessWithEvent: @"PrebidBannerViewController.loadBannerClicked" properties: @{ @"example": @"value" } error: &error]; + // `id` and `neighbors` are sent as given, so Email addresses and phone numbers must be hashed first. [OPTABLE profileWithTraits: @{ @"example": @"value", @"anotherExample": @123, @"thirdExample": @YES } - id: NULL - neighbors: NULL + id: @"c:demo-user-1234" + neighbors: @[[@"e:" stringByAppendingString:OptableDemoSHA256Email(@"some.email@address.com")]] error: &error]; }