Skip to content

Commit

Permalink
feat(apple): add app privacy manifest support (#2002)
Browse files Browse the repository at this point in the history
  • Loading branch information
tido64 committed Apr 30, 2024
1 parent f22e1e7 commit 9f26369
Show file tree
Hide file tree
Showing 15 changed files with 403 additions and 276 deletions.
454 changes: 227 additions & 227 deletions example/ios/Podfile.lock

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions example/macos/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1018,7 +1018,7 @@ PODS:
- React-jsi (= 0.73.24)
- React-logger (= 0.73.24)
- React-perflogger (= 0.73.24)
- ReactNativeHost (0.4.6):
- ReactNativeHost (0.4.7):
- glog
- RCT-Folly (= 2022.05.16.00)
- React-Core
Expand All @@ -1028,7 +1028,7 @@ PODS:
- React-Core
- React-jsi
- ReactTestApp-Resources (1.0.0-dev)
- RNWWebStorage (0.2.4):
- RNWWebStorage (0.2.5):
- glog
- RCT-Folly (= 2022.05.16.00)
- React-Core
Expand Down Expand Up @@ -1252,10 +1252,10 @@ SPEC CHECKSUMS:
React-runtimescheduler: 265991752129c54256f223defa0272509fe81ad7
React-utils: fad5a988b6e23d8b98a4450408bfcc56d63ac85c
ReactCommon: 67c1d3a5dafe396de7d497fe224c12217f8b6f49
ReactNativeHost: 339df34d3c2050f37e8909ade6693465e8a947f7
ReactNativeHost: 69c76dddefb1a795780b5292dad8817e2097f9cd
ReactTestApp-DevSupport: c4abadbb90a8a9903400407e9857c2a2ef0343fb
ReactTestApp-Resources: 9d83e280b173ba2ee053b8135730dff60f9ab674
RNWWebStorage: 0663af06cd0cf0e465b87e6144f85e0720ded82e
RNWWebStorage: ce8c1b8f1ced2ffd85024a0fac266a62087488a8
SocketRocket: f6c6249082c011e6de2de60ed641ef8bbe0cfac9
Yoga: 24ddb4963e2a3330df762423f0a9de2096325401

Expand Down
8 changes: 4 additions & 4 deletions example/visionos/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1067,7 +1067,7 @@ PODS:
- React-jsi (= 0.73.10)
- React-logger (= 0.73.10)
- React-perflogger (= 0.73.10)
- ReactNativeHost (0.4.6):
- ReactNativeHost (0.4.7):
- glog
- RCT-Folly (= 2022.05.16.00)
- React-Core
Expand All @@ -1077,7 +1077,7 @@ PODS:
- React-Core
- React-jsi
- ReactTestApp-Resources (1.0.0-dev)
- RNWWebStorage (0.2.4):
- RNWWebStorage (0.2.5):
- glog
- RCT-Folly (= 2022.05.16.00)
- React-Core
Expand Down Expand Up @@ -1317,10 +1317,10 @@ SPEC CHECKSUMS:
React-runtimescheduler: 3f2a280b446060ac359d32175cf26402b7fd8de0
React-utils: 940f007d5dad05ec028b873ebfed3bf6ec46c417
ReactCommon: 0c2cdd6a193b8ce7c05105b66b881c41bf014308
ReactNativeHost: 339df34d3c2050f37e8909ade6693465e8a947f7
ReactNativeHost: 69c76dddefb1a795780b5292dad8817e2097f9cd
ReactTestApp-DevSupport: c4abadbb90a8a9903400407e9857c2a2ef0343fb
ReactTestApp-Resources: 2ad57492ef72ab9b2c6f6e89ea198cc1999ca20b
RNWWebStorage: 0663af06cd0cf0e465b87e6144f85e0720ded82e
RNWWebStorage: ce8c1b8f1ced2ffd85024a0fac266a62087488a8
SocketRocket: 0ba3e799f983d2dfa878777017659ef6c866e5c6
Yoga: a2d1866030ed66b042bdd302fe0541f5b107f3b6

Expand Down
4 changes: 4 additions & 0 deletions ios/ReactTestApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
191A67842BDFCD5C0094F246 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 191A67832BDFCD5C0094F246 /* PrivacyInfo.xcprivacy */; };
192DD201240FCAF5004E9CEB /* Manifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 192DD200240FCAF5004E9CEB /* Manifest.swift */; };
196C22622490CB7600449D3C /* React+Compatibility.m in Sources */ = {isa = PBXBuildFile; fileRef = 196C22602490CB7600449D3C /* React+Compatibility.m */; };
196C7215232F1788006556ED /* ReactInstance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 196C7214232F1788006556ED /* ReactInstance.swift */; };
Expand Down Expand Up @@ -40,6 +41,7 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
191A67832BDFCD5C0094F246 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = SOURCE_ROOT; };
192DD200240FCAF5004E9CEB /* Manifest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Manifest.swift; sourceTree = "<group>"; };
192F052624AD3CC500A48456 /* ReactTestApp.release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ReactTestApp.release.xcconfig; sourceTree = "<group>"; };
192F052724AD3CC500A48456 /* ReactTestApp.common.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ReactTestApp.common.xcconfig; sourceTree = "<group>"; };
Expand Down Expand Up @@ -131,6 +133,7 @@
1988284424105BEC005057FF /* UIViewController+ReactTestApp.m */,
19ECD0DB232ED427003D8557 /* Assets.xcassets */,
19ECD0E3232ED427003D8557 /* Info.plist */,
191A67832BDFCD5C0094F246 /* PrivacyInfo.xcprivacy */,
196C7216232F6CD9006556ED /* ReactTestApp.entitlements */,
192F052724AD3CC500A48456 /* ReactTestApp.common.xcconfig */,
192F052824AD3CC500A48456 /* ReactTestApp.debug.xcconfig */,
Expand Down Expand Up @@ -265,6 +268,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
191A67842BDFCD5C0094F246 /* PrivacyInfo.xcprivacy in Resources */,
19ECD0DC232ED427003D8557 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
20 changes: 20 additions & 0 deletions ios/pod_helpers.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
require('json')

