App Tracking Transparency con Combine

Come incapsulare ATTrackingManager e ASIdentifierManager in un ObservableObject con Combine, per chiedere il consenso al tracking e leggere l'advertising ID.

Read in English

Tradotto dall’inglese con l’aiuto dell’IA e rivisto dall’autore.Leggi l’originale

Un cielo chiaro sul mare con un gabbiano in volo, una alta ciminiera di mattoni a sinistra e una nave all'orizzonte.

Una delle funzionalità più pubblicizzate di iOS 14.5 è la nuova App Tracking Transparency. In giro ci sono moltissimi articoli che cercano di chiarire le posizioni in gioco in questa decisione di Apple e delle controparti come Facebook. Non voglio addentrarmi nell'aspetto politico della questione. Vorrei invece fare una rapida panoramica di ciò che serve per lavorare con questa novità e di cosa significa dal punto di vista tecnologico.

App Tracking Transparency si basa su un solo componente software, una classe chiamata ATTrackingManager.

Swift
class ATTrackingManager : NSObject

Secondo Apple questa classe offre tutto il necessario per chiedere il permesso e ottenere lo stato attuale della richiesta. Collabora con l'Ad framework per identificare gli utenti in forma anonima e consentire di tracciarne abitudini e interessi.

Diagramma: ATTrackingManager fornisce l'AuthorizationStatus e ASIdentifierManager fornisce l'advertisingIdentifier

L'Ad framework ha un'istanza condivisa, disponibile in tutto il sistema, sotto forma di una classe chiamata ASIdentifierManager. Possiamo ottenerla nel modo classico:

Swift
ASIdentifierManager.shared()

Inoltre possiamo leggere l'identificatore anonimo corrente in modo altrettanto semplice:

Swift
ASIdentifierManager.shared().advertisingIdentifier

Prima di iOS 14.5 voi (sviluppatori) avevate sempre la possibilità di ottenere questo identificatore sotto forma di oggetto UUID. L'identificatore è anonimo e coerente in tutto il sistema operativo, e permette di tracciare gli utenti tra app e domini diversi. Questo, ovviamente, può compromettere la privacy se uno o più attori in gioco hanno cattive intenzioni (e, credetemi, di attori malintenzionati ce ne sono tantissimi in giro).

Diagramma: ASIdentifierManager.advertisingIdentifier al centro, condiviso con nove app diverse rappresentate da quadrati colorati

Da iOS 14.5 bisogna chiedere il consenso dell'utente, app per app, prima di poter accedere a questo identificatore. Significa che voi (utenti) potete decidere in ogni momento chi può accedere al vostro identificatore, sia all'installazione dell'app sia, se cambiate idea, dalle Impostazioni. Dal punto di vista dei diritti dell'utente è fantastico, e non è nemmeno discutibile.

Impostazioni di iOS, Privacy, Tracking: l'interruttore Allow Apps to Request to Track e un interruttore per l'app AskPermissions, entrambi attivi

Il nostro obiettivo di oggi è incapsulare queste classi in un oggetto più comodo, da usare sia con Swift sia con SwiftUI. Per riuscirci useremo alcune funzionalità di base di Combine, in modo da esporre correttamente tutti i pezzi che ci servono.

Per brevità useremo un approccio SwiftUI puro, ma tutto il codice sarà pronto anche per i progetti Swift standard. Cominciamo creando un progetto SwiftUI.

La prima cosa da fare è aggiungere la descrizione nel nostro Info.plist, in modo da popolare correttamente il messaggio che vedranno gli utenti. Come con CoreLocation o Photos, dobbiamo descrivere con una frase efficace cosa ci serve e perché chiediamo il consenso al tracking. Per farlo aggiungiamo una nuova chiave NSUserTrackingUsageDescription e la relativa stringa.

Sorgente di Info.plist con la chiave NSUserTrackingUsageDescription e la stringa I would like to track you for this reason.

Quando siamo pronti, possiamo scrivere la classe helper che esporrà alla nostra app gli accessor necessari. L'abbiamo chiamata ATTrackingHelper. Poiché vogliamo essere notificati a ogni cambiamento di AuthorizationStatus o di AdvertisingIdentifier, ereditiamo da ObservableObject e marchiamo le due proprietà interessate con @Published. Questo significa che Combine, dietro le quinte, genererà per noi tutto il codice necessario per tracciare i cambiamenti e notificare i subscriber.

Diagramma di ATTrackingHelper: AuthorizationStatus e advertisingIdentifier marcati @Published, un metodo requestAuth() tra i due, sopra ATTrackingManager e ASIdentifierManager

L'ultimo pezzo che manca per rendere utile questa classe è un metodo requestAuth(), che interagirà con il framework e popolerà di conseguenza i risultati.

Swift
class ATTrackingHelper: ObservableObject {
    @Published var status = ATTrackingManager.trackingAuthorizationStatus
    @Published var currentUUID = ASIdentifierManager.shared().advertisingIdentifier

    func requestAuth() {
        guard ATTrackingManager.trackingAuthorizationStatus != .authorized else {
            return
        }
        
        ATTrackingManager.requestTrackingAuthorization { status in
            DispatchQueue.main.async { [unowned self] in
                self.status = status
                if status == .authorized {
                    self.currentUUID = ASIdentifierManager.shared().advertisingIdentifier
                }
            }
        }
    }
}

È il momento di un Proof-Of-Concept. Con questo approccio basta dichiarare un ObservedObject nella nostra ContentView. Più o meno così:

Swift
struct ContentView: View {
    @ObservedObject var trackingHelper = ATTrackingHelper()

    var body: some View {
        VStack(alignment: .leading){
            Text("Your advertising ID:").font(.callout)
            Text("\(trackingHelper.currentUUID.uuidString)").font(.caption)
            HStack {
                Spacer()
                Button(action: {
                    trackingHelper.requestAuth()
                }, label: {
                    Text("Ask for tracking")
                })
                Spacer()
            }.padding(.vertical)
        }
        .padding()
    }
}

Ora possiamo chiedere il consenso all'utente, la prima volta che eseguiamo l'app, e popolare la uuidString richiesta. Che bel periodo per essere sviluppatori iOS! :-)

Happy coding! 🖖🏻

Continua a leggere

Tutti gli articoli
  1. TextField e UIViewRepresentable
  2. Un gioco TicTacToe in SwiftUI
  3. Programmare con SwiftUI
↑ ↓ naviga↵ apriesc chiudi