Get the IDFA in iOS14 with Xcode11

If you find yourself in a situation you must obtain the IDFA in new iOS14 but you are not ready for new Xcode12 this is a hopefully legal way of getting this.

This method is not new and works thanks to obj-c dynamic features :). I am no one to recommend this code for apps that goes throught the AppStore. Probably it might pass Apple's review. Anyway, this approach is definetely good for investigations, prototypes, etc.

// HackyTrackingManager.h

@interface HackyTrackingManager: NSObject {
}
+(void)requestIdfa:(void(^)(BOOL granted, NSString* idfaUUID))completion;
@end
// HackyTrackingManager.m

#import AdSupport/AdSupport.h
#import "dlfcn.h"

// Declare interfaces of ATTrackingManager and ATTrackingManagerAuthorizationStatus to trick the compiler

NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, ATTrackingManagerAuthorizationStatus) {
    ATTrackingManagerAuthorizationStatusNotDetermined = 0,
    ATTrackingManagerAuthorizationStatusRestricted,
    ATTrackingManagerAuthorizationStatusDenied,
    ATTrackingManagerAuthorizationStatusAuthorized
} NS_SWIFT_NAME(ATTrackingManager.AuthorizationStatus);
@interface ATTrackingManager : NSObject
@property (class, nonatomic, readon ly, assign) ATTrackingManagerAuthorizationStatus trackingAuthorizationStatus;
+ (void)requestTrackingAuthorizationWithCompletionHandler:(void(^)(ATTrackingManagerAuthorizationStatus status))completion;
@end
NS_ASSUME_NONNULL_END

// Implement the trick

@implementation HackyTrackingManager

+(void)requestIdfa:(void(^)(BOOL granted, NSString* idfaUUID))completion {
    
    // Attempt to use iOS14's ATTrackingManager and for that we need to (dinamically) load AppTrackingTransparency
    dlopen("/Library/Frameworks/AppTrackingTransparency.framework/AppTrackingTransparency", RTLD_LAZY);
    Class tm = NSClassFromString(@"ATTrackingManager");

    if (tm) {  
        // iOS14 and later
        [tm requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
            ASIdentifierManager *im = [ASIdentifierManager sharedManager];
            completion(status == ATTrackingManagerAuthorizationStatusAuthorized, im.advertisingIdentifier.UUIDString);
        }];
    } else {
        // iOS13 and prior
        ASIdentifierManager *im = [ASIdentifierManager sharedManager];
        completion([im isAdvertisingTrackingEnabled], im.advertisingIdentifier.UUIDString);
    }
}

Explanation

First, since Xcode11 does not know anything about iOS14 SDK we need some @interfaces for ATTrackingManager and ATTrackingManagerAuthorizationStatus that tricks the compiler. The real trick about this is I don't provide @implementation for ATTrackingManager because the real implementation is already inside iOS14 AppTrackingTransparency.framework.

Second, we need to use ATTrackingManager and in order to do so we need to get the class dinamically using NSClassFromString and since Xcode11 does not have iOS14 SDK at compile time we need to load new framework AppTrackingTransparency at runtime by ourselves using dlopen. This will ensure NSClassFromString(@"ATTrackingManager") succeeds in iOS14.

Third, we check ATTrackingManager is there. In iOS14 this succeeds so we use requestTrackingAuthorizationWithCompletionHandler. Inside the block we can use get the status and use AdSupport.framework classes to get the IDFA. In iOS13 and prior OSes this will fail so we go inside the else and directly use AdSupport.framework classes.

I hope it helps

0 comments :

This work is licensed under BSD Zero Clause License | nacho4d ®