diff --git a/src/main/java/com/gluonhq/substrate/util/ios/CodeSigning.java b/src/main/java/com/gluonhq/substrate/util/ios/CodeSigning.java index e1d1fe7c..d60f5bea 100644 --- a/src/main/java/com/gluonhq/substrate/util/ios/CodeSigning.java +++ b/src/main/java/com/gluonhq/substrate/util/ios/CodeSigning.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Gluon + * Copyright (c) 2019, 2024, Gluon * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -66,6 +66,7 @@ public class CodeSigning { private static final String CODESIGN_ALLOCATE_ENV = "CODESIGN_ALLOCATE"; private static final String EMBEDDED_PROVISIONING_PROFILE = "embedded.mobileprovision"; + private static final String PRIVACY_MANIFEST_FILE = "PrivacyInfo.xcprivacy"; private static final String ERRLINK = "Please check https://docs.gluonhq.com/ for more information."; private static final String KEYCHAIN_ERROR_MESSAGE = "errSecInternalComponent"; @@ -110,8 +111,16 @@ public boolean signApp() throws IOException, InterruptedException { Path provisioningProfilePath = mobileProvision.getProvisioningPath(); Path embeddedPath = appPath.resolve(EMBEDDED_PROVISIONING_PROFILE); Files.copy(provisioningProfilePath, embeddedPath, REPLACE_EXISTING); + + // entitlements Path entitlementsPath = getEntitlementsPath(bundleId, getProvisioningProfile().isTaskAllow()); Logger.logDebug("Signing with entitlements path: " + entitlementsPath); + + // privacy + Path privacyManifestPath = getPrivacyManifestPath(); + Logger.logDebug("Privacy manifest path: " + privacyManifestPath); + Files.copy(privacyManifestPath, appPath.resolve(PRIVACY_MANIFEST_FILE), REPLACE_EXISTING); + return sign(entitlementsPath, appPath); } @@ -343,6 +352,18 @@ private Path getEntitlementsPath(String bundleId, boolean taskAllow) throws IOEx return tmpEntitlements; } + private Path getPrivacyManifestPath() throws IOException { + Path privacyManifest = rootPath.resolve(PRIVACY_MANIFEST_FILE); + + if (!Files.exists(privacyManifest)) { + Path tmpPrivacyManifest = tmpPath.resolve(PRIVACY_MANIFEST_FILE); + privacyManifest = FileOps.copyResource("/native/ios/PrivacyInfo.xcprivacy", tmpPrivacyManifest); + Logger.logInfo("Default iOS privacy manifest generated in " + tmpPrivacyManifest + ".\n" + + "Consider copying it to " + rootPath + " before performing any modification"); + } + return privacyManifest; + } + private List getIdentity() { if (providedIdentityName != null) { return findIdentityByName(providedIdentityName); diff --git a/src/main/resources/native/ios/PrivacyInfo.xcprivacy b/src/main/resources/native/ios/PrivacyInfo.xcprivacy new file mode 100644 index 00000000..526129c8 --- /dev/null +++ b/src/main/resources/native/ios/PrivacyInfo.xcprivacy @@ -0,0 +1,34 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + DDA9.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryDiskSpace + NSPrivacyAccessedAPITypeReasons + + E174.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + 8FFB.1 + + + + +