From 358a8d1bcf7ac6c0ca247092ad1223e7098beb3e Mon Sep 17 00:00:00 2001 From: Jerome Lacoste Date: Thu, 12 Nov 2026 07:37:05 +0100 Subject: [PATCH] fix: write */ in a comment as (*)/, as Xcode does A group named `Group */ Name` was written as `/* Group */ Name */`, which ends the comment early: neither XcodeProj, plutil nor Xcode could open the project afterwards. --- .../XcodeProj/Objects/Project/PBXProjEncoder.swift | 2 ++ .../Objects/Project/PBXProjEncoderTests.swift | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/Sources/XcodeProj/Objects/Project/PBXProjEncoder.swift b/Sources/XcodeProj/Objects/Project/PBXProjEncoder.swift index 311bad70a..d68cc4158 100644 --- a/Sources/XcodeProj/Objects/Project/PBXProjEncoder.swift +++ b/Sources/XcodeProj/Objects/Project/PBXProjEncoder.swift @@ -303,6 +303,8 @@ final class PBXProjEncoder { } private func write(comment: String, to output: inout [String]) { + // Xcode writes */ inside a comment as (*)/, so the comment does not end early. + let comment = comment.contains("*/") ? comment.replacingOccurrences(of: "*/", with: "(*)/") : comment output.append("/* \(comment) */") } diff --git a/Tests/XcodeProjTests/Objects/Project/PBXProjEncoderTests.swift b/Tests/XcodeProjTests/Objects/Project/PBXProjEncoderTests.swift index aa7424c5e..5fba3a4f5 100644 --- a/Tests/XcodeProjTests/Objects/Project/PBXProjEncoderTests.swift +++ b/Tests/XcodeProjTests/Objects/Project/PBXProjEncoderTests.swift @@ -203,6 +203,17 @@ lines.validate(line: "23766C2C1EAA3484007A9026 /* Info.plist */,", betweenLine: iosTestsChildrenStart, andLine: iosTestsChildrenEnd) } + func test_navigator_group_name_containing_comment_end_when_iOSProject() throws { + try loadiOSProject() + let group = try XCTUnwrap(proj.groups.first { $0.name == "GroupWithoutFolder" }) + group.name = "Group */ Name" + + let encoded = encodeProject() + lines(fromFile: encoded).validate(line: "3CD1EAD92057638200DAEECB /* Group (*)/ Name */ = {") + let reread = try PBXProj(data: XCTUnwrap(encoded.data(using: .utf8))) + XCTAssertNotNil(reread.groups.first { $0.name == "Group */ Name" }) + } + func test_navigator_groups_in_filename_order_when_iOSProject() throws { try loadiOSProject()