Repository: tuist/XcodeGraph
Pull request #156: feat(PBXFileReference): support XCFramework expectedSignature property
Author: login: TamarMilchtaich
Labels: name: enhancement
description: New feature or request
name: size:M
description: This PR changes 30-99 lines, ignoring generated files.
Created: 2025-03-17T12:50:57Z | Merged: 2025-04-30T13:25:14Z

Support XCFrameworks expectedSignature property, and add the expected expectedSignature to the target dependency, to be verified against the actual signature.

More about XCFramework's siganture:
https://developer.apple.com/documentation/Xcode/verifying-the-origin-of-your-xcframeworks


Diff:
diff --git a/Package.resolved b/Package.resolved
index 08a456d1..363cdd53 100644
--- a/Package.resolved
+++ b/Package.resolved
@@ -1,5 +1,5 @@
 {
-  "originHash" : "6e9c67eb028efd58882f60208b868c006b6ab0a4c7006e49085a4c346df383b0",
+  "originHash" : "d4f68e752993519aee6ad2c355139fa210271f229b5503b0a029f07f61d7b15c",
   "pins" : [
     {
       "identity" : "aexml",
@@ -33,8 +33,8 @@
       "kind" : "remoteSourceControl",
       "location" : "https://github.com/tuist/FileSystem.git",
       "state" : {
-        "revision" : "1bff6d54e90a79706a4a9d77cd081104de430361",
-        "version" : "0.7.7"
+        "revision" : "eb33ba606c2d988a6f7247e3fc23faa681d75028",
+        "version" : "0.7.16"
       }
     },
     {
@@ -51,8 +51,8 @@
       "kind" : "remoteSourceControl",
       "location" : "https://github.com/Kolos65/Mockable.git",
       "state" : {
-        "revision" : "203336d0ccb7ff03a8a03db54a4fa18fc2b0c771",
-        "version" : "0.3.0"
+        "revision" : "68f3ed6c4b62afab27a84425494cb61421a61ac1",
+        "version" : "0.3.1"
       }
     },
     {
@@ -123,8 +123,8 @@
       "kind" : "remoteSourceControl",
       "location" : "https://github.com/apple/swift-log",
       "state" : {
-        "revision" : "96a2f8a0fa41e9e09af4585e2724c4e825410b91",
-        "version" : "1.6.2"
+        "revision" : "3d8596ed08bd13520157f0355e35caed215ffbfa",
+        "version" : "1.6.3"
       }
     },
     {
@@ -132,8 +132,8 @@
       "kind" : "remoteSourceControl",
       "location" : "https://github.com/apple/swift-nio",
       "state" : {
-        "revision" : "c51907a839e63ebf0ba2076bba73dd96436bd1b9",
-        "version" : "2.81.0"
+        "revision" : "0f54d58bb5db9e064f332e8524150de379d1e51c",
+        "version" : "2.82.1"
       }
     },
     {
diff --git a/Sources/XcodeGraph/DependenciesGraph/DependenciesGraph.swift b/Sources/XcodeGraph/DependenciesGraph/DependenciesGraph.swift
index 639bd16b..b90e332a 100644
--- a/Sources/XcodeGraph/DependenciesGraph/DependenciesGraph.swift
+++ b/Sources/XcodeGraph/DependenciesGraph/DependenciesGraph.swift
@@ -33,9 +33,14 @@ public struct DependenciesGraph: Equatable, Codable, Sendable {
             name: String = "Test",
             // swiftlint:disable:next force_try
             path: AbsolutePath = AbsolutePath.root.appending(try! RelativePath(validating: "Test.xcframework")),
+            expectedSignature: XCFrameworkSignature = .unsigned,
             status: LinkingStatus = .required
         ) -> DependenciesGraph {
-            let externalDependencies = [name: [TargetDependency.xcframework(path: path, status: status)]]
+            let externalDependencies = [name: [TargetDependency.xcframework(
+                path: path,
+                expectedSignature: expectedSignature,
+                status: status
+            )]]
 
             return .init(
                 externalDependencies: externalDependencies,
diff --git a/Sources/XcodeGraph/Graph/GraphDependency.swift b/Sources/XcodeGraph/Graph/GraphDependency.swift
index 37988426..35c514d9 100644
--- a/Sources/XcodeGraph/Graph/GraphDependency.swift
+++ b/Sources/XcodeGraph/Graph/GraphDependency.swift
@@ -10,6 +10,7 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
         public let status: LinkingStatus
         public let swiftModules: [AbsolutePath]
         public let moduleMaps: [AbsolutePath]
+        public let expectedSignature: String?
 
         public init(
             path: AbsolutePath,
@@ -19,7 +20,8 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
             status: LinkingStatus,
             macroPath _: AbsolutePath?,
             swiftModules: [AbsolutePath],
-            moduleMaps: [AbsolutePath]
+            moduleMaps: [AbsolutePath],
+            expectedSignature: String? = nil
         ) {
             self.path = path
             self.infoPlist = infoPlist
@@ -28,6 +30,7 @@ public enum GraphDependency: Hashable, CustomStringConvertible, Comparable, Coda
             self.status = status
             self.swiftModules = swiftModules
             self.moduleMaps = moduleMaps
+            self.expectedSignature = expectedSignature
         }
 
         public var description: String {
diff --git a/Sources/XcodeGraph/Models/Metadata/XCFrameworkMetadata.swift b/Sources/XcodeGraph/Models/Metadata/XCFrameworkMetadata.swift
index 72e24715..c0318077 100644
--- a/Sources/XcodeGraph/Models/Metadata/XCFrameworkMetadata.swift
+++ b/Sources/XcodeGraph/Models/Metadata/XCFrameworkMetadata.swift
@@ -13,6 +13,7 @@ public struct XCFrameworkMetadata: Equatable {
     public var swiftModules: [AbsolutePath]
     /// All `.modulemap` files present in the `.xcframework`
     public var moduleMaps: [AbsolutePath]
+    public var expectedSignature: XCFrameworkSignature?
 
     public init(
         path: AbsolutePath,
@@ -22,7 +23,8 @@ public struct XCFrameworkMetadata: Equatable {
         status: LinkingStatus,
         macroPath: AbsolutePath?,
         swiftModules: [AbsolutePath] = [],
-        moduleMaps: [AbsolutePath] = []
+        moduleMaps: [AbsolutePath] = [],
+        expectedSignature: XCFrameworkSignature? = nil
     ) {
         self.path = path
         self.infoPlist = infoPlist
@@ -32,6 +34,7 @@ public struct XCFrameworkMetadata: Equatable {
         self.macroPath = macroPath
         self.swiftModules = swiftModules
         self.moduleMaps = moduleMaps
+        self.expectedSignature = expectedSignature
     }
 }
 
@@ -46,7 +49,8 @@ public struct XCFrameworkMetadata: Equatable {
             status: LinkingStatus = .required,
             macroPath: AbsolutePath? = nil,
             swiftModules: [AbsolutePath] = [],
-            moduleMaps: [AbsolutePath] = []
+            moduleMaps: [AbsolutePath] = [],
+            expectedSignature: XCFrameworkSignature? = nil
         ) -> XCFrameworkMetadata {
             XCFrameworkMetadata(
                 path: path,
@@ -56,7 +60,8 @@ public struct XCFrameworkMetadata: Equatable {
                 status: status,
                 macroPath: macroPath,
                 swiftModules: swiftModules,
-                moduleMaps: moduleMaps
+                moduleMaps: moduleMaps,
+                expectedSignature: expectedSignature
             )
         }
     }
diff --git a/Sources/XcodeGraph/Models/TargetDependency.swift b/Sources/XcodeGraph/Models/TargetDependency.swift
index 1c60d0bb..54ddb418 100644
--- a/Sources/XcodeGraph/Models/TargetDependency.swift
+++ b/Sources/XcodeGraph/Models/TargetDependency.swift
@@ -7,6 +7,23 @@ public enum LinkingStatus: String, Hashable, Codable, Sendable {
     case none
 }
 
+public enum XCFrameworkSignature: Equatable, Hashable, Codable, Sendable {
+    case unsigned
+    case signedWithAppleCertificate(teamIdentifier: String, teamName: String)
+    case selfSigned(fingerprint: String)
+
+    public func signatureString() -> String? {
+        switch self {
+        case .unsigned:
+            return nil
+        case let .selfSigned(fingerprint: fingerprint):
+            return "SelfSigned:\(fingerprint)"
+        case let .signedWithAppleCertificate(teamIdentifier: teamIdentifier, teamName: teamName):
+            return "AppleDeveloperProgram:\(teamIdentifier):\(teamName)"
+        }
+    }
+}
+
 public enum TargetDependency: Equatable, Hashable, Codable, Sendable {
     public enum PackageType: String, Equatable, Hashable, Codable, Sendable {
         case runtime
@@ -18,7 +35,12 @@ public enum TargetDependency: Equatable, Hashable, Codable, Sendable {
     case target(name: String, status: LinkingStatus = .required, condition: PlatformCondition? = nil)
     case project(target: String, path: AbsolutePath, status: LinkingStatus = .required, condition: PlatformCondition? = nil)
     case framework(path: AbsolutePath, status: LinkingStatus, condition: PlatformCondition? = nil)
-    case xcframework(path: AbsolutePath, status: LinkingStatus, condition: PlatformCondition? = nil)
+    case xcframework(
+        path: AbsolutePath,
+        expectedSignature: XCFrameworkSignature?,
+        status: LinkingStatus,
+        condition: PlatformCondition? = nil
+    )
     case library(
         path: AbsolutePath,
         publicHeaders: AbsolutePath,
@@ -37,7 +59,7 @@ public enum TargetDependency: Equatable, Hashable, Codable, Sendable {
             condition
         case .framework(path: _, status: _, condition: let condition):
             condition
-        case .xcframework(path: _, status: _, condition: let condition):
+        case .xcframework(path: _, expectedSignature: _, status: _, condition: let condition):
             condition
         case .library(path: _, publicHeaders: _, swiftModuleMap: _, condition: let condition):
             condition
@@ -57,8 +79,8 @@ public enum TargetDependency: Equatable, Hashable, Codable, Sendable {
             return .project(target: target, path: path, status: status, condition: condition)
         case .framework(path: let path, status: let status, condition: _):
             return .framework(path: path, status: status, condition: condition)
-        case .xcframework(path: let path, status: let status, condition: _):
-            return .xcframework(path: path, status: status, condition: condition)
+        case .xcframework(path: let path, expectedSignature: let expectedSignature, status: let status, condition: _):
+            return .xcframework(path: path, expectedSignature: expectedSignature, status: status, condition: condition)
         case .library(path: let path, publicHeaders: let headers, swiftModuleMap: let moduleMap, condition: _):
             return .library(path: path, publicHeaders: headers, swiftModuleMap: moduleMap, condition: condition)
         case .package(product: let product, type: let type, condition: _):
diff --git a/Sources/XcodeGraphMapper/Extensions/TargetDependency+Extensions.swift b/Sources/XcodeGraphMapper/Extensions/TargetDependency+Extensions.swift
index c92c1fce..6642bac8 100644
--- a/Sources/XcodeGraphMapper/Extensions/TargetDependency+Extensions.swift
+++ b/Sources/XcodeGraphMapper/Extensions/TargetDependency+Extensions.swift
@@ -14,7 +14,7 @@ extension TargetDependency {
             return product
         case let .framework(path, _, _):
             return path.basenameWithoutExt
-        case let .xcframework(path, _, _):
+        case let .xcframework(path, _, _, _):
             return path.basenameWithoutExt
         case let .library(path, _, _, _):
             return path.basenameWithoutExt
diff --git a/Sources/XcodeGraphMapper/Mappers/Phases/PBXFrameworksBuildPhaseMapper.swift b/Sources/XcodeGraphMapper/Mappers/Phases/PBXFrameworksBuildPhaseMapper.swift
index 0317421f..27d1211f 100644
--- a/Sources/XcodeGraphMapper/Mappers/Phases/PBXFrameworksBuildPhaseMapper.swift
+++ b/Sources/XcodeGraphMapper/Mappers/Phases/PBXFrameworksBuildPhaseMapper.swift
@@ -97,7 +97,7 @@ struct PBXFrameworksBuildPhaseMapper: PBXFrameworksBuildPhaseMapping {
             .throwing(PBXFrameworksBuildPhaseMappingError.missingFilePath(name: fileRef.name))
 
         let absolutePath = try AbsolutePath(validating: filePathString)
-        return try pathMapper.map(path: absolutePath, condition: nil)
+        return try pathMapper.map(path: absolutePath, expectedSignature: nil, condition: nil)
     }
 }
 
diff --git a/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetDependencyMapper.swift b/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetDependencyMapper.swift
index f06e4eb1..dc90edb5 100644
--- a/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetDependencyMapper.swift
+++ b/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetDependencyMapper.swift
@@ -108,12 +108,14 @@ struct PBXTargetDependencyMapper: PBXTargetDependencyMapping {
             if let fileRef = object as? PBXFileReference {
                 return try mapFileDependency(
                     pathString: fileRef.path,
+                    expectedSignature: nil,
                     condition: condition,
                     xcodeProj: xcodeProj
                 )
             } else if let refProxy = object as? PBXReferenceProxy {
                 return try mapFileDependency(
                     pathString: refProxy.path,
+                    expectedSignature: nil,
                     condition: condition,
                     xcodeProj: xcodeProj
                 )
@@ -131,12 +133,14 @@ struct PBXTargetDependencyMapper: PBXTargetDependencyMapping {
     /// Maps file-based dependencies (e.g., frameworks, libraries) into `TargetDependency` models.
     /// - Parameters:
     ///   - pathString: The path string for the file-based dependency (relative or absolute).
+    ///   - expectedSignature: The expected signature if `path` is of a signed XCFramework, `nil` otherwise.
     ///   - condition: An optional platform condition.
     ///   - xcodeProj: The Xcode project reference for resolving the directory structure.
     /// - Returns: A `TargetDependency` reflecting the file’s extension (framework, library, etc.).
     /// - Throws: If the path is missing or invalid.
     private func mapFileDependency(
         pathString: String?,
+        expectedSignature: XCFrameworkSignature?,
         condition: PlatformCondition?,
         xcodeProj: XcodeProj
     ) throws -> TargetDependency {
@@ -144,7 +148,7 @@ struct PBXTargetDependencyMapper: PBXTargetDependencyMapping {
             TargetDependencyMappingError.missingFileReference("Path string is nil in file dependency.")
         )
         let path = xcodeProj.srcPath.appending(try RelativePath(validating: pathString))
-        return try pathMapper.map(path: path, condition: condition)
+        return try pathMapper.map(path: path, expectedSignature: expectedSignature, condition: condition)
     }
 }
 
diff --git a/Sources/XcodeGraphMapper/Mappers/Targets/TargetDependency+GraphMapping.swift b/Sources/XcodeGraphMapper/Mappers/Targets/TargetDependency+GraphMapping.swift
index 46434e64..adede7ef 100644
--- a/Sources/XcodeGraphMapper/Mappers/Targets/TargetDependency+GraphMapping.swift
+++ b/Sources/XcodeGraphMapper/Mappers/Targets/TargetDependency+GraphMapping.swift
@@ -51,8 +51,12 @@ extension TargetDependency {
                 status: status
             )
 
-        case let .xcframework(path, status, _):
-            let metadata = try await xcframeworkMetadataProvider.loadMetadata(at: path, status: status)
+        case let .xcframework(path, expectedSignature, status, _):
+            let metadata = try await xcframeworkMetadataProvider.loadMetadata(
+                at: path,
+                expectedSignature: expectedSignature,
+                status: status
+            )
             return .xcframework(
                 .init(
                     path: path,
@@ -62,7 +66,8 @@ extension TargetDependency {
                     status: status,
                     macroPath: metadata.macroPath,
                     swiftModules: metadata.swiftModules,
-                    moduleMaps: metadata.moduleMaps
+                    moduleMaps: metadata.moduleMaps,
+                    expectedSignature: metadata.expectedSignature?.signatureString()
                 )
             )
 
diff --git a/Sources/XcodeGraphMapper/Utilities/PathDependencyMapper.swift b/Sources/XcodeGraphMapper/Utilities/PathDependencyMapper.swift
index 417c1f6d..b8e9f758 100644
--- a/Sources/XcodeGraphMapper/Utilities/PathDependencyMapper.swift
+++ b/Sources/XcodeGraphMapper/Utilities/PathDependencyMapper.swift
@@ -8,22 +8,33 @@ protocol PathDependencyMapping {
     ///
     /// - Parameters:
     ///   - path: The file path to map.
+    ///   - expectedSignature: The expected signature if `path` is of a signed XCFramework, `nil` otherwise.
     ///   - condition: An optional platform condition (e.g., iOS only).
     /// - Returns: The corresponding `TargetDependency`, if the path extension is recognized.
     /// - Throws: `PathDependencyError.invalidExtension` if the file extension is not supported.
-    func map(path: AbsolutePath, condition: PlatformCondition?) throws -> TargetDependency
+    func map(path: AbsolutePath, expectedSignature: XCFrameworkSignature?, condition: PlatformCondition?) throws
+        -> TargetDependency
 }
 
 /// A mapper that converts file paths (like `.framework`, `.xcframework`, or libraries) to `TargetDependency` models.
 struct PathDependencyMapper: PathDependencyMapping {
-    func map(path: AbsolutePath, condition: PlatformCondition?) throws -> TargetDependency {
+    func map(
+        path: AbsolutePath,
+        expectedSignature: XCFrameworkSignature? = nil,
+        condition: PlatformCondition?
+    ) throws -> TargetDependency {
         let status: LinkingStatus = .required
 
         switch path.fileExtension {
         case .framework:
             return .framework(path: path, status: status, condition: condition)
         case .xcframework:
-            return .xcframework(path: path, status: status, condition: condition)
+            return .xcframework(
+                path: path,
+                expectedSignature: expectedSignature ?? .unsigned,
+                status: status,
+                condition: condition
+            )
         case .dynamicLibrary, .textBasedDynamicLibrary, .staticLibrary:
             return .library(
                 path: path,
diff --git a/Sources/XcodeMetadata/Providers/XCFrameworkMetadataProvider.swift b/Sources/XcodeMetadata/Providers/XCFrameworkMetadataProvider.swift
index 8cc8149e..da38de01 100644
--- a/Sources/XcodeMetadata/Providers/XCFrameworkMetadataProvider.swift
+++ b/Sources/XcodeMetadata/Providers/XCFrameworkMetadataProvider.swift
@@ -49,9 +49,9 @@ public protocol XCFrameworkMetadataProviding: PrecompiledMetadataProviding {
     /// - Parameter binaryPath: Path to the binary.
     func uuids(binaryPath: AbsolutePath) throws -> Set<UUID>
 
-    /// Loads all the metadata associated with an XCFramework at the specified path
+    /// Loads all the metadata associated with an XCFramework at the specified path, and expected signature if signed
     /// - Note: This performs various shell calls and disk operations
-    func loadMetadata(at path: AbsolutePath, status: LinkingStatus) async throws
+    func loadMetadata(at path: AbsolutePath, expectedSignature: XCFrameworkSignature?, status: LinkingStatus) async throws
         -> XCFrameworkMetadata
 
     /// Returns the info.plist of the xcframework at the given path.
@@ -75,6 +75,7 @@ public final class XCFrameworkMetadataProvider: PrecompiledMetadataProvider,
 
     public func loadMetadata(
         at path: AbsolutePath,
+        expectedSignature: XCFrameworkSignature?,
         status: LinkingStatus
     ) async throws -> XCFrameworkMetadata {
         guard try await fileSystem.exists(path) else {
@@ -93,7 +94,8 @@ public final class XCFrameworkMetadataProvider: PrecompiledMetadataProvider,
             status: status,
             macroPath: try await macroPath(xcframeworkPath: path),
             swiftModules: try await fileSystem.glob(directory: path, include: ["**/*.swiftmodule"]).collect().sorted(),
-            moduleMaps: try await fileSystem.glob(directory: path, include: ["**/*.modulemap"]).collect().sorted()
+            moduleMaps: try await fileSystem.glob(directory: path, include: ["**/*.modulemap"]).collect().sorted(),
+            expectedSignature: expectedSignature
         )
     }
 
diff --git a/Tests/XcodeGraphMapperTests/MapperTests/Target/TargetDependencyExtensionsTests.swift b/Tests/XcodeGraphMapperTests/MapperTests/Target/TargetDependencyExtensionsTests.swift
index 636ee8a8..9f1336bc 100644
--- a/Tests/XcodeGraphMapperTests/MapperTests/Target/TargetDependencyExtensionsTests.swift
+++ b/Tests/XcodeGraphMapperTests/MapperTests/Target/TargetDependencyExtensionsTests.swift
@@ -83,7 +83,12 @@ struct TargetDependencyExtensionsTests {
     func testTargetGraphDependency_XCFramework() async throws {
         // Given
         let xcframeworkPath = sourceDirectory.appending(component: "MyFramework.xcframework")
-        let dependency = TargetDependency.xcframework(path: xcframeworkPath, status: .required, condition: nil)
+        let dependency = TargetDependency.xcframework(
+            path: xcframeworkPath,
+            expectedSignature: nil,
+            status: .required,
+            condition: nil
+        )
 
         // When
         let graphDep = try await dependency.graphDependency(
diff --git a/Tests/XcodeGraphTests/Models/TargetDependencyTests.swift b/Tests/XcodeGraphTests/Models/TargetDependencyTests.swift
index 0cf1d5d9..e3a070a7 100644
--- a/Tests/XcodeGraphTests/Models/TargetDependencyTests.swift
+++ b/Tests/XcodeGraphTests/Models/TargetDependencyTests.swift
@@ -50,7 +50,12 @@ final class TargetDependencyTests: XCTestCase {
             .sdk(name: "", status: .required, condition: expected),
             .package(product: "", type: .plugin, condition: expected),
             .target(name: "", condition: expected),
-            .xcframework(path: try! AbsolutePath(validating: "/"), status: .required, condition: expected),
+            .xcframework(
+                path: try! AbsolutePath(validating: "/"),
+                expectedSignature: nil,
+                status: .required,
+                condition: expected
+            ),
             .project(target: "", path: try! AbsolutePath(validating: "/"), condition: expected),
         ]
 
diff --git a/Tests/XcodeMetadataTests/XCFrameworkMetadataProviderTests.swift b/Tests/XcodeMetadataTests/XCFrameworkMetadataProviderTests.swift
index 37977b63..15390de0 100644
--- a/Tests/XcodeMetadataTests/XCFrameworkMetadataProviderTests.swift
+++ b/Tests/XcodeMetadataTests/XCFrameworkMetadataProviderTests.swift
@@ -97,7 +97,7 @@ struct XCFrameworkMetadataProviderTests {
         )
 
         // When
-        let metadata = try await subject.loadMetadata(at: frameworkPath, status: .required)
+        let metadata = try await subject.loadMetadata(at: frameworkPath, expectedSignature: nil, status: .required)
 
         // Then
         let expectedInfoPlist = XCFrameworkInfoPlist(libraries: [
@@ -161,7 +161,7 @@ struct XCFrameworkMetadataProviderTests {
         )
 
         // When
-        let metadata = try await subject.loadMetadata(at: frameworkPath, status: .required)
+        let metadata = try await subject.loadMetadata(at: frameworkPath, expectedSignature: nil, status: .required)
 
         // Then
         let expectedInfoPlist = XCFrameworkInfoPlist(libraries: [
@@ -243,7 +243,7 @@ struct XCFrameworkMetadataProviderTests {
         )
 
         // When
-        let metadata = try await subject.loadMetadata(at: frameworkPath, status: .required)
+        let metadata = try await subject.loadMetadata(at: frameworkPath, expectedSignature: nil, status: .required)
 
         // Then
         #expect(metadata.infoPlist.libraries.count == 2, "Libraries count mismatch")


Reviews:
user: login: TamarMilchtaich
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T12:52:14Z
user: login: copilot-pull-request-reviewer[bot]
body: 

Copilot reviewed 10 out of 10 changed files in this pull request and generated no comments.




state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T11:07:24Z
user: login: pepicrft
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T11:09:01Z
user: login: pepicrft
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T11:14:30Z
user: login: TamarMilchtaich
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T11:58:58Z

Review comments:
diff_hunk: @@ -8,22 +8,24 @@ protocol PathDependencyMapping {
     ///
     /// - Parameters:
     ///   - path: The file path to map.
+    ///   - originalSignature: The expected signature if `path` is of a signed XCFramework, `nil` otherwise.
     ///   - condition: An optional platform condition (e.g., iOS only).
     /// - Returns: The corresponding `TargetDependency`, if the path extension is recognized.
     /// - Throws: `PathDependencyError.invalidExtension` if the file extension is not supported.
-    func map(path: AbsolutePath, condition: PlatformCondition?) throws -> TargetDependency
+  func map(path: AbsolutePath, originalSignature:XCFrameworkOriginalSignatureType?, condition: PlatformCondition?) throws -> TargetDependency
 }
 
 /// A mapper that converts file paths (like `.framework`, `.xcframework`, or libraries) to `TargetDependency` models.
 struct PathDependencyMapper: PathDependencyMapping {
-    func map(path: AbsolutePath, condition: PlatformCondition?) throws -> TargetDependency {
+  func map(path: AbsolutePath, originalSignature: XCFrameworkOriginalSignatureType? = nil, condition: PlatformCondition?) throws -> TargetDependency {
         let status: LinkingStatus = .required
 
         switch path.fileExtension {
         case .framework:
             return .framework(path: path, status: status, condition: condition)
         case .xcframework:
-            return .xcframework(path: path, status: status, condition: condition)
+          // TODO: throw some error? log warning? this is very general, but from what I can tell it's only called from a non-xcframework contexts
path: Sources/XcodeGraphMapper/Utilities/PathDependencyMapper.swift
user: login: TamarMilchtaich
body: WDYT? Not sure how to treat this.
created_at: 2025-03-17T12:52:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -13,6 +13,7 @@ public struct XCFrameworkMetadata: Equatable {
     public var swiftModules: [AbsolutePath]
     /// All `.modulemap` files present in the `.xcframework`
     public var moduleMaps: [AbsolutePath]
+    public var expectedSignature: String?
path: Sources/XcodeGraph/Models/Metadata/XCFrameworkMetadata.swift
user: login: pepicrft
body: Why isn't this type?

```suggestion
    public var expectedSignature: XCFrameworkSignature?
```
created_at: 2025-04-07T11:09:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,22 +8,24 @@ protocol PathDependencyMapping {
     ///
     /// - Parameters:
     ///   - path: The file path to map.
+    ///   - originalSignature: The expected signature if `path` is of a signed XCFramework, `nil` otherwise.
     ///   - condition: An optional platform condition (e.g., iOS only).
     /// - Returns: The corresponding `TargetDependency`, if the path extension is recognized.
     /// - Throws: `PathDependencyError.invalidExtension` if the file extension is not supported.
-    func map(path: AbsolutePath, condition: PlatformCondition?) throws -> TargetDependency
+  func map(path: AbsolutePath, originalSignature:XCFrameworkOriginalSignatureType?, condition: PlatformCondition?) throws -> TargetDependency
 }
 
 /// A mapper that converts file paths (like `.framework`, `.xcframework`, or libraries) to `TargetDependency` models.
 struct PathDependencyMapper: PathDependencyMapping {
-    func map(path: AbsolutePath, condition: PlatformCondition?) throws -> TargetDependency {
+  func map(path: AbsolutePath, originalSignature: XCFrameworkOriginalSignatureType? = nil, condition: PlatformCondition?) throws -> TargetDependency {
         let status: LinkingStatus = .required
 
         switch path.fileExtension {
         case .framework:
             return .framework(path: path, status: status, condition: condition)
         case .xcframework:
-            return .xcframework(path: path, status: status, condition: condition)
+          // TODO: throw some error? log warning? this is very general, but from what I can tell it's only called from a non-xcframework contexts
path: Sources/XcodeGraphMapper/Utilities/PathDependencyMapper.swift
user: login: pepicrft
body: Since this utility is being used from [here](https://github.com/tuist/XcodeGraph/blob/7e9c1e69ac47e84e515cb2f1d468511b8ec27b3b/Sources/XcodeGraphMapper/Mappers/Phases/PBXFrameworksBuildPhaseMapper.swift#L38) where we can access the file reference through the build file, wouldn't we have the information that we need to set the right value here?
created_at: 2025-04-07T11:14:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,22 +8,24 @@ protocol PathDependencyMapping {
     ///
     /// - Parameters:
     ///   - path: The file path to map.
+    ///   - originalSignature: The expected signature if `path` is of a signed XCFramework, `nil` otherwise.
     ///   - condition: An optional platform condition (e.g., iOS only).
     /// - Returns: The corresponding `TargetDependency`, if the path extension is recognized.
     /// - Throws: `PathDependencyError.invalidExtension` if the file extension is not supported.
-    func map(path: AbsolutePath, condition: PlatformCondition?) throws -> TargetDependency
+  func map(path: AbsolutePath, originalSignature:XCFrameworkOriginalSignatureType?, condition: PlatformCondition?) throws -> TargetDependency
 }
 
 /// A mapper that converts file paths (like `.framework`, `.xcframework`, or libraries) to `TargetDependency` models.
 struct PathDependencyMapper: PathDependencyMapping {
-    func map(path: AbsolutePath, condition: PlatformCondition?) throws -> TargetDependency {
+  func map(path: AbsolutePath, originalSignature: XCFrameworkOriginalSignatureType? = nil, condition: PlatformCondition?) throws -> TargetDependency {
         let status: LinkingStatus = .required
 
         switch path.fileExtension {
         case .framework:
             return .framework(path: path, status: status, condition: condition)
         case .xcframework:
-            return .xcframework(path: path, status: status, condition: condition)
+          // TODO: throw some error? log warning? this is very general, but from what I can tell it's only called from a non-xcframework contexts
path: Sources/XcodeGraphMapper/Utilities/PathDependencyMapper.swift
user: login: TamarMilchtaich
body: Actually this seems OK now, there were some changes here following the review on tuist/tuist, and it makes sense now

From what I can tell, the path dependency mapper is used [here](https://github.com/tuist/XcodeGraph/blob/c367a1d1a47313cccf875e31278da43a65889a3e/Sources/XcodeGraphMapper/Mappers/Phases/PBXFrameworksBuildPhaseMapper.swift#L100) (frameworks, no signature is required) and [here](https://github.com/tuist/XcodeGraph/blob/c367a1d1a47313cccf875e31278da43a65889a3e/Sources/XcodeGraphMapper/Mappers/Targets/PBXTargetDependencyMapper.swift#L150) (signature propagated, as expected)

So probably just need to remove the TODO
created_at: 2025-04-07T11:58:58Z
author_association: CONTRIBUTOR

PR comments:
user: login: TamarMilchtaich
created_at: 2025-04-07T12:43:02Z
body: Fixed & squashed the fixes into one commit
author_association: CONTRIBUTOR
user: login: pepicrft
created_at: 2025-04-07T13:15:51Z
body: > Fixed & squashed the fixes into one commit

Let's then wait until `tuist/tuist` is ready.
author_association: CONTRIBUTOR
user: login: TamarMilchtaich
created_at: 2025-04-09T08:07:25Z
body: Pushed the following change (and only this):
`notSigned` -> `unsigned`
author_association: CONTRIBUTOR
user: login: TamarMilchtaich
created_at: 2025-04-23T08:08:34Z
body: Pushed the following change (and only this):
`signedByApple` -> `signedWithAppleCertificate`
author_association: CONTRIBUTOR