Skip to content

Commit 6d72279

Browse files
fix: Consent Mapping Configuration (#37)
* fix: Consent Mapping Configuration * remove FIRApp configure call * keep consistent naming within method * Add error handling in mappingForKey Co-authored-by: Brandon Stalnaker <[email protected]> --------- Co-authored-by: Brandon Stalnaker <[email protected]>
1 parent a009cf5 commit 6d72279

File tree

1 file changed

+96
-62
lines changed

1 file changed

+96
-62
lines changed

mParticle-Google-Analytics-Firebase-GA4/MPKitFirebaseGA4Analytics.m

Lines changed: 96 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,13 @@ @implementation MPKitFirebaseGA4Analytics
4444
static NSString *const instanceIdIntegrationKey = @"app_instance_id";
4545
static NSString *const invalidFirebaseKey = @"invalid_ga4_key";
4646

47+
// Consent Mapping Keys
4748
static NSString *const kMPFIRGA4AdStorageKey = @"ad_storage";
4849
static NSString *const kMPFIRGA4AdUserDataKey = @"ad_user_data";
4950
static NSString *const kMPFIRGA4AdPersonalizationKey = @"ad_personalization";
5051
static NSString *const kMPFIRGA4AnalyticsStorageKey = @"analytics_storage";
52+
53+
// Default Consent Keys (from mParticle UI)
5154
static NSString *const kMPFIRGA4DefaultAdStorageKey = @"defaultAdStorageConsentSDK";
5255
static NSString *const kMPFIRGA4DefaultAdUserDataKey = @"defaultAdUserDataConsentSDK";
5356
static NSString *const kMPFIRGA4DefaultAdPersonalizationKey = @"defaultAdPersonalizationConsentSDK";
@@ -385,76 +388,51 @@ - (MPKitExecStatus *)setConsentState:(nullable MPConsentState *)state {
385388
}
386389

387390
- (void)updateConsent {
388-
FIRConsentStatus adStorageStatus;
389-
FIRConsentStatus adUserDataStatus;
390-
FIRConsentStatus analyticsStorageStatus;
391-
FIRConsentStatus adPersonalizationStatus;
392-
393-
// Default Consent States
394-
if (self.configuration[kMPFIRGA4DefaultAdStorageKey]) {
395-
if ([self.configuration[kMPFIRGA4DefaultAdStorageKey] isEqual: @"Granted"]) {
396-
adStorageStatus = FIRConsentStatusGranted;
397-
} else if ([self.configuration[kMPFIRGA4DefaultAdStorageKey] isEqual: @"Denied"]) {
398-
adStorageStatus = FIRConsentStatusDenied;
399-
}
400-
}
401-
if (self.configuration[kMPFIRGA4DefaultAdUserDataKey]) {
402-
if ([self.configuration[kMPFIRGA4DefaultAdUserDataKey] isEqual: @"Granted"]) {
403-
adUserDataStatus = FIRConsentStatusGranted;
404-
} else if ([self.configuration[kMPFIRGA4DefaultAdUserDataKey] isEqual: @"Denied"]) {
405-
adUserDataStatus = FIRConsentStatusDenied;
406-
}
407-
}
408-
if (self.configuration[kMPFIRGA4DefaultAnalyticsStorageKey]) {
409-
if ([self.configuration[kMPFIRGA4DefaultAnalyticsStorageKey] isEqual: @"Granted"]) {
410-
analyticsStorageStatus = FIRConsentStatusGranted;
411-
} else if ([self.configuration[kMPFIRGA4DefaultAnalyticsStorageKey] isEqual: @"Denied"]) {
412-
analyticsStorageStatus = FIRConsentStatusDenied;
413-
}
414-
}
415-
if (self.configuration[kMPFIRGA4DefaultAdPersonalizationKey]) {
416-
if ([self.configuration[kMPFIRGA4DefaultAdPersonalizationKey] isEqual: @"Granted"]) {
417-
adPersonalizationStatus = FIRConsentStatusGranted;
418-
} else if ([self.configuration[kMPFIRGA4DefaultAdPersonalizationKey] isEqual: @"Denied"]) {
419-
adPersonalizationStatus = FIRConsentStatusDenied;
420-
}
391+
NSArray<NSDictionary *> *mappings = [self mappingForKey: @"consentMappingSDK"];
392+
NSDictionary<NSString *, NSString *> *mappingsConfig;
393+
if (mappings != nil) {
394+
mappingsConfig = [self convertToKeyValuePairs: mappings];
421395
}
422396

423-
MParticleUser *currentUser = [[[MParticle sharedInstance] identity] currentUser];
424-
NSDictionary<NSString *, MPGDPRConsent *> *userConsentMap = currentUser.consentState.gdprConsentState;
425397

426-
// Update from mParticle consent
427-
if (self.configuration[kMPFIRGA4AdStorageKey] && userConsentMap[self.configuration[kMPFIRGA4AdStorageKey]]) {
428-
MPGDPRConsent *consent = userConsentMap[self.configuration[kMPFIRGA4AdStorageKey]];
429-
adStorageStatus = consent.consented ? FIRConsentStatusGranted : FIRConsentStatusDenied;
398+
MParticleUser *currentUser = [[[MParticle sharedInstance] identity] currentUser];
399+
NSDictionary<NSString *, MPGDPRConsent *> *gdprConsents = currentUser.consentState.gdprConsentState;
400+
401+
NSNumber *adStorage = [self resolvedConsentForMappingKey:kMPFIRGA4AdStorageKey
402+
defaultKey:kMPFIRGA4DefaultAdStorageKey
403+
gdprConsents:gdprConsents
404+
mapping:mappingsConfig];
405+
406+
NSNumber *adUserData = [self resolvedConsentForMappingKey:kMPFIRGA4AdUserDataKey
407+
defaultKey:kMPFIRGA4DefaultAdUserDataKey
408+
gdprConsents:gdprConsents
409+
mapping:mappingsConfig];
410+
411+
NSNumber *analyticsStorage = [self resolvedConsentForMappingKey:kMPFIRGA4AnalyticsStorageKey
412+
defaultKey:kMPFIRGA4DefaultAnalyticsStorageKey
413+
gdprConsents:gdprConsents
414+
mapping:mappingsConfig];
415+
416+
NSNumber *adPersonalization = [self resolvedConsentForMappingKey:kMPFIRGA4AdPersonalizationKey
417+
defaultKey:kMPFIRGA4DefaultAdPersonalizationKey
418+
gdprConsents:gdprConsents
419+
mapping:mappingsConfig];
420+
421+
NSMutableDictionary *uploadDict = [NSMutableDictionary dictionary];
422+
423+
if (adStorage != nil) {
424+
uploadDict[FIRConsentTypeAdStorage] = adStorage.boolValue ? FIRConsentStatusGranted : FIRConsentStatusDenied;
430425
}
431-
if (self.configuration[kMPFIRGA4AdUserDataKey] && userConsentMap[self.configuration[kMPFIRGA4AdUserDataKey]]) {
432-
MPGDPRConsent *consent = userConsentMap[self.configuration[kMPFIRGA4AdUserDataKey]];
433-
adUserDataStatus = consent.consented ? FIRConsentStatusGranted : FIRConsentStatusDenied;
426+
if (adUserData != nil) {
427+
uploadDict[FIRConsentTypeAdUserData] = adUserData.boolValue ? FIRConsentStatusGranted : FIRConsentStatusDenied;
434428
}
435-
if (self.configuration[kMPFIRGA4AnalyticsStorageKey] && userConsentMap[self.configuration[kMPFIRGA4AnalyticsStorageKey]]) {
436-
MPGDPRConsent *consent = userConsentMap[self.configuration[kMPFIRGA4AnalyticsStorageKey]];
437-
analyticsStorageStatus = consent.consented ? FIRConsentStatusGranted : FIRConsentStatusDenied;
429+
if (analyticsStorage != nil) {
430+
uploadDict[FIRConsentTypeAnalyticsStorage] = analyticsStorage.boolValue ? FIRConsentStatusGranted : FIRConsentStatusDenied;
438431
}
439-
if (self.configuration[kMPFIRGA4AdPersonalizationKey] && userConsentMap[self.configuration[kMPFIRGA4AdPersonalizationKey]]) {
440-
MPGDPRConsent *consent = userConsentMap[self.configuration[kMPFIRGA4AdPersonalizationKey]];
441-
adPersonalizationStatus = consent.consented ? FIRConsentStatusGranted : FIRConsentStatusDenied;
432+
if (adPersonalization != nil) {
433+
uploadDict[FIRConsentTypeAdPersonalization] = adPersonalization.boolValue ? FIRConsentStatusGranted : FIRConsentStatusDenied;
442434
}
443435

444-
// Construct a dictionary of consents
445-
NSMutableDictionary *uploadDict = [[NSMutableDictionary alloc] init];
446-
if (adStorageStatus) {
447-
uploadDict[FIRConsentTypeAdStorage] = adStorageStatus;
448-
}
449-
if (adUserDataStatus) {
450-
uploadDict[FIRConsentTypeAdUserData] = adUserDataStatus;
451-
}
452-
if (analyticsStorageStatus) {
453-
uploadDict[FIRConsentTypeAnalyticsStorage] = analyticsStorageStatus;
454-
}
455-
if (adPersonalizationStatus) {
456-
uploadDict[FIRConsentTypeAdPersonalization] = adPersonalizationStatus;
457-
}
458436

459437
// Update consent state with FIRAnalytics
460438
[FIRAnalytics setConsent:uploadDict];
@@ -688,4 +666,60 @@ - (void)limitDictionary:(NSMutableDictionary *)dictionary maxCount:(int)maxCount
688666
}
689667
}
690668

669+
#pragma mark - Helpers
670+
671+
- (NSNumber * _Nullable)resolvedConsentForMappingKey:(NSString *)mappingKey
672+
defaultKey:(NSString *)defaultKey
673+
gdprConsents:(NSDictionary<NSString *, MPGDPRConsent *> *)gdprConsents
674+
mapping:(NSDictionary<NSString *, NSString*> *) mapping {
675+
676+
// Prefer mParticle Consent if available
677+
NSString *purpose = mapping[mappingKey];
678+
if (purpose) {
679+
MPGDPRConsent *consent = gdprConsents[purpose];
680+
if (consent) {
681+
return @(consent.consented);
682+
}
683+
}
684+
685+
// Fallback to configuration defaults
686+
NSString *value = self->_configuration[defaultKey];
687+
if ([value isEqualToString:@"Granted"]) {
688+
return @(YES);
689+
} else if ([value isEqualToString:@"Denied"]) {
690+
return @(NO);
691+
}
692+
return nil;
693+
}
694+
695+
- (NSArray<NSDictionary *>*)mappingForKey:(NSString*)key {
696+
NSString *mappingJson = _configuration[@"consentMappingSDK"];
697+
if (![mappingJson isKindOfClass:[NSString class]]) {
698+
return nil;
699+
}
700+
701+
NSData *jsonData = [mappingJson dataUsingEncoding:NSUTF8StringEncoding];
702+
NSError *error;
703+
NSArray *result = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
704+
705+
if (error) {
706+
NSLog(@"Failed to parse consent mapping JSON: %@", error.localizedDescription);
707+
return nil;
708+
}
709+
710+
return result;
711+
}
712+
713+
- (NSDictionary*)convertToKeyValuePairs: (NSArray<NSDictionary *>*) mappings {
714+
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
715+
for (NSDictionary *entry in mappings) {
716+
NSString *value = entry[@"value"];
717+
NSString *purpose = [entry[@"map"] lowercaseString];
718+
if (value && purpose) {
719+
dict[value] = purpose;
720+
}
721+
}
722+
return dict;
723+
}
724+
691725
@end

0 commit comments

Comments
 (0)