Jotting down what I've learnt. - Hong C.
Programming

Adding AdMob GDPR Consent Message for iOS Using SwiftUI

Why?

We are now required to add a GDPR consent message to our apps after 16 Jan 2024 because of EEA requirements to show ads to users in Europe.

If we don’t do so, the revenue from the ads will be largely impacted.

How?

You can watch this official video from Google AdMob to learn how to implement this.

However, like always, Google documentation just shows you the UIKit way to add it, not SwiftUI.

SwiftUI Example

After a day of exploring, I finally got it to work using pure SwiftUI.

It is actually pretty simple:


import SwiftUI
import UserMessagingPlatform

struct ContentView: View {
  @AppStorage("gdprConsentShown") var gdprConsentShown = false

  var body: some View {
      VStack {
      }
        .onAppear {
          gdprConsentCheck()
        }
  }

    func gdprConsentCheck() {
        guard !gdprConsentShown else { return }

        // Create a UMPRequestParameters object.
        let parameters = UMPRequestParameters()
        let debugSettings = UMPDebugSettings ()
        // - This is for mimicking EEA users when you're outside the EU.
        // - Use a simulator so you can read the consent form.
        debugSettings.geography = .EEA
        parameters.debugSettings = debugSettings

        // Set tag for under age of consent. false means users are not under the age of consent. If you turn this on, no consent form is presented.
        parameters.tagForUnderAgeOfConsent = false

        // Request an update for the consent information.
        UMPConsentInformation.sharedInstance.requestConsentInfoUpdate(with: parameters) { (requestConsentError) in
            if let consentError = requestConsentError {
                // Consent gathering failed.
                return print("Error: \(consentError.localizedDescription)")
            }
            // Load and present the consent form.
            loadAndPresentConsentForm()
        }
    }

    func loadAndPresentConsentForm() {
        UMPConsentForm.load { (form, loadError) in
            if let error = loadError {
                // Consent form could not be loaded.
                return print("Error: \(error.localizedDescription)")
            }

            if let form = form {
                if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
                    if let window = windowScene.windows.first {
                        if let rootViewController = window.rootViewController {
                            form.present(from: rootViewController) { (dismissError) in
                                if let error = dismissError {
                                    // Consent form could not be dismissed.
                                    print("Error: \(error.localizedDescription)")
                                    gdprConsentShown = false
                                } else {
                                    // Consent form was dismissed.
                                    // Check the consent status.
                                    let status = UMPConsentInformation.sharedInstance.consentStatus
                                    print("Consent status: \(status)")
                                    gdprConsentShown = true
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

Yes, the if let looks ugly, because it’s half ChatGPTed, but you get the idea. I modified it and put everything in another view.

So for every app I’m making, I just copy and paste that view. Here’s how I would use it in my main view:

    //for ads and consent form
    @State var readyToShow = false
    @State private var bannerView: BannerView? = nil
    /////
    context
        .onAppear() {
            // create the banner onAppear, readyToShow is false by default
            bannerView = BannerView(readyToShow: $readyToShow, adUnitID: "ca-app-pub-yourKeyHere")
            // This performs the consent form logic inside the bannerView, if everything goes well, toggle readyToShow to true.
            bannerView?.gdprConsentCheck()
        }
        .toolbar {
            ToolbarItem(placement: .bottomBar) {
                // So only users who passed the consent form will see this banner ad.
                if !adRemoved && readyToShow{
                    bannerView
                        .frame(width: GADAdSizeBanner.size.width, height: GADAdSizeBanner.size.height)
                }

            }
        }

Personalised Ads

To control whether to show personalized ads, we used to set the ads request option to something like this:

import GoogleMobileAds

let request = GADRequest()
let extras = GADExtras()
extras.additionalParameters = ["npa": "1"] // This means no personalized ads are shown
request.register(extras)

Remember, we need to set up ATT for App Store compliance.

However, it serves no purpose to interact with Google AdMob now. It is because when you use UserMessagingPlatform, Google manages the “personalization” on their platform.

“In case this is still helpful for anyone, confirming that using the UMP SDK for Android you do not need to set npa=1. The UMP SDK writes the TCF string to shared preferences (related info at https://support.google.com/admob/answer/9760862), and the Google Mobile Ads SDK reads the TCF string on ad requests.” – Eric, Mobile Ads SDK Team – Read more

That’s what I read on Google AdMob forum.

Although they said It’s for Android, I’d assume it works the same way on iOS.

But still, we need to implement the ATT for Apple anyway, so I’m keeping it for now.

Hope this guide simplifies things a bit and helps you.

Share:

Article written by:

Hong C.

Passionate in programming, music, languages, and learning new things.

Currently working on: ZenTube - decluttered: iOS YouTube app

Follow me on: Bluesky

Personal website: HongCT.net

Leave a Reply

Your email address will not be published. Required fields are marked *

back to top