def app_manifest(project_root)
@app_manifest ||= {}
return @app_manifest[project_root] if @app_manifest.key?(project_root)

manifest_path = find_file('app.json', project_root)
return if manifest_path.nil?

@app_manifest[project_root] = JSON.parse(File.read(manifest_path))
end

def assert(condition, message)
raise message unless condition
end
Expand Down Expand Up @@ -38,6 +50,14 @@ def new_architecture_enabled?(options, react_native_version)
options[:fabric_enabled] || options[:turbomodule_enabled])
end

def platform_config(key, project_root, target_platform)
manifest = app_manifest(project_root)
return if manifest.nil?

config = manifest[target_platform.to_s]
config[key] if !config.nil? && !config.empty?
end

def resolve_module(request, start_dir = Pod::Config.instance.installation_root)
@module_cache ||= {}
return @module_cache[request] if @module_cache.key?(request)
Expand Down
59 changes: 59 additions & 0 deletions ios/privacy_manifest.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
require('cfpropertylist')

require_relative('pod_helpers')

# https://developer.apple.com/documentation/bundleresources/privacy_manifest_files
PRIVACY_ACCESSED_API_TYPES = 'NSPrivacyAccessedAPITypes'.freeze
PRIVACY_COLLECTED_DATA_TYPES = 'NSPrivacyCollectedDataTypes'.freeze
PRIVACY_TRACKING = 'NSPrivacyTracking'.freeze
PRIVACY_TRACKING_DOMAINS = 'NSPrivacyTrackingDomains'.freeze

# https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api
PRIVACY_ACCESSED_API_TYPE = 'NSPrivacyAccessedAPIType'.freeze
PRIVACY_ACCESSED_API_TYPE_REASONS = 'NSPrivacyAccessedAPITypeReasons'.freeze
PRIVACY_ACCESSED_API_CATEGORY_FILE_TIMESTAMP = 'NSPrivacyAccessedAPICategoryFileTimestamp'.freeze
PRIVACY_ACCESSED_API_CATEGORY_SYSTEM_BOOT_TIME = 'NSPrivacyAccessedAPICategorySystemBootTime'.freeze
PRIVACY_ACCESSED_API_CATEGORY_USER_DEFAULTS = 'NSPrivacyAccessedAPICategoryUserDefaults'.freeze

def generate_privacy_manifest!(project_root, target_platform, destination)
privacy = {
PRIVACY_TRACKING => false,
PRIVACY_TRACKING_DOMAINS => [],
PRIVACY_COLLECTED_DATA_TYPES => [],
PRIVACY_ACCESSED_API_TYPES => [
{
PRIVACY_ACCESSED_API_TYPE => PRIVACY_ACCESSED_API_CATEGORY_FILE_TIMESTAMP,
PRIVACY_ACCESSED_API_TYPE_REASONS => ['C617.1'],
},
{
PRIVACY_ACCESSED_API_TYPE => PRIVACY_ACCESSED_API_CATEGORY_SYSTEM_BOOT_TIME,
PRIVACY_ACCESSED_API_TYPE_REASONS => ['35F9.1'],
},
{
PRIVACY_ACCESSED_API_TYPE => PRIVACY_ACCESSED_API_CATEGORY_USER_DEFAULTS,
PRIVACY_ACCESSED_API_TYPE_REASONS => ['CA92.1'],
},
],
}

user_privacy_manifest = platform_config('privacyManifest', project_root, target_platform)
unless user_privacy_manifest.nil?
tracking = user_privacy_manifest[PRIVACY_TRACKING]
privacy[PRIVACY_TRACKING] = tracking unless tracking.nil?

