Privacy manifests: how to identify which pod a category use is originating from?

I received an email from Apple saying the app is using:

NSPrivacyAccessedAPICategoryDiskSpace
NSPrivacyAccessedAPICategoryFileTimestamp
NSPrivacyAccessedAPICategorySystemBootTime

I'm not directly calling (afaik) any API that might be involved in getting the disk space, file timestamp, nor system boot time, so presumably these are indirectly originating in a pod whose api I'm using.

However I have about 100 pods in the app, how can I know which one these are originating from? (100 seems a lot, but its a React Native app and that alone pulls in dozens and dozens of pods implicitly in addition those specified explicitly in a pod file)

I can try and update all the pods to the latest version, but if the offending pod(s) hasn't added a manifest file, then I have no way of knowing which one it is - therefore I can neither contact them to ask when they will release a new version, nor can I attempt to try and remove the pod, because I just don't know which one might be causing the manifest warning.

So what are we supposed to do in this situation?

If you wish to do this properly, see this tread on the Apple forums which links to this guide about "Using a Link Map to Track Down a Symbol’s Origin" but also contains a few additions to the guide.

However, you can also try your luck and just do a text search for each API through your pods and see what comes up, but you've no guarantee you haven't missed something.

I didn't understand what @Sisky_RO meant by using a link map to track down a use of a particular API. So I went about it another way...

  1. Quit Xcode

  2. Clear your DerivedData folder

    rm -rf ~/Library/Developer/Xcode/DerivedData
    
  3. Launch your project in Xcode

  4. Wait for dependencies to resolve and build the app (I don't use React Native, but presumably you have another way to do this than in Xcode)

  5. Find the offending API(s)

  6. Search for calls. For example:

    grep -RHn "systemUptime" ~/Library/Developer/Xcode/DerivedData 
    
  7. A lot of results were returned, so I didn't include everything. The interesting results I found were within a SourcePackages directory. Here's an example of my results (with pathnames shortened to make it easier to view):

    In /Users/username/Library/Developer/Xcode/DerivedData/MyTargetName-darnasgcpehipbceilnyjlqsmpxt/SourcePackages:

    Binary file /artifacts/newrelic-ios-agent-spm/NewRelic/NewRelic.xcframework/tvos-arm64/dSYMs/NewRelic.framework.dSYM/Contents/Resources/DWARF/NewRelic matches
    Binary file /artifacts/newrelic-ios-agent-spm/NewRelic/NewRelic.xcframework/ios-arm64_x86_64-maccatalyst/dSYMs/NewRelic.framework.dSYM/Contents/Resources/DWARF/NewRelic matches
    Binary file /artifacts/newrelic-ios-agent-spm/NewRelic/NewRelic.xcframework/ios-arm64/dSYMs/NewRelic.framework.dSYM/Contents/Resources/DWARF/NewRelic matches
    /checkouts/Alamofire/Source/Core/DownloadRequest.swift:368:            let start = ProcessInfo.processInfo.systemUptime
    /checkouts/Alamofire/Source/Core/DownloadRequest.swift:377:            let end = ProcessInfo.processInfo.systemUptime
    /checkouts/Alamofire/Source/Core/WebSocketRequest.swift:283:        let startTimestamp = ProcessInfo.processInfo.systemUptime
    /checkouts/Alamofire/Source/Core/WebSocketRequest.swift:293:                let endTimestamp = ProcessInfo.processInfo.systemUptime
    /checkouts/Alamofire/Source/Core/DataRequest.swift:244:            let start = ProcessInfo.processInfo.systemUptime
    /checkouts/Alamofire/Source/Core/DataRequest.swift:254:            let end = ProcessInfo.processInfo.systemUptime
    /checkouts/Alamofire/Source/Features/AuthenticationInterceptor.swift:336:        mutableState.refreshTimestamps.append(ProcessInfo.processInfo.systemUptime)
    /checkouts/Alamofire/Source/Features/AuthenticationInterceptor.swift:357:        let refreshWindowMin = ProcessInfo.processInfo.systemUptime - refreshWindow.interval
    /checkouts/AlamofireNetworkActivityLogger/Carthage/Checkouts/Alamofire/Source/AuthenticationInterceptor.swift:338:        mutableState.refreshTimestamps.append(ProcessInfo.processInfo.systemUptime)
    /checkouts/AlamofireNetworkActivityLogger/Carthage/Checkouts/Alamofire/Source/AuthenticationInterceptor.swift:359:        let refreshWindowMin = ProcessInfo.processInfo.systemUptime - refreshWindow.interval
    /checkouts/AlamofireNetworkActivityLogger/Carthage/Checkouts/Alamofire/Source/ResponseSerialization.swift:217:            let start = ProcessInfo.processInfo.systemUptime
    /checkouts/AlamofireNetworkActivityLogger/Carthage/Checkouts/Alamofire/Source/ResponseSerialization.swift:227:            let end = ProcessInfo.processInfo.systemUptime
    /checkouts/AlamofireNetworkActivityLogger/Carthage/Checkouts/Alamofire/Source/ResponseSerialization.swift:332:            let start = ProcessInfo.processInfo.systemUptime
    /checkouts/AlamofireNetworkActivityLogger/Carthage/Checkouts/Alamofire/Source/ResponseSerialization.swift:341:            let end = ProcessInfo.processInfo.systemUptime
    

For me, it's pretty clear that AlamoFire uses one of these APIs. I will also be looking into NewRelic as well.

I'm not sure that this is an exhastive approach, but it's hopefully another approach to tracking down libraries that you find helpful.

I fixed it by changed pods to static libraries - use_frameworks! :linkage => :static in your pdofile if you don't use these things and one of your libs is...just ensure they aren't being bundled into the project...

If you need to keep it without static libs then you'll need to throw up a privacy thingy

Privacy manifests: how to identify which pod a category use is originating from?
 
 
Q