I'm currently developing an iOS app that enables users to log in to the app using TouchID, but firstly they must set up a password inside the app first. Problem is, to show the setup password option to enable the TouchID login, I need to detect if the iOS device supports TouchID.
Using the LAContext and canEvaluatePolicy (like the answers in here If Device Supports Touch ID), I am able to determine whether the current device supports TouchID if the user has set up passcode on their iOS device. Here is a my code snippet (I'm using Xamarin, so it's in C#):
static bool DeviceSupportsTouchID ()
{
if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
var context = new LAContext();
NSError authError;
bool touchIDSetOnDevice = context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out authError);
return (touchIDSetOnDevice || (LAStatus) Convert.ToInt16(authError.Code) != LAStatus.TouchIDNotAvailable);
}
return false;
}
If the user has not set up the device passcode, the authError will just return "PasscodeNotSet" error regardless of whether the device actually supports TouchID or not.
If the user's device supports TouchID, I want to always show the TouchID option in my app regardless of whether the user has set up passcode on their device (I will just warn the user to setup passcode on their device first). Vice versa, if the user's device doesn't support TouchID, I obviously don't want to show the TouchID option in my app.
So my question is, is there a nice way to consistently determine whether an iOS device supports TouchID regardless of whether the user has set up passcode on their device?
The only workaround I can think of is to determine the architecture of the device (which is answered in Determine if iOS device is 32- or 64-bit), as TouchID is only supported on devices with 64-bit architecture. However, I'm looking if there's any nicer way to do this.
In conclusion of the discussion below, for the time being it is not possible to determine whether a device actually supports TouchID or not when the user hasn't set up passcode on their device.
I have reported this TouchID flaw on the Apple bug reporter. Those who want to follow the issue can see it on Open Radar here: http://www.openradar.me/20342024
Thanks #rckoenes for the input :)
EDIT
Turns out that someone has reported a similar issue already (#18364575). Here is Apple's reply regarding the issue:
"Engineering has determined that this issue behaves as intended based on the following information:
If passcode is not set, you will not be able to detect Touch ID presence. Once the passcode is set, canEvaluatePolicy will eventually return LAErrorTouchIDNotAvailable or LAErrorTouchIdNotEnrolled and you will be able to detect Touch ID presence/state.
If users have disabled passcode on phone with Touch ID, they knew that they will not be able to use Touch ID, so the apps don't need to detect Touch ID presence or promote Touch ID based features. "
So..... the final answer from Apple is No. :(
Note: similar StackOverflow question from the person who reported this -> iOS8 check if device has Touch ID
(wonder why I didn't find this question before despite my extensive searching...)
The correct way to detect if TouchID is available:
BOOL hasTouchID = NO;
// if the LAContext class is available
if ([LAContext class]) {
LAContext *context = [LAContext new];
NSError *error = nil;
hasTouchId = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error];
}
Sorry it is in Objective-C, you might have to translate it to C#.
You should refrain from checking the system version and just check whether or not the class or methods are available.
I know this is a question from last year, but this solution does not make what you need? (Swift code)
if #available(iOS 8.0, *) {
var error: NSError?
let hasTouchID = LAContext().canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error)
//Show the touch id option if the device has touch id hardware feature (even if the passcode is not set or touch id is not enrolled)
if(hasTouchID || (error?.code != LAError.TouchIDNotAvailable.rawValue)) {
touchIDContentView.hidden = false
}
}
Then, when the user presses the button to log in with touch id:
#IBAction func loginWithTouchId() {
let context = LAContext()
var error: NSError?
let reasonString = "Log in with Touch ID"
if (context.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error)) {
[context.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: reasonString, reply: { (success: Bool, evalPolicyError: NSError?) -> Void in
//Has touch id. Treat the success boolean
})]
} else {
//Then, if the user has touch id but is not enrolled or the passcode is not set, show a alert message
switch error!.code{
case LAError.TouchIDNotEnrolled.rawValue:
//Show alert message to inform that touch id is not enrolled
break
case LAError.PasscodeNotSet.rawValue:
//Show alert message to inform that passcode is not set
break
default:
// The LAError.TouchIDNotAvailable case.
// Will not catch here, because if not available, the option will not visible
}
}
}
Hope it helps!
For Objective C
It works great on all devices without checking device version.
- (void)canAuthenticatedByTouchID{
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
NSString *myLocalizedReasonString = touchIDRequestReason;
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
}else{
switch (authError.code) {
case kLAErrorTouchIDNotAvailable:
[labelNotSupportTouchID setHidden:NO];
[switchBtn setHidden:YES];
[labelEnableTouchid setHidden:YES];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self showAlertMessage:#"EyeCheck Pro" message:#"Device does not support Touch ID Service."];
});
break;
}
}
}
Here is a bit tedious way to figure out if device has physical touch id sensor.
+ (BOOL)isTouchIDExist {
if(![LAContext class]) //Since this mandotory class is not there, that means there is no physical touch id.
return false;
//Get the current device model name
size_t size;
sysctlbyname("hw.machine", NULL, &size, NULL, 0);
char *model = malloc(size);
sysctlbyname("hw.machine", model, &size, NULL, 0);
NSString *deviceModel = [NSString stringWithCString:model encoding:NSUTF8StringEncoding];
//Devices that does not support touch id
NSArray *deviceModelsWithoutTouchID = [[NSArray alloc]
initWithObjects:
#"iPhone1,1", //iPhone
#"iPhone1,2", //iPhone 3G
#"iPhone2,1", //iPhone 3GS
#"iPhone3,1", //iPhone 4
#"iPhone3,2",
#"iPhone3,3",
#"iPhone4,1", //iPhone 4S
#"iPhone5,1", //iPhone 5
#"iPhone5,2",
#"iPhone5,3", //iPhone 5C
#"iPhone5,4",
#"iPod1,1", //iPod
#"iPod2,1",
#"iPod3,1",
#"iPod4,1",
#"iPod5,1",
#"iPod7,1",
#"iPad1,1", //iPad
#"iPad2,1", //iPad 2
#"iPad2,2",
#"iPad2,3",
#"iPad2,4",// iPad mini 1G
#"iPad2,5",
#"iPad2,5",
#"iPad2,7",
#"iPad3,1", //iPad 3
#"iPad3,2",
#"iPad3,3",
#"iPad3,4", //iPad 4
#"iPad3,5",
#"iPad3,6",
#"iPad4,1", //iPad Air
#"iPad4,2",
#"iPad4,3",
#"iPad4,4", //iPad mini 2
#"iPad4,5",
#"iPad4,6",
#"iPad4,7",
nil];
return ![deviceModelsWithoutTouchID containsObject:deviceModel];
}
Reference:
https://www.theiphonewiki.com/wiki/Models
https://en.wikipedia.org/wiki/IOS
Following is the way by which you can identify whether Touch Id or Face ID is supported on the device
open class LocalAuth: NSObject {
public static let shared = LocalAuth()
private override init() {}
var laContext = LAContext()
func canAuthenticate() -> Bool {
var error: NSError?
let hasTouchId = laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
return hasTouchId
}
func hasTouchId() -> Bool {
if canAuthenticate() && laContext.biometryType == .touchID {
return true
}
return false
}
func hasFaceId() -> Bool {
if canAuthenticate() && laContext.biometryType == .faceID {
return true
}
return false
}
}
And following is the Usage of the above-shared code
if LocalAuth.shared.hasTouchId() {
print("Has Touch Id")
} else if LocalAuth.shared.hasFaceId() {
print("Has Face Id")
} else {
print("Device does not have Biometric Authentication Method")
}
For iOS 11+ you can use biometryType: LABiometryType of LAContext. More from Apple documentation:
/// Indicates the type of the biometry supported by the device.
///
/// #discussion This property is set only when canEvaluatePolicy succeeds for a biometric policy.
/// The default value is LABiometryTypeNone.
#available(iOS 11.0, *)
open var biometryType: LABiometryType { get }
#available(iOS 11.0, *)
public enum LABiometryType : Int {
/// The device does not support biometry.
#available(iOS 11.2, *)
case none
/// The device does not support biometry.
#available(iOS, introduced: 11.0, deprecated: 11.2, renamed: "LABiometryType.none")
public static var LABiometryNone: LABiometryType { get }
/// The device supports Touch ID.
case touchID
/// The device supports Face ID.
case faceID
}
For iOS 11+, for context error, you can check for kLAErrorBiometryNotAvailable
Related
I want to get the string for the current lock type used in the device, whether it is FaceID, touchID or PassCode. Below is my code :-
func getBiometricType() -> String {
var biometricType: Global.BiometricType {
let context = LAContext()
var error: NSError?
guard context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) else { return .none }
if #available(iOS 11.0, *) {
switch context.biometryType {
case .touchID:
return .touchID
case .faceID:
return .faceID
case .none:
return .none
}
} else {
guard context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) else { return .none }
return context.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil) ?
.touchID : .PIN
}
}
return biometricType.rawValue
}
But this canEvaluatePolicy only checks whether the device supports biometric or not. Even when the FaceID is not set up yet but the Passcode is enabled, it does not give info regarding the passcode. As I need to show the type enabled is "Passcode". Is there any way to achieve this?
You have to use LAPolicy.deviceOwnerAuthenticationWithBiometrics.
As per apple docs:
If Touch ID or Face ID is not available or not enrolled, policy
evaluation fails. After three unsuccessful Touch ID or Face ID
attempts in a row, policy evaluation will fail. Both Touch ID and Face
ID authentication are disabled after five unsuccessful attempts,
requiring the user to enter the device passcode in order to be
reenabled.
LAPolicy.deviceOwnerAuthentication enables:
If Touch ID or Face ID is available, enrolled, and not disabled, the
user is asked for that first. Otherwise, they are asked to enter the
device passcode. If the device passcode is not enabled, policy
evaluation fails. Passcode authentication is disabled after 6
unsuccessful attempts, with progressively increasing delays between
them.
In our app, the user has to register to the device biometry in order to use it for authentication.
The registration text and legal notes are according to the relevant biometry (register to touch ID or register to face ID)
As far as I found, the biometry type is obtainable via the LAContext, but if the user denies usage of biometry, then the context returns biometryType=.none
Any ideas other that asking for the screen size and comparing to iphone X (bad bad code)?
static fileprivate var biometryType: DSLocalAuthenticationBiometryType {
let context = LAContext()
var error: NSError?
let _ = context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
if #available(iOS 11.0, *) {
return context.biometryType == .typeFaceID ? .typeFaceID : .none
}
else {
return .none
}
}
Thanks
I've got the same identical issue, and I've just found out that if you evaluate against the key LAPolicyDeviceOwnerAuthentication instead of LAPolicyDeviceOwnerAuthenticationWithBiometrics, even after the user declined the permission, the evaluation succeeds and you get the correct biometryType. Your code would be like
static fileprivate var biometryType: DSLocalAuthenticationBiometryType {
let context = LAContext()
var error: NSError?
let _ = context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error)
if #available(iOS 11.0, *) {
return context.biometryType == .typeFaceID ? .typeFaceID : .none
}
else {
return .none
}
}
NOTE: on devices without touch id and face id, it still returning YES, so you would not know whether the device really has a biometric hw or not with iOS lower than 11 (which do not expose the property biometriyType)
Update
For devices with iOS version 10 or lower, you can use the
LAPolicyDeviceOwnerAuthenticationWithBiometrics as usual, it will behave correctly (returning true whether the device supports the touch Id), so it's just a matter of differentiating the running OS version :)
Let me know if it works :)
Best
I've running the latest Xcode 9 GM (13 Sep 2017) and have set Hardware > Face ID > Enrolled in simulator as well as Deployment Target 11.0. However I'm getting error code -6 LAErrorTouchIDNotAvailable.
Is there some setting I'm missing?
let myContext = LAContext()
let myLocalizedReasonString = "You are pretty"
var authError: NSError?
if #available(iOS 8.0, macOS 10.12.1, *) {
if myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) {
myContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: myLocalizedReasonString) { success, evaluateError in
if success {
print("// User authenticated successfully, take appropriate action")
} else {
print(" // User did not authenticate successfully, look at error and take appropriate action")
}
}
} else {
print(" // Could not evaluate policy; look at authError and present an appropriate message to user")
}
} else {
print(" // Fallback on earlier versions")
}
Face ID does not work in the Xcode 9 GM due to a framework bug. Xcode 9.1 fixes this issue.
You can test your app in the iPhone 8 simulator and ensure it works correctly with Touch ID or run the Xcode 9.1 beta and test Face ID support there.
I think the iphone X simulator's faceID doesn't work at the moment, hopefully they will fix it soon...
https://forums.developer.apple.com/thread/86779
we could do a bug report to see if it speed things along :P
https://developer.apple.com/bug-reporting
Face ID is working now, with Xcode 9.1. Follow these steps to test it in Simulator.
Add privacy statement in your target's info.plist file.
Import LocalAuthentication framework to your project and add following code to your view controller and try with Face-ID
import LocalAuthentication
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
localAuthentication()
}
func localAuthentication() -> Void {
let laContext = LAContext()
var error: NSError?
let biometricsPolicy = LAPolicy.deviceOwnerAuthenticationWithBiometrics
if (laContext.canEvaluatePolicy(biometricsPolicy, error: &error)) {
if let laError = error {
print("laError - \(laError)")
return
}
var localizedReason = "Unlock device"
if #available(iOS 11.0, *) {
if (laContext.biometryType == LABiometryType.faceID) {
localizedReason = "Unlock using Face ID"
print("FaceId support")
} else if (laContext.biometryType == LABiometryType.touchID) {
localizedReason = "Unlock using Touch ID"
print("TouchId support")
} else {
print("No Biometric support")
}
} else {
// Fallback on earlier versions
}
laContext.evaluatePolicy(biometricsPolicy, localizedReason: localizedReason, reply: { (isSuccess, error) in
DispatchQueue.main.async(execute: {
if let laError = error {
print("laError - \(laError)")
} else {
if isSuccess {
print("sucess")
} else {
print("failure")
}
}
})
})
}
}
}
FaceID authentication will prompt you for first time to allow FaceID detection for your app.
Now enable Face ID enrolment and run your app to test Face ID simulation Testing.
Here is simulation result for matching and non-matching faces.
Result for matching face:
Result for non-matching face:
XCode 9.1 beta came out today in which the original code should work perfectly in the simulator!
According to Apples Documentation for LAContext, we need to add the key NSFaceIDUsageDescription with a reason of use String, as that will display the authorisation request for the use of FaceId on the device.
Example add this to info.plist:
NSFaceIDUsageDescription
set it to type String, and add a text that you want to be shown, in the prompt request for access to the Face ID camera.
"Your app" request your permission to use Face ID, for you to login to your account / unlock your notes / what ever reason in the end.
By adding this, you can go to the simulator for iPhone X, and you will be prompted for the Face ID, press accept, and everything should work perfectly.
Remember to enrol biometry support for the simulator by going into
Simulator -> Hardware -> Face ID / Touch ID -> Enrolled
Then you just need to pressed the Match / Non-Matching Touch / Face ID, to test out your handling
For more details and check out Apple's documentation: https://developer.apple.com/documentation/localauthentication/lacontext
---- Edit ----
This worked for me in both Xcode 9.0 and 9.1
I want to to show message in my application when haptic feedback is disabled in phone settings. How to detect that haptic feedback is disabled in device settings?
It's kludgy, but might this work?
- (BOOL)isHapticFeedbackDisabled {
BOOL result = NO;
UISelectionFeedbackGenerator *feedbackGenerator = [[UISelectionFeedbackGenerator alloc] init];
[feedbackGenerator prepare];
if ([feedbackGenerator.description containsString:#"prepared=0"]) result = YES;
feedbackGenerator = nil;
return result;
}
There is no way to check Haptic Feedback is enabled/disabled but there is private int _feedbackSupportLevel in UIKit for checking if device supports it:
func logFeedbackSupported() {
let supportLevel = UIDevice.current.value(forKey: "_feedbackSupportLevel")
print(supportLevel ?? "")
}
0: Not available,
1: First generation available (< iPhone 7),
2: Second generation available.
I advise you not to use Apples private APIs because:
The API could be changed in any version without you knowing about it.
Apple is parsing your app code to find out if you're using private API so be aware. Your app could be rejected.
I would like to get the device IDFA. How to get this info from iOS official API ?
First, you have to ask permission from the user to use their IDFA:
#import <AppTrackingTransparency/AppTrackingTransparency.h>
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
// Tracking authorization completed. Start loading ads here.
}];
This permission flow will only run once, the first time it is called, even if you re-start the app and/or call it again. If you want to answer differently, you'll have to delete the app off the device or simulator completely and reinstall it. Note that iOS simulators return blanked IDFAs (all zeroes) no matter what the answer to the permission flow. See https://developer.apple.com/documentation/apptrackingtransparency for details, including how to customize the message shown to users when asking to track them. Note that many advertising SDKs have their own consent flow calls that you can use.
To actually get the IDFA once you have permission:
#import <AdSupport/ASIdentifierManager.h>
If you would like to get it as an NSString, use:
[[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]
So your code might look like this:
NSString *idfaString = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
NSLog (#"IDFA: %#", idfaString);
You first have to check if user user has decided to opt out from ad tracking. Only if he allowed it you can use the IDFA.
You can check it by calling isAdvertisingTrackingEnabled method of ASIdentifierManager.
isAdvertisingTrackingEnabled
Check the value of this property before performing any advertising
tracking. If the value is NO, use the advertising identifier only for
the following purposes: frequency capping, conversion events,
estimating the number of unique users, security and fraud detection,
and debugging.
The following code snippet shows how to obtain a string value of IDFA.
ObjC
#import AdSupport;
- (NSString *)identifierForAdvertising {
// Check whether advertising tracking is enabled
if([[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled]) {
NSUUID *identifier = [[ASIdentifierManager sharedManager] advertisingIdentifier];
return [identifier UUIDString];
}
// Get and return IDFA
return nil;
}
Swift
import AdSupport
func identifierForAdvertising() -> String? {
// Check whether advertising tracking is enabled
guard ASIdentifierManager.shared().isAdvertisingTrackingEnabled else {
return nil
}
// Get and return IDFA
return ASIdentifierManager.shared().advertisingIdentifier.uuidString
}
IDFA - Identifier for Advertising
isAdvertisingTrackingEnabled -> trackingAuthorizationStatus
From iOS v14 Apple deprecated isAdvertisingTrackingEnabled and moved the logic into AppTrackingTransparency Framework. Now user has to grand a permission to read idfa(in the same way as Location permission)
User can control it via:
#iOS 13
#AdSupport
#ASIdentifierManager.shared().isAdvertisingTrackingEnabled
Settings -> Privacy -> Advertising -> Limit Ad Tracking
#iOS 14
#AppTrackingTransparency
#ATTrackingManager.trackingAuthorizationStatus
#a global flag
Settings -> Privacy -> Tracking -> `Allow Apps to Request to Track`
#or select an app from list to control every app separately
#a local flag
Settings -> <app_name> -> Allow Tracking
Implementation
import AppTrackingTransparency
import AdSupport
func getIDFA() -> String? {
// Check whether advertising tracking is enabled
if #available(iOS 14, *) {
if ATTrackingManager.trackingAuthorizationStatus != ATTrackingManager.AuthorizationStatus.authorized {
return nil
}
} else {
if ASIdentifierManager.shared().isAdvertisingTrackingEnabled == false {
return nil
}
}
return ASIdentifierManager.shared().advertisingIdentifier.uuidString
}
ASIdentifierManager is the official way to garner the Advertising Identification Number from a device running iOS 6+. You can use -[[ASIdentifierManager sharedManager] advertisingIdentifier]; to get it.
Get IDFA in Swift:
import AdSupport
...
let myIDFA: String?
// Check if Advertising Tracking is Enabled
if ASIdentifierManager.sharedManager().advertisingTrackingEnabled {
// Set the IDFA
myIDFA = ASIdentifierManager.sharedManager().advertisingIdentifier.UUIDString
} else {
myIDFA = nil
}
Beginning in iOS 10, when a user enables “Limit Ad Tracking,” the OS will send along the advertising identifier with a new value of “00000000-0000-0000-0000-000000000000.”
As per this article: https://fpf.org/2016/08/02/ios-10-feature-stronger-limit-ad-tracking/
Here's a commented helper class in Swift that will give you a nil object for the identifier if the user has turned advertisement tracking off:
import AdSupport
class IDFA {
// MARK: - Stored Type Properties
static let shared = IDFA()
// MARK: - Computed Instance Properties
/// Returns `true` if the user has turned off advertisement tracking, else `false`.
var limited: Bool {
return !ASIdentifierManager.shared().isAdvertisingTrackingEnabled
}
/// Returns the identifier if the user has turned advertisement tracking on, else `nil`.
var identifier: String? {
guard !limited else { return nil }
return ASIdentifierManager.shared().advertisingIdentifier.uuidString
}
}
Just add it to your project (for example in a file named IDFA.swift) and link the AdSupport.framework in your target via the "Linked Frameworks and Libraries" section in the General settings tab.
Then you can use it like this:
if let identifier = IDFA.shared.identifier {
// use the identifier
} else {
// put any fallback logic in here
}
Swift 3 & 4
var IDFA = String()
if ASIdentifierManager.shared().isAdvertisingTrackingEnabled {
IDFA = ASIdentifierManager.shared().advertisingIdentifier
}
Please pay attention that in iOS 14, ASIdentifierManager.shared().isAdvertisingTrackingEnabled is deprecated. please use ATTrackingManager.trackingAuthorizationStatus == .authorized instead.
import AdSupport
import AppTrackingTransparency
extension ASIdentifierManager {
//NOTE: if the user has enabled Limit Ad Tracking, this IDFA will be all zeros on a physical device
static var identifierForAdvertising: String {
// Check whether advertising tracking is enabled
if #available(iOS 14, *) {
guard ATTrackingManager.trackingAuthorizationStatus == .authorized else {
return ""
}
} else {
guard ASIdentifierManager.shared().isAdvertisingTrackingEnabled else {
return ""
}
}
// Get and return IDFA
return ASIdentifierManager.shared().advertisingIdentifier.uuidString
}
}
A nicer approach to get the IDFA or nil if tracking is disabled via iOS Setting is using a (private) extension:
import AdSupport
class YourClass {
func printIDFA() {
print(ASIdentifierManager.shared().advertisingIdentifierIfPresent)
}
}
private extension ASIdentifierManager {
/// IDFA or nil if ad tracking is disabled via iOS system settings
var advertisingIdentifierIfPresent: String? {
if isAdvertisingTrackingEnabled {
return advertisingIdentifier.uuidString
}
return nil
}
}
Just to extend Amro's Swift answer, here's similar code wrapped in a method:
import AdSupport
...
func provideIdentifierForAdvertisingIfAvailable() -> String? {
if ASIdentifierManager.sharedManager().advertisingTrackingEnabled {
return ASIdentifierManager.sharedManager().advertisingIdentifier?.UUIDString ?? nil
} else {
return nil
}
}
Swift 5 with encapsulation:
import AdSupport
struct ID{
static var advertising: String? {
// Firstly, Check whether advertising tracking is enabled
guard ASIdentifierManager.shared().isAdvertisingTrackingEnabled else {
return nil
}
// Then, Get and return IDFA
return ASIdentifierManager.shared().advertisingIdentifier.uuidString
}
}