[
PRIVACY_TRACKING_DOMAINS,
PRIVACY_COLLECTED_DATA_TYPES,
PRIVACY_ACCESSED_API_TYPES,
].each do |field|
value = user_privacy_manifest[field]
privacy[field] += value if value.is_a? Enumerable
end
end

plist = CFPropertyList::List.new
plist.value = CFPropertyList.guess(privacy)
plist.save(File.join(destination, 'PrivacyInfo.xcprivacy'),
CFPropertyList::List::FORMAT_XML,
{ :formatted => true })
end
20 changes: 2 additions & 18 deletions ios/test_app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,9 @@
require('pathname')

require_relative('pod_helpers')
require_relative('privacy_manifest')
require_relative('xcode')

def app_manifest(project_root)
@app_manifest ||= {}
return @app_manifest[project_root] if @app_manifest.key?(project_root)

manifest_path = find_file('app.json', project_root)
return if manifest_path.nil?

@app_manifest[project_root] = JSON.parse(File.read(manifest_path))
end

def app_config(project_root)
manifest = app_manifest(project_root)
return [nil, nil, nil] if manifest.nil?
Expand All @@ -41,14 +32,6 @@ def autolink_script_path(project_root, target_platform)
File.join(package_path, 'native_modules')
end

def platform_config(key, project_root, target_platform)
manifest = app_manifest(project_root)
return if manifest.nil?

config = manifest[target_platform.to_s]
config[key] if !config.nil? && !config.empty?
end

def nearest_node_modules(project_root)
path = find_file('node_modules', project_root)
assert(!path.nil?, "Could not find 'node_modules'")
Expand Down Expand Up @@ -297,6 +280,7 @@ def make_project!(xcodeproj, project_root, target_platform, options)

generate_assets_catalog!(project_root, target_platform, destination)
generate_info_plist!(project_root, target_platform, destination)
generate_privacy_manifest!(project_root, target_platform, destination)

# Copy localization files and replace instances of `ReactTestApp` with app display name
product_name = display_name || name
Expand Down
6 changes: 5 additions & 1 deletion macos/ReactTestApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
191A67882BDFD9E90094F246 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 191A67872BDFD9E90094F246 /* PrivacyInfo.xcprivacy */; };
193EF063247A736200BE8C79 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193EF062247A736200BE8C79 /* AppDelegate.swift */; };
193EF065247A736200BE8C79 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193EF064247A736200BE8C79 /* ViewController.swift */; };
193EF067247A736300BE8C79 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 193EF066247A736300BE8C79 /* Assets.xcassets */; };
Expand Down Expand Up @@ -39,6 +40,7 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
191A67872BDFD9E90094F246 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = SOURCE_ROOT; };
193EF05F247A736200BE8C79 /* ReactTestApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ReactTestApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
193EF062247A736200BE8C79 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
193EF064247A736200BE8C79 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
Expand All @@ -61,7 +63,7 @@
19B368BC24B12C24002CCEFF /* ReactTestApp.common.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ReactTestApp.common.xcconfig; sourceTree = "<group>"; };
19B368BD24B12C24002CCEFF /* ReactTestApp.debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ReactTestApp.debug.xcconfig; sourceTree = "<group>"; };
19B368BE24B12C24002CCEFF /* ReactTestApp.release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ReactTestApp.release.xcconfig; sourceTree = "<group>"; };
19C4C89227710D8500157870 /* Manifest+Embedded.g.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Manifest+Embedded.g.swift"; path = "Manifest+Embedded.g.swift"; sourceTree = SOURCE_ROOT; };
19C4C89227710D8500157870 /* Manifest+Embedded.g.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Manifest+Embedded.g.swift"; sourceTree = SOURCE_ROOT; };
19E0B90C27E9F8FD006FD558 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = ../Localizations/en.lproj/Main.strings; sourceTree = "<group>"; };
19E791BF24B08E1400FA6468 /* ReactTestAppTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactTestAppTests.swift; sourceTree = "<group>"; };
19E791C224B08E4D00FA6468 /* ReactTestAppUITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactTestAppUITests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -129,6 +131,7 @@
193EF066247A736300BE8C79 /* Assets.xcassets */,
193EF068247A736300BE8C79 /* Main.storyboard */,
193EF06B247A736300BE8C79 /* Info.plist */,
191A67872BDFD9E90094F246 /* PrivacyInfo.xcprivacy */,
193EF06C247A736300BE8C79 /* ReactTestApp.entitlements */,
19B368BC24B12C24002CCEFF /* ReactTestApp.common.xcconfig */,
19B368BD24B12C24002CCEFF /* ReactTestApp.debug.xcconfig */,
Expand Down Expand Up @@ -264,6 +267,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
191A67882BDFD9E90094F246 /* PrivacyInfo.xcprivacy in Resources */,
193EF067247A736300BE8C79 /* Assets.xcassets in Resources */,
193EF06A247A736300BE8C79 /* Main.storyboard in Resources */,
);
Expand Down

0 comments on commit 9f26369

Please sign in to comment.