Inspecting app activity data
Verify that your app accesses only the user data and network resources that you expect it to access.
Overview
In iOS 15.2, iPadOS 15.2, and watchOS 8.3 or later, users can view a privacy report of when your app:
Accesses certain kinds of user data, like photos and contacts.
Accesses sensitive device resources, like the camera and microphone.
Contacts network domains, including websites that a user visits from within your app (iOS- and iPadOS-only).
Examine the data that your app contributes to this summary to find out what the report shows users, and to make sure that your app behaves as you expect.
Start recording app activity
Users can enable app activity recording on their device from the Settings app by choosing Privacy > App Privacy Report, and then tapping Turn On App Privacy Report.
The system keeps a record — stored only on the user’s device — of the last seven days of app activity. After turning on this feature on your own device, use your app to fully test its data, resource, and network accesses. Be sure to use the app for long enough to allow for any delayed accesses. How long depends on the behavior of your app. For example, if an analytics framework in your app caches data to avoid frequent server accesses, be sure to run your app until it experiences an analytics server access.
Get the app activity data
After running your app for a while, you can get a report on your app’s activity by tapping the Share button on the App Privacy Report screen:
[Image]
Save the report to a location where you can examine it. For example, you can use AirDrop to send it to a nearby Mac.
The report uses a newline-delimited JSON format (NDJSON), which you can open with any text editor, and contains a collection of JSON dictionaries separated by newlines. Dictionaries that have the type key with a value set to access provide information about resource accesses, while those that have the type key with a value set to networkActivity provide information about network activity:
{..., "type":"access", ...}
{..., "type":"access", ...}
...
{..., "type":"networkActivity", ...}
{..., "type":"networkActivity", ...}
...Examine resource access data
Each access dictionary in the file represents the start or end of an access made by a particular app. The following shows an example of this dictionary with whitespace, newlines, and comments added for readability:
{
"accessor" : {
"identifier" : "com.example.calendar", // The app accessing the resource.
"identifierType" : "bundleID"
},
"broadcaster": { // Only present for screen recording.
"identifier" : "com.apple.springboard", // The app broadcasting the screen.
"identifierType" : "bundleID"
},
"category" : "screenRecording", // The accessed resource.
"identifier" : "8A4054BB-1810-4F8B-8163-483409E2D35C", // A unique identifier.
"kind" : "intervalBegin", // Whether this the beginning or end of an interval.
"timeStamp" : "2021-08-06T15:46:13.532-07:00", // The time of the access.
"type" : "access" // This is resource access data.
}To find accesses made by your app, look for all of the access dictionaries that have an accessor key with a dictionary value that contains the bundle identifier of your app. Use the category field to determine which resource your app accessed, and the timeStamp field to associate the access with the activity that generated the access. You might encounter any of the following category values:
Category | Resource |
|---|---|
| The device’s camera |
| The user’s contacts |
| Location data |
| The user’s media library |
| The device’s microphone |
| The user’s photo library |
| Screen sharing |
Every resource access occurs over a time interval and generates a pair of access dictionaries to indicate:
The start of the interval with the
kindfield set tointervalBegin, as shown in the example above.The end of the interval with the
kindfield set tointervalEnd.
The two dictionaries that bound an access interval have the same value for the identifier key.
For the screenRecording category, the dictionary also includes a broadcaster key with a value that indicates the app broadcasting the screen to the accessor, which is typically Springboard.
Examine network activity
A file exported from iOS or iPadOS contains another set of dictionaries that have the type key with a value set to networkActivity to report network accesses. Each dictionary describes a connection made by a specified app to a particular domain. The following shows an example of this dictionary, again with whitespace and newlines added for clarity:
{
"domain" : "api.example.com",
"firstTimeStamp" : "2021-06-06T16:15:48Z",
"context" : "",
"timeStamp" : "2021-06-06T16:15:59Z",
"domainType" : 2,
"initiatedType" : "AppInitiated",
"hits" : 10,
"type" : "networkActivity",
"domainOwner" : "",
"bundleID" : "com.example.App1"
}The dictionary includes the following keys to describe the network activity:
domainThe domain of the network connection.
firstTimeStampThe time of the first connection to this domain.
contextThe website that made the connection, if applicable.
timeStampThe time of the most recent connection.
domainTypeWhen the associated value is
1, the domain has been identified as potentially collecting information across apps and sites, and potentially profiling users. A value of2means that the domain hasn’t been identified as such.initiatedTypeWhether the app (
AppInitiated) or the user (NonAppInitiated) initiated the connection.hitsThe number of times the app contacted the domain in the last seven days.
typeAn associated value of
networkActivitymeans that this dictionary describes network activity data.domainOwnerThe owner of the domain, if applicable.
bundleIDThe bundle identifier of the initiating app.
When deciding how to set the value for the initiatedType key, the system attributes connections made from a web browser in your app, like when you instantiate an SFSafariViewController, to the user. Otherwise, the system considers any connection that your app makes with the URL Loading System, or lower-level interfaces like the Network framework, as app-initiated. This includes user data that you load from a server in direct response to a user action.
You can change the associated value of this key when you make a general network request by setting a property. For example, when creating a URLRequest, set the attribution property; when using an NWConnection instance, call the nw_parameters_set_attribution(_:_:) function. However, only change the attribution for connections that the user directly and completely controls, such as when the user enters a URL or taps or clicks a URL that they can read. For more information about network attribution, see Indicating the source of network activity.
Consider whether your app needs changes
If you see that your app makes network connections that you don’t recognize, or accesses resources that it shouldn’t, review your app. Closely examine any third-party frameworks or modules that you’ve integrated because they might make unexpected connections.