diff --git a/CHANGELOG b/CHANGELOG index 1a0c38833..8d88bc60b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,8 @@ +Version 1.7.0 (2021-04-03) +-------------------------- +Fix issue of OpenIDFA causing App Rejection (#575) + Version 1.6.2 (2021-01-12) -------------------------- Fix internal Carthage issues with Xcode 12.3 (#561) diff --git a/Snowplow iOSTests/TestUtils.m b/Snowplow iOSTests/TestUtils.m index 63e91e1a0..6375d7fab 100644 --- a/Snowplow iOSTests/TestUtils.m +++ b/Snowplow iOSTests/TestUtils.m @@ -85,46 +85,6 @@ - (void)testGetEventId { @"UUID generated doesn't match the type 4 UUID RFC"); } -// TODO: Fix AppleIdfa test -/* -- (void)testGetAppleIdfa { - // The simulator running the test must have "limit ad tracking" disabled. - // (You can find it in the Simulator: Settings > Privacy > Advertising > Limit Ad Tracking > Set to False) - NSString *sample_uuid = [SPUtilities getAppleIdfa]; - - // For regex pattern matching to verify if it's of UUID type 4 - NSString *pattern = @"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"; - NSRange searchRange = NSMakeRange(0, [sample_uuid length]); - NSError *error = NULL; - NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error]; - NSArray *matches = [regex matchesInString:sample_uuid options:0 range:searchRange]; - - XCTAssertEqual([matches count], (NSUInteger)1, - @"UUID generated doesn't match the type 4 UUID RFC"); -} -*/ - -- (void)testGetOpenIdfa { - NSString *sample_uuid = [SPUtilities getOpenIdfa]; -#if TARGET_OS_IPHONE - if (SNOWPLOW_iOS_9_OR_LATER) { - XCTAssertNil(sample_uuid); - } else { - // For regex pattern matching to verify if it's of UUID type 4 - NSString *pattern = @"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"; - NSRange searchRange = NSMakeRange(0, [sample_uuid length]); - NSError *error = NULL; - NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error]; - NSArray *matches = [regex matchesInString:sample_uuid options:0 range:searchRange]; - NSLog(@"UUID generated: %@", sample_uuid); - XCTAssertEqual([matches count], (NSUInteger)1, - @"UUID generated doesn't match the type 4 UUID RFC"); - } -#else - XCTAssertNil(sample_uuid); -#endif -} - - (void)testGetTransactionId { // Supressing deprecated warning only for tests #pragma clang diagnostic push diff --git a/Snowplow.xcodeproj/project.pbxproj b/Snowplow.xcodeproj/project.pbxproj index 122b7adbb..abda0c39c 100644 --- a/Snowplow.xcodeproj/project.pbxproj +++ b/Snowplow.xcodeproj/project.pbxproj @@ -29,8 +29,6 @@ 752DAC2721CC42BC0065F874 /* SPUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = ABFCC3751922984A00FAE8FE /* SPUtilities.m */; }; 752DAC2921CC42BC0065F874 /* SPRequestResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 0413DD761B78D643000D2112 /* SPRequestResult.m */; }; 752DAC2B21CC42BC0065F874 /* SPWeakTimerTarget.m in Sources */ = {isa = PBXBuildFile; fileRef = 044CA88C1B94792B000EA3B1 /* SPWeakTimerTarget.m */; }; - 752DAC3021CC431C0065F874 /* OpenIDFA.m in Sources */ = {isa = PBXBuildFile; fileRef = AB39617119530E850002F235 /* OpenIDFA.m */; }; - 752DAC3121CC43C60065F874 /* OpenIDFA.h in Headers */ = {isa = PBXBuildFile; fileRef = AB39617019530E850002F235 /* OpenIDFA.h */; settings = {ATTRIBUTES = (Private, ); }; }; 752DAC3221CC43C60065F874 /* Snowplow.h in Headers */ = {isa = PBXBuildFile; fileRef = AB0C27C5191B408200018557 /* Snowplow.h */; settings = {ATTRIBUTES = (Public, ); }; }; 752DAC3321CC43C70065F874 /* SPTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = AB9E8210192DD336006744C9 /* SPTracker.h */; settings = {ATTRIBUTES = (Public, ); }; }; 752DAC3421CC43C70065F874 /* SPEmitter.h in Headers */ = {isa = PBXBuildFile; fileRef = AB0C27E9191B43D600018557 /* SPEmitter.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -129,12 +127,10 @@ 75CAC46221F2A21B00271FB3 /* SPRequestResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 0413DD751B78D635000D2112 /* SPRequestResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; 75CAC46321F2A21B00271FB3 /* SPWeakTimerTarget.h in Headers */ = {isa = PBXBuildFile; fileRef = 044CA88B1B94791E000EA3B1 /* SPWeakTimerTarget.h */; settings = {ATTRIBUTES = (Private, ); }; }; 75CAC46521F2A21B00271FB3 /* SPRequestCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = 049B2BDA1B7A203200BD82FC /* SPRequestCallback.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 75CAC46621F2A22500271FB3 /* OpenIDFA.h in Headers */ = {isa = PBXBuildFile; fileRef = AB39617019530E850002F235 /* OpenIDFA.h */; settings = {ATTRIBUTES = (Private, ); }; }; 75CAC46921F2A25B00271FB3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB0C27C0191B408200018557 /* Foundation.framework */; }; 75F9C5D121FA2E8B00A5B8FC /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ABB67D8C192D9552009A1ECE /* UIKit.framework */; }; 75F9C5D221FA2E9F00A5B8FC /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB9E8213192DEC38006744C9 /* CoreTelephony.framework */; }; 75F9C5D321FA352800A5B8FC /* FMDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 752DAC0C21CC3EEA0065F874 /* FMDB.framework */; }; - 75F9C5D521FA357100A5B8FC /* OpenIDFA.m in Sources */ = {isa = PBXBuildFile; fileRef = AB39617119530E850002F235 /* OpenIDFA.m */; }; 75F9C5D621FA357100A5B8FC /* Snowplow.m in Sources */ = {isa = PBXBuildFile; fileRef = 043EC5E61B8F224900294081 /* Snowplow.m */; }; 75F9C5D721FA357100A5B8FC /* SPTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = AB9E8211192DD336006744C9 /* SPTracker.m */; }; 75F9C5D821FA357100A5B8FC /* SPEmitter.m in Sources */ = {isa = PBXBuildFile; fileRef = AB0C27EA191B43D600018557 /* SPEmitter.m */; }; @@ -146,7 +142,6 @@ 75F9C5DE21FA357100A5B8FC /* SPUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = ABFCC3751922984A00FAE8FE /* SPUtilities.m */; }; 75F9C5DF21FA357100A5B8FC /* SPRequestResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 0413DD761B78D643000D2112 /* SPRequestResult.m */; }; 75F9C5E021FA357100A5B8FC /* SPWeakTimerTarget.m in Sources */ = {isa = PBXBuildFile; fileRef = 044CA88C1B94792B000EA3B1 /* SPWeakTimerTarget.m */; }; - 75F9C5E421FA35BC00A5B8FC /* OpenIDFA.h in Headers */ = {isa = PBXBuildFile; fileRef = AB39617019530E850002F235 /* OpenIDFA.h */; settings = {ATTRIBUTES = (Private, ); }; }; 75F9C5E521FA35BC00A5B8FC /* Snowplow-Bridging-Header.h in Headers */ = {isa = PBXBuildFile; fileRef = 75D6061E21C9CA8A00C7B016 /* Snowplow-Bridging-Header.h */; settings = {ATTRIBUTES = (Public, ); }; }; 75F9C5E621FA35BC00A5B8FC /* Snowplow.h in Headers */ = {isa = PBXBuildFile; fileRef = AB0C27C5191B408200018557 /* Snowplow.h */; settings = {ATTRIBUTES = (Public, ); }; }; 75F9C5E721FA35BC00A5B8FC /* SPTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = AB9E8210192DD336006744C9 /* SPTracker.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -512,8 +507,6 @@ AB0C27EA191B43D600018557 /* SPEmitter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPEmitter.m; sourceTree = ""; }; AB0C27F3191C67CD00018557 /* SPPayload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPPayload.h; sourceTree = ""; }; AB0C27F4191C67CD00018557 /* SPPayload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPPayload.m; sourceTree = ""; }; - AB39617019530E850002F235 /* OpenIDFA.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenIDFA.h; sourceTree = ""; }; - AB39617119530E850002F235 /* OpenIDFA.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OpenIDFA.m; sourceTree = ""; }; AB9E8210192DD336006744C9 /* SPTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTracker.h; sourceTree = ""; }; AB9E8211192DD336006744C9 /* SPTracker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTracker.m; sourceTree = ""; }; AB9E8213192DEC38006744C9 /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = System/Library/Frameworks/CoreTelephony.framework; sourceTree = SDKROOT; }; @@ -851,8 +844,6 @@ ED6AC5152369D42800A8F8A3 /* ios.modulemap */, B3D9BE0F237ACE0D009B310A /* watchos.modulemap */, AB0C27C4191B408200018557 /* Snowplow-Prefix.pch */, - AB39617019530E850002F235 /* OpenIDFA.h */, - AB39617119530E850002F235 /* OpenIDFA.m */, ); name = "Supporting Files"; sourceTree = ""; @@ -908,7 +899,6 @@ CE4F9CCA244B066500968CFC /* SPGlobalContext.h in Headers */, CE4F9CD6244B066500968CFC /* SPScreenView.h in Headers */, 752DAC4221CC60F20065F874 /* Snowplow-Bridging-Header.h in Headers */, - 752DAC3121CC43C60065F874 /* OpenIDFA.h in Headers */, CE4F9C9A244B066500968CFC /* SPEvent.h in Headers */, 752DAC3621CC43C70065F874 /* SPSession.h in Headers */, 752DAC3B21CC43C70065F874 /* SPRequestResult.h in Headers */, @@ -958,7 +948,6 @@ CE4F9D13244B066500968CFC /* SPBackground.h in Headers */, 75CAC46521F2A21B00271FB3 /* SPRequestCallback.h in Headers */, CE4F9CAB244B066500968CFC /* SPTrackerEvent.h in Headers */, - 75CAC46621F2A22500271FB3 /* OpenIDFA.h in Headers */, EDD8540D24EE786900661F6B /* SPEventStore.h in Headers */, CE4F9D07244B066500968CFC /* SPSchemaRuleset.h in Headers */, 75CAC45D21F2A21B00271FB3 /* SPSession.h in Headers */, @@ -1065,7 +1054,6 @@ 7534D20422569BFF00904EE5 /* SPScreenState.h in Headers */, EDD8543724EFFFB300661F6B /* SPRequest.h in Headers */, 75F9C5F221FA35BC00A5B8FC /* SPRequestCallback.h in Headers */, - 75F9C5E421FA35BC00A5B8FC /* OpenIDFA.h in Headers */, CE4F9CCD244B066500968CFC /* SPGlobalContext.h in Headers */, CE4F9CD9244B066500968CFC /* SPScreenView.h in Headers */, 7534D20622569BFF00904EE5 /* SPInstallTracker.h in Headers */, @@ -1321,7 +1309,6 @@ 754774D0222756470043B814 /* UIViewController+SPScreenView_SWIZZLE.m in Sources */, CE4F9CDA244B066500968CFC /* SPPageView.m in Sources */, CE4F9C96244B066500968CFC /* SPEcommerceItem.m in Sources */, - 752DAC3021CC431C0065F874 /* OpenIDFA.m in Sources */, 752DAC1721CC42BC0065F874 /* Snowplow.m in Sources */, CE4F9C92244B066500968CFC /* SPForeground.m in Sources */, ED91CB7123AA8AD50078E75F /* SPDevicePlatform.m in Sources */, @@ -1506,7 +1493,6 @@ EDEE836124BE0944000B8530 /* SPLogger.m in Sources */, CE4F9CE1244B066500968CFC /* SPStructured.m in Sources */, ED914EC124325AB40068DA0A /* SPGdprContext.m in Sources */, - 75F9C5D521FA357100A5B8FC /* OpenIDFA.m in Sources */, EDD8543B24EFFFB300661F6B /* SPRequest.m in Sources */, CE4F9CC5244B066500968CFC /* SPConsentGranted.m in Sources */, 75F9C5D621FA357100A5B8FC /* Snowplow.m in Sources */, diff --git a/Snowplow/OpenIDFA.h b/Snowplow/OpenIDFA.h deleted file mode 100644 index 1135535ac..000000000 --- a/Snowplow/OpenIDFA.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// OpenIDFA.h -// -// NON PRODUCTION RELEASE VERSION 0.9 -// -// Authored by Yann Lechelle on 07 Feb 2014. -// Open sourced to early adopters for private peer-review on 12 Feb 2014. -// Updated for stability on 19 Feb 2014. -// Open sourced to the wider community on 18 March 2014. -// Copyright (c) 2014 APPSFIRE. -// -// Homepage: http://OpenIDFA.org -// Twitter: @openIDFA -// -// This piece of code is released under the Creative Commons licence with "Attribution + No Derivatives" (CC BY-ND) http://creativecommons.org/licenses/by-nd/3.0/# -// -// Disclaimer: -// - using OpenIDFA requires understanding the difference between IDFA and OpenIDFA (see full comparison on http://OpenIDFA.org) -// - software is provided as is, comes with no guarantee -// - the author or APPSFIRE may not be held liable in any way for any issue arising from the use of OpenIDFA -// - -#import "Snowplow.h" -#import - -#if SNOWPLOW_TARGET_IOS - -@interface OpenIDFA : NSObject - -+ (NSString*) sameDayOpenIDFA; -+ (NSArray*) threeDaysOpenIDFAArray; - -@end - -#endif diff --git a/Snowplow/OpenIDFA.m b/Snowplow/OpenIDFA.m deleted file mode 100644 index 97302ad7e..000000000 --- a/Snowplow/OpenIDFA.m +++ /dev/null @@ -1,259 +0,0 @@ -// -// OpenIDFA.m -// -// NON PRODUCTION RELEASE VERSION 0.9 -// -// Authored by Yann Lechelle on 07 Feb 2014. -// Open sourced to early adopters for private peer-review on 12 Feb 2014. -// Updated for stability on 19 Feb 2014. -// Open sourced to the wider community on 18 March 2014. -// Copyright (c) 2014 APPSFIRE. -// -// Homepage: http://OpenIDFA.org -// Twitter: @openIDFA -// -// This piece of code is released under the Creative Commons licence with "Attribution + No Derivatives" (CC BY-ND) http://creativecommons.org/licenses/by-nd/3.0/# -// -// Disclaimer: -// - using OpenIDFA requires understanding the difference between IDFA and OpenIDFA (see full comparison on http://OpenIDFA.org) -// - software is provided as is, comes with no guarantee -// - the author or APPSFIRE may not be held liable in any way for any issue arising from the use of OpenIDFA -// - -#import "Snowplow.h" - -#if SNOWPLOW_TARGET_IOS - -#import "OpenIDFA.h" -#import -#import -#import -#include -#include -#include -#include - -@implementation OpenIDFA - -+ (NSString*) sameDayOpenIDFA -{ - return [[OpenIDFA threeDaysOpenIDFAArray] objectAtIndex:0]; -} - -+ (BOOL) canOpenURL:(NSURL *)URL { -// @selector(sharedApplication) does not exist when building app extensions instead -// of apps. With strict objc_msgSend message checking turned on, calls to the class -// method will become compile-time warnings. To prevent that from happening, build -// with -DSNOWPLOW_APP_EXTENSIONS=1. -#if defined(SNOWPLOW_APP_EXTENSIONS) - return NO; -#else - return [[UIApplication sharedApplication] canOpenURL:URL]; -#endif -} - -+ (NSArray*) threeDaysOpenIDFAArray { - - // The following list represents a rather large array of ids used to detect - // the presence of a number of apps available on the device. The apps on this - // list have been handpicked by Appsfire AppGenome engine; they each carry - // a statistically significant weight to define and differentiate a user from - // another. For the purpose of obfuscation inside the lib, we only used apps - // with Facebook-related URLHanders typically starting with the string "fb" - // then followed by a numerical string. - // - // RATIONALE: an "appmap" profile may vary over time, however, the likeness that it - // evolves between two tracking events over a large proportion of the user - // base is low. - // - // NOTICE: this list is the property of Appsfire and may not be extracted from - // the context of OpenIDFA which is governed by a Creative Commons (No Derivatives) - // - NSArray* base = @[ @101015295179ll, @102443183283204ll, @105130332854716ll, @110633906966ll, @111774748922919ll, @112953085413703ll, @113174082133029ll, @113246946530ll, @114870218560647ll, @115829135094686ll, @115862191798713ll, @118506164848956ll, @118589468194837ll, @118881298142408ll, @120176898077068ll, @121848807893603ll, @123448314320ll, @123591657714831ll, @124024574287414ll, @127449357267488ll, @127995567256931ll, @132363533491609ll, @134841016914ll, @138326442889677ll, @138713932872514ll, @146348772076164ll, @147364571964693ll, @147712241956950ll, @148327618516767ll, @152777738124418ll, @154615291248069ll, @156017694504926ll, @158761204309396ll, @159248674166087ll, @160888540611569ll, @161500167252219ll, @161599933913761ll, @162729813767876ll, @165482230209033ll, @176151905794941ll, @177821765590225ll, @178508429994ll, @192454074134796ll, @194714260574159ll, @208559595824260ll, @209864595695358ll, @210068525731476ll, @239823722730183ll, @246290217488ll, @255472420580ll, @267160383314960ll, @292065597989ll, @99197768694ll, @342234513642ll, @349724985922ll, @99554596360ll, @40343401983ll, @500407959994978ll, @52216716219ll, @90376669494ll]; - - - // Playing with the prefix some more for the purpose of obfuscation - // - NSMutableString* _s_appmap = [NSMutableString stringWithString:@""]; - NSString* _s = @"/"; - NSString* _c = @":"; - NSString* _b = @"b"; - NSString* _f = @"f"; - - // Computing the "appmap" profile string for this user - // STRENGTH: potential of 2^61 distinct combinations - // - [_s_appmap appendString:[self.class canOpenURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@%@%@%@%@",_f,_b,_c,_s,_s]]]?@"|":@"-"]; - for (id baseid in base) { - [_s_appmap appendString:[self.class canOpenURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@%@%@%@%@%@",_f,_b,[baseid stringValue],_c,_s,_s]]]?@"|":@"-"]; - } - - // Collecting the device boottime (Unix epoch) and turn it into a string - // - // RATIONALE: any reboot will cause former OpenIDFAs for this user to be - // invalidated and useless. This is a built-in property of OpenIDFA that - // wants to ensure that tokens expire regularly, and in this case, non- - // determiniscally. - // - // COMPLEXITY: boottime is NOT stable. Few days after releasing the lib, - // I realized that the clock was adjusted by 2 seconds per day on an actual - // device probably due to real-time clock adjustments over NTP. - // - // SOLUTION: truncating the right hand side of the boottime by 4 digits - // This allows the boottime to slide by up to 9999 seconds... many days! - // Side benefits, reboots within a 2.77h window will NOT reset the IDFA - // - // STRENGTH: iOS devices do not reboot often (ahem, version 7.1+!) - // Typical reboot occurs days if not weeks apart. Using one week, - // gives us 600k distinct possibilities assuming a random distribution - // of reboots across all devices - // - - size_t size; - struct timeval boottime; - - int mib[2] = {CTL_KERN, KERN_BOOTTIME}; - size = sizeof(boottime); - NSString* _s_bt = @""; - if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0) { - _s_bt =[NSString stringWithFormat:@"%lu",boottime.tv_sec]; - _s_bt = [_s_bt substringToIndex:[_s_bt length]-4]; - } - - // Collecting the device "machine" identifier - // - // STRENGTH: 20+ combinations - // - // WEAKNESS: compared to Android, iOS pales here :) oh wait... - // - sysctlbyname("hw.machine", NULL, &size, NULL, 0); - char *machine = malloc(size); - sysctlbyname("hw.machine", machine, &size, NULL, 0); - NSString *_s_machine = [NSString stringWithCString:machine encoding:NSUTF8StringEncoding]; - free(machine); - - // Collecting the device "model" identifier, for completition - // - // STRENGTH: adds little, helps in case of simulator - // - sysctlbyname("hw.model", NULL, &size, NULL, 0); - char *model = malloc(size); - sysctlbyname("hw.model", model, &size, NULL, 0); - NSString *_s_model = [NSString stringWithCString:model encoding:NSUTF8StringEncoding]; - free(model); - - // Collecting the locale country code - // - // STRENGTH: its complicated, but at least 80 combinations not evenly distributed! - // Check CLDR release 24 to know more: http://cldr.unicode.org/index/downloads/cldr-24 - // - NSString *_s_ccode = [[NSLocale currentLocale] objectForKey: NSLocaleCountryCode]; - - // Collecting an ordered array of preferred languages - // - // RATIONALE: this is an ordered list of preferred languages, theoretically speaking - // Most people only configure one and thefore the rest of the list remains unchanged - // However, polyglots will impact this list deeply. - // I've chosen to only mark up to 8 languages. Infiniglots don't pass the Turing test. - // - // STRENGTH: something like 8! but not evenly distributed. - // - NSArray* preferredLang = [NSLocale preferredLanguages]; - NSString* _s_langs; - if (preferredLang == nil || ![preferredLang isKindOfClass:[NSArray class]] || [ preferredLang count ] == 0) - _s_langs = @"en"; - else - _s_langs = [[preferredLang subarrayWithRange:NSMakeRange(0, MIN(8,[preferredLang count]))] componentsJoinedByString:@""]; - - // Collecting a few more device specific discriminating strings - // - // RATIONALE: the goal is to reinfoce uniqueness / reduce collisions. - // 1/ memory size: iOS devices come in variations of 8Gb, 16Gb, 32Gb, and 64Gb - // 2/ OS version: unevenly distributed, most people tend to have the latest version - // in iOS world... which is good! distinctiveness value = close to zero - // 3/ timezone: assuming equal distribution, 24 variations. Though we intuitively - // all know which 8 to 10 timezones represent 90% of all iOS devices! - // - // STRENGTH: say 50 combinations. - // - NSDictionary *fattributes = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:nil]; - NSString *_s_disk = [[fattributes objectForKey:NSFileSystemSize] stringValue]; - NSString* _s_osv = [[UIDevice currentDevice] systemVersion]; - NSString* _s_tmz = [[NSTimeZone systemTimeZone] name]; - - // Marking the day, sameDay™, tomorrow or day after - // - // RATIONALE: this is another built-in and unique property of OpenIDFA - // The token is only valid for today (and the next two days). - // Tthe notion of day is pegged to the user himself, to their own - // biological clock. Assumption and experience is that most transactions - // occur within the same waking hours. So the notion of day was shifted - // by 4 hours (i.e. day begins at 4am, ends at 4am next day). - // The following study/graph by MixPanel illustrates this quite well: - // http://tctechcrunch2011.files.wordpress.com/2014/02/day-in-mobile-california.png?w=1280 - // When tracking precision requires more than sameDay™, then tracker - // can venture into getting 3 days worth of OpenIDFA tokens... - // - NSDateFormatter* dateFormatter = [ [ NSDateFormatter alloc ] init ]; - [ dateFormatter setDateFormat:@"yyMMdd" ]; - NSCalendar *calendar; - calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; - - NSDateComponents *hourShift = [[NSDateComponents alloc] init]; - [hourShift setHour:-4]; - NSDate *currentDay= [calendar dateByAddingComponents:hourShift toDate:[NSDate date] options:0]; - NSDateComponents *dayShift = [[NSDateComponents alloc] init]; - [dayShift setDay:1]; - - // Creating array of 3 OpenIDFAs - // - NSMutableArray* openIDFAs = [NSMutableArray arrayWithCapacity:3]; - for (int j=0; j<3; j++) { - - // Creating the fingerprint with the various elements - // PART 1: Boot Time - // PART 2: Mostly stable and unique elements over time - // PART 3: Day Stamp - // - NSString* _s_day = [dateFormatter stringFromDate:currentDay]; - NSString* fingerprint = [NSString stringWithFormat:@"%@ %@ %@ %@ %@ %@ %@ %@ %@ %@", - _s_bt, _s_disk, - _s_machine, _s_model, - _s_osv, _s_ccode, - _s_langs, _s_appmap, - _s_tmz, _s_day]; - - const char* str = [fingerprint UTF8String]; - unsigned char result[CC_SHA256_DIGEST_LENGTH]; - CC_SHA256(str, (CC_LONG)strlen(str), result); - - // Creating a UUID-like formatting by only taking one byte out of two from - // the SHA-256 Hash, and by inserting dashes where relevant. - // Should anyone see benefits in better compliance with http://www.itu.int/rec/T-REC-X.667/en - // especially perhaps the version number represented by bits 49 to 52, then raise your hand! - // - NSMutableString *hash = [NSMutableString stringWithCapacity:36]; - for(int i = 0; iSNOWPLOW_NO_OPENIDFA to your build settings. - - @return A string containing a formatted UUID for example E621E1F8-C36C-495A-93FC-0C247A3E6E5F. - */ -+ (NSString *) getOpenIdfa; - -/*! - @brief Returns a generated string unique to each device, used only for serving advertisements. This works only if you have the AdSupport library in your project. If you have it, but do not want to use IDFA, add the compiler flag SNOWPLOW_NO_IFA to your build settings. + @brief Returns a generated string unique to each device, used only for serving advertisements. This works only if you have the AdSupport library in your project and you enable the compiler flag SNOWPLOW_IDFA_ENABLED to your build settings. @return A string containing a formatted UUID for example E621E1F8-C36C-495A-93FC-0C247A3E6E5F. */ diff --git a/Snowplow/SPUtilities.m b/Snowplow/SPUtilities.m index deaed76c6..7d888a6e8 100644 --- a/Snowplow/SPUtilities.m +++ b/Snowplow/SPUtilities.m @@ -31,7 +31,6 @@ #if SNOWPLOW_TARGET_IOS -#import "OpenIDFA.h" #import #import #import @@ -86,31 +85,19 @@ + (bool ) isUUIDString:(nonnull NSString *)uuidString { return [[NSUUID alloc] initWithUUIDString:uuidString] != nil; } -+ (NSString *) getOpenIdfa { - NSString * idfa = nil; -#if SNOWPLOW_TARGET_IOS -#ifndef SNOWPLOW_NO_OPENIDFA - if (!SNOWPLOW_iOS_9_OR_LATER) { - idfa = [OpenIDFA sameDayOpenIDFA]; - } -#endif -#endif - return idfa; -} - /* The IDFA can be retrieved using selectors rather than proper instance methods because the compiler would complain about the missing AdSupport framework. As stated in the header file, this only works if you have the AdSupport library in your project. - If you have it, but do not want to use IDFA, add the compiler flag SNOWPLOW_NO_IFA to your build settings. - If you haven't AdSupport framework in your project it just compiles returning a nil advertisingIdentifier. + If you have it and you want to use IDFA, add the compiler flag SNOWPLOW_IDFA_ENABLED to your build settings. + If you haven't AdSupport framework in your project or SNOWPLOW_IDFA_ENABLED it's not set, it just compiles returning a nil advertisingIdentifier. Note that `advertisingIdentifier` returns a sequence of 0s when used in the simulator. Use a real device if you want a proper IDFA. */ + (NSString *) getAppleIdfa { #if SNOWPLOW_TARGET_IOS || SNOWPLOW_TARGET_TV -#ifndef SNOWPLOW_NO_IFA +#ifdef SNOWPLOW_IDFA_ENABLED Class ASIdentifierManagerClass = NSClassFromString(@"ASIdentifierManager"); if (!ASIdentifierManagerClass) return nil; diff --git a/Snowplow/Snowplow.h b/Snowplow/Snowplow.h index 679db5dcf..b2a040188 100644 --- a/Snowplow/Snowplow.h +++ b/Snowplow/Snowplow.h @@ -128,7 +128,6 @@ extern NSString * const kSPPlatformDeviceModel; // --- Mobile Context extern NSString * const kSPMobileCarrier; -extern NSString * const kSPMobileOpenIdfa; extern NSString * const kSPMobileAppleIdfa; extern NSString * const kSPMobileAppleIdfv; extern NSString * const kSPMobileNetworkType; diff --git a/Snowplow/Snowplow.m b/Snowplow/Snowplow.m index 25685f3e6..2acbc63d4 100644 --- a/Snowplow/Snowplow.m +++ b/Snowplow/Snowplow.m @@ -27,13 +27,13 @@ @implementation Snowplow // --- Version #if SNOWPLOW_TARGET_IOS -NSString * const kSPVersion = @"ios-1.6.2"; +NSString * const kSPVersion = @"ios-1.7.0"; #elif SNOWPLOW_TARGET_TV -NSString * const kSPVersion = @"tvos-1.6.2"; +NSString * const kSPVersion = @"tvos-1.7.0"; #elif SNOWPLOW_TARGET_WATCHOS -NSString * const kSPVersion = @"watchos-1.6.2"; +NSString * const kSPVersion = @"watchos-1.7.0"; #else -NSString * const kSPVersion = @"osx-1.6.2"; +NSString * const kSPVersion = @"osx-1.7.0"; #endif // --- Emitter @@ -120,7 +120,6 @@ @implementation Snowplow // --- Mobile Context NSString * const kSPMobileCarrier = @"carrier"; -NSString * const kSPMobileOpenIdfa = @"openIdfa"; NSString * const kSPMobileAppleIdfa = @"appleIdfa"; NSString * const kSPMobileAppleIdfv = @"appleIdfv"; NSString * const kSPMobileNetworkType = @"networkType"; diff --git a/SnowplowTracker.podspec b/SnowplowTracker.podspec index 37ab11fa1..7e50edbb2 100644 --- a/SnowplowTracker.podspec +++ b/SnowplowTracker.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SnowplowTracker" - s.version = "1.6.2" + s.version = "1.7.0" s.summary = "Snowplow event tracker for iOS, macOS, tvOS, watchOS for apps and games." s.description = <<-DESC Snowplow is a mobile and event analytics platform with a difference: rather than tell our users how they should analyze their data, we deliver their event-level data in their own data warehouse, on their own Amazon Redshift or Postgres database, so they can analyze it any way they choose. Snowplow mobile is used by data-savvy games companies and app developers to better understand their users and how they engage with their games and applications. Snowplow is open source using the business-friendly Apache License, Version 2.0 and scales horizontally to many billions of events. diff --git a/VERSION b/VERSION index fdd3be6df..bd8bf882d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.6.2 +1.7.0