10

In iOS 14, It could display ATT (App Tracking Transparency) dialog when app starts in SwiftUI as follows.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    if #available(iOS 14, *) {
        ATTrackingManager.requestTrackingAuthorization(completionHandler: { status in
            // loadAd()
        })
    } else {
        // loadAd()
    }
    return true
}

But, in iOS 15.0, it does not work. Apple document describes as follows.

Calls to the API only prompt when the application state is: UIApplicationStateActive. Calls to the API through an app extension do not prompt. https://developer.apple.com/documentation/apptrackingtransparency/attrackingmanager/3547037-requesttrackingauthorization

How to display ATT dialog when the app starts in iOS 15 ?

2021/9/28 update I solved it as follows.

struct HomeView: View {
    var body: some View {
        VStack {
            Text("Hello!")
        }.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in
            ATTrackingManager.requestTrackingAuthorization(completionHandler: { status in })
        }
    }
}
iUrii
  • 11,742
  • 1
  • 33
  • 48
donchan
  • 197
  • 1
  • 8
  • seems like a nice solution - but it does not work (iOS 15.1). Any other ideas ? – iKK Dec 03 '21 at 18:27

5 Answers5

12

Instead of calling app tracking transparency permission in didFinishLaunchingWithOptions call in applicationDidBecomeActive it will solve your issue

In AppDelegate

func applicationDidBecomeActive(_ application: UIApplication) {
    requestDataPermission()
}

func requestDataPermission() {
    if #available(iOS 14, *) {
        ATTrackingManager.requestTrackingAuthorization(completionHandler: { status in
            switch status {
            case .authorized:
                // Tracking authorization dialog was shown
                // and we are authorized
                print("Authorized")
            case .denied:
                // Tracking authorization dialog was
                // shown and permission is denied
                print("Denied")
            case .notDetermined:
                // Tracking authorization dialog has not been shown
                print("Not Determined")
            case .restricted:
                print("Restricted")
            @unknown default:
                print("Unknown")
            }
        })
    } else {
        //you got permission to track, iOS 14 is not yet installed
    }
}

in info.plist

<key>NSUserTrackingUsageDescription</key>
<string>Reason_for_data_tracking</string>
Siddhesh Bhide
  • 374
  • 5
  • 9
  • 2
    or if you are using scenes "sceneDidBecomeActive" in the `UISceneDelegate` subclass, as UIKit is calling that method instead of the one in the app delegate then, and also add a delay of 1 second, as otherwise it just returns the `notDetermined` status without showing the dialog – Stoyan Sep 29 '21 at 15:10
  • Yes its the timing issue in iOS 15. For below version its working fine and apple is rejecting most of the apps because of this issue as they are testing the new apps on iOS 15 devices – Siddhesh Bhide Oct 01 '21 at 07:50
  • This works for such projects only that are created newly. Thanks! – Sheikh Wahab Mahmood Oct 02 '21 at 06:57
  • i think you are missing override keyword – Kedar Sukerkar Oct 07 '21 at 22:20
7

As @donchan already mentioned use the following code:

struct HomeView: View {
    var body: some View {
        VStack {
            Text("Hello!")
        }.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in
            ATTrackingManager.requestTrackingAuthorization(completionHandler: { status in })
        }
    }
}
4

For iOS 15, I faced the same problems and I fixed them by delaying code execution for a second.

DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
        if #available(iOS 14, *) {
            ATTrackingManager.requestTrackingAuthorization(completionHandler: { status in
                DispatchQueue.main.async {
                    self.bannerView.load(GADRequest())
                    self.interstitial.load(request)
                }
            })
        } else {
            // Fallback on earlier versions
            self.bannerView.load(GADRequest())
            self.interstitial.load(request)
        }
    }
0

A very important addition to all the answers above: the ATT dialogue must be invoked once! If, for example, inside the advertising manager you have repeated calls to the ATT dialog before requesting an advertisement (as it was for previous OS versions), then the dialog WILL NOT be shown! Therefore, the ATT dialogue request must be inserted directly into the view and with a delay of at least 1 second for its unconditional triggering.

Sergei Volkov
  • 818
  • 8
  • 15
-1

If you are writing a SwiftUI app, you can trigger it on your start screen.

struct HomeView: View {
    
    var body: some View {
        VStack {
            Text("Hello!")
        }.onAppear {
            ATTrackingManager.requestTrackingAuthorization(completionHandler: { status in })
        }
    }
}

Do not forget to add the necessary additions to the .plist.

<key>NSUserTrackingUsageDescription</key>
<string>...</string>

Thus, it will run on the simulator or real device.

uikenan
  • 64
  • 2
  • Thank you, but it does not work it. I solved it myselft. – donchan Sep 27 '21 at 22:04
  • This is a timing issue, giving this a 1sec delay shows that it does indeed work. This seems to be an iOS 15 issue... – Florian S Sep 28 '21 at 15:56
  • If you get notification or other permissions before this process, it will not work. You must need write this code block in ATT completion block. – uikenan Sep 28 '21 at 23:11