One of the most publicized feature coming with iOS14.5 is the new App Tracking Trasparency feature. There are plenty of articles around that aim to pinpoint the relevant positions in this decision from Apple and counterparts like Facebook. I don’t want to digging out the political aspect of this feature. Instead, I would like to quickly overview what you need to work with this brand new capability and what this means in terms of technology underground.
App Tracking Trasparency consists of one piece of software in the form of a class named: ATTrackingManager.
class ATTrackingManager : NSObject
Apple states that this class provides all what you need to ask for permission and get the current status of your request. This class collaborates together with the Ad framework in order to identify anonymously users and let track their habits / interests.
Ad frameworks has a shared instance, available within the whole system, in the form of a class named ASIdentifierManager
. We can ask for the shared instance as usual with the classical approach:
ASIdentifierManager.shared()
Furthemore, we can ask for the current anonymous identifier as easy as:
ASIdentifierManager.shared().advertisingIdentifier
Before iOS14.5 you (the developer) was always granted to get the above identifier in the form of an UUID object. This identifier is anonymous and consistent across all the operating system allowing to track users across different apps and domains. This, obviously, could lead to a lack of privacy if one or more players in this game are malicious (and, believe me, there’re tons of malicious players all the way around).
Since iOS14.5 you need to ask app based user consensus before you can access the above identifier. This means that you (the user) can decide who can access your identifier at every moment either when you install the app or - if you change your mind - from the Settings. That’s awesome in terms of user rights! And it’s not opinable at all.
Our goals for today is to wrap the above classes into a more convenient object that we could use both with Swift and SwiftUI. To achieve that we will use some basic Combine capabilities in order to expose correctly all pieces that we need.
For such of conciseness we will use a pure SwiftUI approach, but all the relevant code will be also ready for Swift standard projects as well. Let’s start and create a SwiftUI project.
The first thing to do is to add the relevant description within our Info.plist in order to populate correctly the message that will appear to our users. Pretty much as with CoreLocation or Photos, we need to describe with an effective sentence what and why we need user consensus to track. To do that, we need to add a new key NSUserTrackingUsageDescription
and a paired phrase String as well.
Once we’re ready, we can start coding our helper class that will expose the relevant accessors to our App. We named this helper ATTrackingHelper
. Since we want to be notified each time there’s a change either into AuthorizationStatus
or AdvertisingIdentifier
, we inherits from ObservableObject
and mark the two relevant properties as @Published
. This means that Combine, under the hood, will create for us all the relevant code in order to track and notify subscribers whenever there is a change.
The last missing piece in order to make this class useful for our purpose is to expose a requestAuth()
method. The latter will interact with the framework and will populate the results accordingly.
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
}
}
}
}
}
Time for a Proof-Of-Concept. Given the above approach it will be as easy as declaring an ObservedObject within our ContentView. Pretty much like that:
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()
}
}
Now we can ask for user consensus, first time we will run our app and populate the required uuidString. What a great time to be an iOS developer! :-)
—
Happy coding! 🖖🏻