From 01255059747f9efbc98e1a89264062f97aad2755 Mon Sep 17 00:00:00 2001 From: Steve Madsen Date: Thu, 1 Oct 2020 21:46:10 -0400 Subject: [PATCH] Shift responsibility of IDFA collection to clients This is a backport from segmentio/analytics-ios (https://github.com/segmentio/analytics-ios/pull/892). Any references to Apple's AdSupport framework might trip the App Store's static analysis, making it a bit unclear if SDK users can confidently say that their apps do not use the IDFA. By moving the responsibility to collect the IDFA out of the PostHog SDK and into the client app, the AdSupport references are gone and the question is easily answered. --- .../Classes/Internal/PHGPostHogIntegration.m | 4 ++-- PostHog/Classes/Internal/PHGPostHogUtils.h | 2 -- PostHog/Classes/Internal/PHGPostHogUtils.m | 22 ------------------- PostHog/Classes/PHGPostHogConfiguration.h | 12 ++++++++++ PostHog/Classes/PHGPostHogConfiguration.m | 1 + PostHogTests/PostHogTests.swift | 2 ++ 6 files changed, 17 insertions(+), 26 deletions(-) diff --git a/PostHog/Classes/Internal/PHGPostHogIntegration.m b/PostHog/Classes/Internal/PHGPostHogIntegration.m index 13f823fff..9edfb8371 100644 --- a/PostHog/Classes/Internal/PHGPostHogIntegration.m +++ b/PostHog/Classes/Internal/PHGPostHogIntegration.m @@ -154,8 +154,8 @@ - (NSDictionary *)staticContext if (NSClassFromString(PHGAdvertisingClassIdentifier)) { dict[@"$device_adCapturingEnabled"] = @(GetAdCapturingEnabled()); } - if (self.configuration.enableAdvertisingCapturing) { - NSString *idfa = PHGIDFA(); + if (self.configuration.enableAdvertisingCapturing && self.configuration.adSupportBlock != nil) { + NSString *idfa = self.configuration.adSupportBlock(); if (idfa.length) dict[@"$device_advertisingId"] = idfa; } diff --git a/PostHog/Classes/Internal/PHGPostHogUtils.h b/PostHog/Classes/Internal/PHGPostHogUtils.h index 95d9c3b20..e45b59444 100644 --- a/PostHog/Classes/Internal/PHGPostHogUtils.h +++ b/PostHog/Classes/Internal/PHGPostHogUtils.h @@ -32,8 +32,6 @@ void PHGLog(NSString *format, ...); JSON_DICT PHGCoerceDictionary(NSDictionary *_Nullable dict); -NSString *_Nullable PHGIDFA(void); - NSString *PHGEventNameForScreenTitle(NSString *title); // Deep copy and check NSCoding conformance diff --git a/PostHog/Classes/Internal/PHGPostHogUtils.m b/PostHog/Classes/Internal/PHGPostHogUtils.m index 2b2c509a5..fa3b7953d 100644 --- a/PostHog/Classes/Internal/PHGPostHogUtils.m +++ b/PostHog/Classes/Internal/PHGPostHogUtils.m @@ -1,5 +1,4 @@ #import "PHGPostHogUtils.h" -#import static BOOL kPostHogLoggerShowLogs = NO; @@ -164,27 +163,6 @@ static id PHGCoerceJSONObject(id obj) return PHGCoerceJSONObject(dict); } -NSString *PHGIDFA() -{ - NSString *idForAdvertiser = nil; - Class identifierManager = NSClassFromString(@"ASIdentifierManager"); - if (identifierManager) { - SEL sharedManagerSelector = NSSelectorFromString(@"sharedManager"); - id sharedManager = - ((id (*)(id, SEL)) - [identifierManager methodForSelector:sharedManagerSelector])( - identifierManager, sharedManagerSelector); - SEL advertisingIdentifierSelector = - NSSelectorFromString(@"advertisingIdentifier"); - NSUUID *uuid = - ((NSUUID * (*)(id, SEL)) - [sharedManager methodForSelector:advertisingIdentifierSelector])( - sharedManager, advertisingIdentifierSelector); - idForAdvertiser = [uuid UUIDString]; - } - return idForAdvertiser; -} - NSString *PHGEventNameForScreenTitle(NSString *title) { return [[NSString alloc] initWithFormat:@"Viewed %@ Screen", title]; diff --git a/PostHog/Classes/PHGPostHogConfiguration.h b/PostHog/Classes/PHGPostHogConfiguration.h index 722227cb8..bb3416461 100644 --- a/PostHog/Classes/PHGPostHogConfiguration.h +++ b/PostHog/Classes/PHGPostHogConfiguration.h @@ -72,6 +72,18 @@ typedef NSMutableURLRequest *_Nonnull (^PHGRequestFactory)(NSURL *_Nonnull); */ @property (nonatomic, assign) BOOL enableAdvertisingCapturing; +/** + * Sets a block to be called when IDFA / AdSupport identifier is created. + * This is to allow for apps that do not want ad tracking to pass App Store guidelines in certain categories while + * still allowing apps that do ad tracking to continue to function. + * + * Example: + * configuration.adSupportBlock = ^{ + * return [[ASIdentifierManager sharedManager] advertisingIdentifier]; + * } + */ +@property (nonatomic, copy, nullable) NSString * _Nonnull (^adSupportBlock)(void); + /** * The number of queued events that the posthog client should flush at. Setting this to `1` will not queue any events and will use more battery. `20` by default. */ diff --git a/PostHog/Classes/PHGPostHogConfiguration.m b/PostHog/Classes/PHGPostHogConfiguration.m index abefaa72b..a7827ddf8 100644 --- a/PostHog/Classes/PHGPostHogConfiguration.m +++ b/PostHog/Classes/PHGPostHogConfiguration.m @@ -52,6 +52,7 @@ - (instancetype)init if (self = [super init]) { self.shouldUseLocationServices = NO; self.enableAdvertisingCapturing = YES; + self.adSupportBlock = nil; self.shouldUseBluetooth = NO; self.flushAt = 20; self.flushInterval = 30; diff --git a/PostHogTests/PostHogTests.swift b/PostHogTests/PostHogTests.swift index 15a16a2b3..d8462c605 100644 --- a/PostHogTests/PostHogTests.swift +++ b/PostHogTests/PostHogTests.swift @@ -34,6 +34,7 @@ class PostHogTests: QuickSpec { expect(posthog.configuration.host) == URL(string: "https://app.posthog.com") expect(posthog.configuration.shouldUseLocationServices) == false expect(posthog.configuration.enableAdvertisingCapturing) == true + expect(posthog.configuration.adSupportBlock).to(beNil()) expect(posthog.configuration.shouldUseBluetooth) == false expect(posthog.configuration.libraryName) == "posthog-ios" expect(posthog.configuration.libraryVersion) == PHGPostHog.version() @@ -54,6 +55,7 @@ class PostHogTests: QuickSpec { expect(posthog.configuration.host) == URL(string: "https://testapp.posthog.test") expect(posthog.configuration.shouldUseLocationServices) == false expect(posthog.configuration.enableAdvertisingCapturing) == true + expect(posthog.configuration.adSupportBlock).to(beNil()) expect(posthog.configuration.shouldUseBluetooth) == false expect(posthog.configuration.libraryVersion) == "posthog-ios-version" expect(posthog.configuration.libraryName) == "posthog-ios-test"