Issue with `<privacy-manifest>` and `cordora prepare ios`
shajz opened this issue · 3 comments
Bug Report
Problem
What is expected to happen?
The <privacy-manifest>
element must be included in the generated PrivacyInfo.xcprivacy
file, and the order of its properties must be the same.
What does actually happen?
When running cordova prepare ios
, the order of properties is messed up and the mess is exported to the generated PrivacyInfo.xcprivacy
file.
Information
config.xml
, before running cordova prepare ios
<privacy-manifest>
<key>NSPrivacyTracking</key>
<true />
<key>NSPrivacyCollectedDataTypes</key>
<array />
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>E174.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>3B52.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
</array>
<key>NSPrivacyTrackingDomains</key>
<array />
</privacy-manifest>
config.xml
, after running cordova prepare ios
<privacy-manifest>
<key>NSPrivacyTracking</key>
<key>NSPrivacyCollectedDataTypes</key>
<key>NSPrivacyAccessedAPITypes</key>
<key>NSPrivacyTrackingDomains</key>
<true/>
<array/>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<key>NSPrivacyAccessedAPITypeReasons</key>
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
<array>
<string>E174.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<key>NSPrivacyAccessedAPITypeReasons</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<array>
<string>CA92.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<key>NSPrivacyAccessedAPITypeReasons</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<array>
<string>3B52.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<key>NSPrivacyAccessedAPITypeReasons</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<array>
<string>35F9.1</string>
</array>
</dict>
</array>
<array/>
</privacy-manifest>
This is then outputted to the PrivacyInfo.xcprivacy
file :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict><key>NSPrivacyTracking</key><key>NSPrivacyCollectedDataTypes</key><key>NSPrivacyAccessedAPITypes</key><key>NSPrivacyTrackingDomains</key><true /><array /><array><dict><key>NSPrivacyAccessedAPIType</key><key>NSPrivacyAccessedAPITypeReasons</key><string>NSPrivacyAccessedAPICategoryDiskSpace</string><array><string>E174.1</string></array></dict><dict><key>NSPrivacyAccessedAPIType</key><key>NSPrivacyAccessedAPITypeReasons</key><string>NSPrivacyAccessedAPICategoryUserDefaults</string><array><string>CA92.1</string></array></dict><dict><key>NSPrivacyAccessedAPIType</key><key>NSPrivacyAccessedAPITypeReasons</key><string>NSPrivacyAccessedAPICategoryFileTimestamp</string><array><string>3B52.1</string></array></dict><dict><key>NSPrivacyAccessedAPIType</key><key>NSPrivacyAccessedAPITypeReasons</key><string>NSPrivacyAccessedAPICategorySystemBootTime</string><array><string>35F9.1</string></array></dict></array><array /></dict></plist>
The correct output (that works well in XCode) is this one :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict><key>NSPrivacyTracking</key><true /><key>NSPrivacyCollectedDataTypes</key><array /><key>NSPrivacyAccessedAPITypes</key><array><dict><key>NSPrivacyAccessedAPIType</key><string>NSPrivacyAccessedAPICategoryDiskSpace</string><key>NSPrivacyAccessedAPITypeReasons</key><array><string>E174.1</string></array></dict><dict><key>NSPrivacyAccessedAPIType</key><string>NSPrivacyAccessedAPICategoryUserDefaults</string><key>NSPrivacyAccessedAPITypeReasons</key><array><string>CA92.1</string></array></dict><dict><key>NSPrivacyAccessedAPIType</key><string>NSPrivacyAccessedAPICategoryFileTimestamp</string><key>NSPrivacyAccessedAPITypeReasons</key><array><string>3B52.1</string></array></dict><dict><key>NSPrivacyAccessedAPIType</key><string>NSPrivacyAccessedAPICategorySystemBootTime</string><key>NSPrivacyAccessedAPITypeReasons</key><array><string>35F9.1</string></array></dict></array><key>NSPrivacyTrackingDomains</key><array /></dict></plist>
Command or Code
cordova prepare ios
Environment, Platform, Device
Mac OS 14.4.1
Version information
"cordova": "^12.0.0"
"cordova-ios": "^7.1.0"
Checklist
- I searched for existing GitHub issues
- I updated all Cordova tooling to most recent version
- I included all the necessary information above
Oops, I forgot that my app edits the config.xml with a cordova hook and that's the source of my problem. Sorry for the spam
@Yura13 I was using xml2js https://www.npmjs.com/package/xml2js which doesn't preserve XML elements order. I switched to https://www.npmjs.com/package/fast-xml-parser.
Here's the code (quick and dirty js) :
// Parse config.xml, edit app & package name and save the changes
import { XMLBuilder, XMLParser } from "fast-xml-parser";
import { readFile, writeFile } from 'fs';
const configFile = "config.xml";
const commonOptions = {
preserveOrder: true,
ignoreAttributes: false,
allowBooleanAttributes: true,
commentPropName: "#comment",
unpairedTags: [
'content',
'access',
'allow-navigation',
'preference',
'allow-intent',
'param',
'hook',
'application',
'custom-preference',
'resource-file',
'icon',
'splash',
'true',
],
}
const parser = new XMLParser({
...commonOptions,
ignoreDeclaration: false,
});
const builder = new XMLBuilder({
...commonOptions,
suppressUnpairedNode: false,
format: true,
});
export function updateConfigFile(overrides) {
readFile(configFile, "utf-8", (err, data) => {
if (err) {
throw err;
}
const result = parser.parse(data);
/** Overrides */
if (overrides?.['android-packageName']) {
result[1][':@']['@_android-packageName'] = overrides['android-packageName'];
}
if (overrides?.['ios-CFBundleIdentifier']) {
result[1][':@']['@_ios-CFBundleIdentifier'] = overrides['ios-CFBundleIdentifier'];
}
if (overrides?.name) {
result[1].widget = result[1].widget.map((obj) => ((obj?.name != null) ? {...obj, name: [ { '#text': overrides.name } ] } : obj));
}
const xml = builder.build(result);
// write updated XML string to a file
writeFile(configFile, xml, { encoding: "utf-8" }, (err) => {
if (err) {
throw err;
}
console.log(`Updated XML is written to a new file.`);
});
});
}
You can adapt the overrides part to your usecase, the syntax is weirder than xml2js
but the order is preserved with the provided options :)
Saved this as a .mjs file so I could use import
syntax safely but you can use require
with .js files.
call it like this in your cordova hooks
updateConfigFile({
'android-packageName': 'com.your.package',
'ios-CFBundleIdentifier': 'com.your.package',
name: 'Your app name',
});
Sorry I let this up without providing a solution https://xkcd.com/979/ 😅