From b55f0cc2de661888cea4c474cd95573971ace471 Mon Sep 17 00:00:00 2001 From: Sergey Ospanov <33452977+TblKBA@users.noreply.github.com> Date: Sun, 23 Aug 2026 13:48:32 +0300 Subject: [PATCH 1/2] Preserve nested target attribute dictionaries --- Sources/ProjectSpec/Settings.swift | 4 ++++ .../PBXProjGeneratorTests.swift | 23 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/Sources/ProjectSpec/Settings.swift b/Sources/ProjectSpec/Settings.swift index f0a5344a..48b9d883 100644 --- a/Sources/ProjectSpec/Settings.swift +++ b/Sources/ProjectSpec/Settings.swift @@ -144,6 +144,10 @@ extension ProjectAttribute { self = .array(array) } else if let object = value as? PBXObject { self = .targetReference(object) + } else if let dictionary = value as? [String: [String: Any]] { + self = .attributeDictionary( + dictionary.mapValues { $0.mapValues { ProjectAttribute(any: $0) } } + ) } else { self = .string("\(value)") } diff --git a/Tests/XcodeGenKitTests/PBXProjGeneratorTests.swift b/Tests/XcodeGenKitTests/PBXProjGeneratorTests.swift index 80b083e1..a94404a0 100644 --- a/Tests/XcodeGenKitTests/PBXProjGeneratorTests.swift +++ b/Tests/XcodeGenKitTests/PBXProjGeneratorTests.swift @@ -471,6 +471,29 @@ class PBXProjGeneratorTests: XCTestCase { XCTAssertEqual(pbxProject.attributes[lastUpgradeKey]?.stringValue, lastUpgradeValue) } } + + func testNestedTargetAttributesAreGeneratedAsDictionaries() throws { + let target = Target( + name: "App", + type: .application, + platform: .iOS, + attributes: [ + "SystemCapabilities": [ + "com.apple.HealthKit": ["enabled": 1], + ], + ] + ) + let pbxProj = try Project(name: "Test", targets: [target]).generatePbxProj() + let pbxProject = try XCTUnwrap(pbxProj.projects.first) + let pbxTarget = try XCTUnwrap(pbxProject.targets.first) + + XCTAssertEqual( + pbxProject.targetAttributes[pbxTarget]?["SystemCapabilities"], + .attributeDictionary([ + "com.apple.HealthKit": ["enabled": .string("1")], + ]) + ) + } func testDefaultLastUpgradeCheckWhenUserDidNotSpecifyValue() throws { let lastUpgradeKey = "LastUpgradeCheck" From 44f46f9c3d28207bff4d1b6cb371810772cff273 Mon Sep 17 00:00:00 2001 From: Sergey Ospanov <33452977+TblKBA@users.noreply.github.com> Date: Sun, 23 Aug 2026 13:48:32 +0300 Subject: [PATCH 2/2] Add changelog entry for nested target attributes --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4886713..953ae53b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ ### Changed - Targets in the generated project now follow the declaration order from the source spec (Xcode sidebar, `xcodebuild -list` output). Previously they were always sorted alphabetically. Applies to both YAML and JSON specs. Declaration order is now also preserved for targets whose `platform`/`name` come from a target template and for targets whose key is a `${VARIABLE}`. #1619 @mirkokg +### Fixed +- Preserve nested target attributes such as `SystemCapabilities` as native PBX dictionaries #1643 @TblKBA + ### Internal - Update to XcodeProj 9.14.0 #1629 @philprime