forked from tuist/tuist
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for privacy manifest file generation (tuist#6117)
* Implement xcprivacy as part of ResourceFileElements * Add acceptance tests * Fix tests * Fix acceptance test * Lint * Implement PR Feedback * Fix acceptance test * Add fixture for privacyManifest * Add acceptance test * Run lint:fix * Fix naming * Add documentation and more details in example * Run mise run lint:fix
- Loading branch information
Showing
41 changed files
with
655 additions
and
116 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import Foundation | ||
|
||
/// Describe the data your app or third-party SDK collects and the reasons required APIs it uses. | ||
public struct PrivacyManifest: Codable, Equatable { | ||
/// A Boolean that indicates whether your app or third-party SDK uses data for tracking as defined under the App | ||
/// Tracking Transparency framework. For more information, see [User Privacy and Data | ||
/// Use](https://developer.apple.com/app-store/user-privacy-and-data-use/). | ||
public var tracking: Bool | ||
|
||
/// An array of strings that lists the internet domains your app or third-party SDK connects to that | ||
/// engage in tracking. If the user has not granted tracking permission through the App Tracking Transparency framework, | ||
/// network requests to these domains fail and your app receives an error. If you set `tracking` to true then you need to | ||
/// provide at least one internet domain in NSPrivacyTrackingDomains; otherwise, you can provide zero or more domains. | ||
public var trackingDomains: [String] | ||
|
||
/// An array of dictionaries that describes the data types your app or third-party SDK collects. For | ||
/// information on the keys and values to use in the dictionaries, see [Describing data use in privacy manifests](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests). | ||
public var collectedDataTypes: [[String: Plist.Value]] | ||
|
||
/// An array of dictionaries that describe the API types your app or third-party SDK accesses that have | ||
/// been designated as APIs that require reasons to access. For information on the keys and values to use in the dictionaries, | ||
/// see [Describing use of required reason API](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api). | ||
public var accessedApiTypes: [[String: Plist.Value]] | ||
|
||
/// Returns a PrivacyManifest. | ||
/// - Parameter tracking: A Boolean that indicates whether your app or third-party SDK uses data for tracking. | ||
/// - Parameter trackingDomains: An array of strings that lists the internet domains your app or third-party SDK connects to | ||
/// that engage in tracking. | ||
/// - Parameter collectedDataTypes: An array of dictionaries that describes the data types your app or third-party SDK | ||
/// collects. | ||
/// - Parameter accessedApiTypes: An array of dictionaries that describe the API types your app or third-party SDK accesses | ||
/// that have | ||
/// been designated as APIs that require reasons to access. | ||
/// - Returns: PrivacyManifest. | ||
public static func privacyManifest( | ||
tracking: Bool, | ||
trackingDomains: [String], | ||
collectedDataTypes: [[String: Plist.Value]], | ||
accessedApiTypes: [[String: Plist.Value]] | ||
) -> Self { | ||
PrivacyManifest( | ||
tracking: tracking, | ||
trackingDomains: trackingDomains, | ||
collectedDataTypes: collectedDataTypes, | ||
accessedApiTypes: accessedApiTypes | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
Sources/TuistGenerator/Mappers/GeneratePrivacyManifestProjectMapper.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import Foundation | ||
import TSCBasic | ||
import TuistCore | ||
import TuistGraph | ||
import TuistSupport | ||
import XcodeProj | ||
|
||
/// A project mapper that generates derived privacyManifest files for targets that define it as a dictonary. | ||
public final class GeneratePrivacyManifestProjectMapper: ProjectMapping { | ||
public init() {} | ||
|
||
// MARK: - ProjectMapping | ||
|
||
public func map(project: Project) throws -> (Project, [SideEffectDescriptor]) { | ||
logger.debug("Transforming project \(project.name): Synthesizing privacy manifest files'") | ||
|
||
let results = try project.targets | ||
.reduce(into: (targets: [Target](), sideEffects: [SideEffectDescriptor]())) { results, target in | ||
let (updatedTarget, sideEffects) = try map(target: target, project: project) | ||
results.targets.append(updatedTarget) | ||
results.sideEffects.append(contentsOf: sideEffects) | ||
} | ||
|
||
return (project.with(targets: results.targets), results.sideEffects) | ||
} | ||
|
||
// MARK: - Private | ||
|
||
private func map(target: Target, project: Project) throws -> (Target, [SideEffectDescriptor]) { | ||
guard let privacyManifest = target.resources.privacyManifest else { | ||
return (target, []) | ||
} | ||
|
||
let dictionary: [String: Any] = [ | ||
"NSPrivacyTracking": privacyManifest.tracking, | ||
"NSPrivacyTrackingDomains": privacyManifest.trackingDomains, | ||
"NSPrivacyCollectedDataTypes": privacyManifest.collectedDataTypes.map { $0.mapValues { $0.value } }, | ||
"NSPrivacyAccessedAPITypes": privacyManifest.accessedApiTypes.map { $0.mapValues { $0.value } }, | ||
] | ||
|
||
let data = try PropertyListSerialization.data( | ||
fromPropertyList: dictionary, | ||
format: .xml, | ||
options: 0 | ||
) | ||
|
||
let privacyManifestPath = project.path | ||
.appending(component: Constants.DerivedDirectory.name) | ||
.appending(component: Constants.DerivedDirectory.privacyManifest) | ||
.appending(component: target.name) | ||
.appending(component: "PrivacyInfo.xcprivacy") | ||
let sideEffect = SideEffectDescriptor.file(FileDescriptor(path: privacyManifestPath, contents: data)) | ||
|
||
var resources = target.resources | ||
resources.resources.append(.init(path: privacyManifestPath)) | ||
|
||
var newTarget = target | ||
newTarget.resources = resources | ||
|
||
return (newTarget, [sideEffect]) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import Foundation | ||
|
||
public struct PrivacyManifest: Codable, Equatable { | ||
public var tracking: Bool | ||
|
||
public var trackingDomains: [String] | ||
|
||
public var collectedDataTypes: [[String: Plist.Value]] | ||
|
||
public var accessedApiTypes: [[String: Plist.Value]] | ||
|
||
public init( | ||
tracking: Bool, | ||
trackingDomains: [String], | ||
collectedDataTypes: [[String: Plist.Value]], | ||
accessedApiTypes: [[String: Plist.Value]] | ||
) { | ||
self.tracking = tracking | ||
self.trackingDomains = trackingDomains | ||
self.collectedDataTypes = collectedDataTypes | ||
self.accessedApiTypes = accessedApiTypes | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import Foundation | ||
|
||
public struct ResourceFileElements: Codable, Equatable { | ||
public var resources: [ResourceFileElement] | ||
|
||
public var privacyManifest: PrivacyManifest? | ||
|
||
public init( | ||
_ resources: [ResourceFileElement], | ||
privacyManifest: PrivacyManifest? = nil | ||
) { | ||
self.resources = resources | ||
self.privacyManifest = privacyManifest | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.