Skip to content

Commit 49253f9

Browse files
committed
2.4.0-1.11.1-2
1 parent 35cdfd0 commit 49253f9

9 files changed

Lines changed: 163 additions & 37 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## [2.4.0-1.11.1-2]
4+
5+
- Fixes `deleteRepresentablesPackage`, `createRepresentablesPackage`, and `exportToSpm` scripts to detect and remove orphaned `XCSwiftPackageProductDependency` entries (entries without a `package` pointer left behind by previous manual or tool-assisted additions). These orphaned entries caused intermittent build failures — Xcode could resolve them from DerivedData cache, but failed with "cannot find type in scope" errors after a clean build or cache reset.
6+
37
## [2.4.0-1.11.1-1]
48

59
- Adds `composeuiviewcontroller.metadataPath` to pass the absolute path passed by the Gradle plugin. This is always correct regardless of the

CHANGELOG_PLUGIN.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## [2.4.0-1.11.1-2]
4+
5+
- Compatible with 2.4.0-1.11.1-2
6+
7+
---
8+
39
## [2.4.0-1.11.1-1]
410

511
- Compatible with 2.4.0-1.11.1-1

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ KSP library and Gradle plugin for generating `ComposeUIViewController` and `UIVi
88

99
| [Version](https://plugins.gradle.org/plugin/io.github.guilhe.kmp.plugin-composeuiviewcontroller) | [Kotlin](https://github.com/JetBrains/kotlin/releases) | [KSP](https://github.com/Google/KSP/releases) | [Compose Multiplatform](https://github.com/JetBrains/compose-multiplatform/releases) | Xcode |
1010
|--------------------------------------------------------------------------------------------------|:------------------------------------------------------:|:---------------------------------------------:|:------------------------------------------------------------------------------------:|:------:|
11-
| 2.4.0-1.11.1-1 | 2.4.0 | 2.3.9 | 1.11.1 | 26.5.0 |
11+
| 2.4.0-1.11.1-2 | 2.4.0 | 2.3.9 | 1.11.1 | 26.5.0 |
1212

1313
[![Android Weekly](https://androidweekly.net/issues/issue-583/badge)](https://androidweekly.net/issues/issue-583) [![Featured in Kotlin Weekly - Issue #378](https://img.shields.io/badge/Featured_in_Kotlin_Weekly-Issue_%23378-7878b4)](https://mailchi.mp/kotlinweekly/kotlin-weekly-378) [![Featured in Kotlin Weekly - Issue #389](https://img.shields.io/badge/Featured_in_Kotlin_Weekly-Issue_%23389-7878b4)](https://mailchi.mp/kotlinweekly/kotlin-weekly-389) <a href="https://jetc.dev/issues/177.html"><img src="https://img.shields.io/badge/As_Seen_In-jetc.dev_Newsletter_Issue_%23177-blue?logo=Jetpack+Compose&amp;logoColor=white" alt="As Seen In - jetc.dev Newsletter Issue #177"></a> <a href="https://jetc.dev/issues/188.html"><img src="https://img.shields.io/badge/As_Seen_In-jetc.dev_Newsletter_Issue_%23188-blue?logo=Jetpack+Compose&amp;logoColor=white" alt="As Seen In - jetc.dev Newsletter Issue #188"></a>
1414

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ buildscript {
1515

1616
allprojects {
1717
group = "com.github.guilhe.kmp"
18-
version = "2.4.0-1.11.1-1"
18+
version = "2.4.0-1.11.1-2"
1919
}
2020

2121
dependencies {

kmp-composeuiviewcontroller-gradle-plugin/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ dependencies {
2727
testImplementation(libs.test.kotlin)
2828
}
2929

30-
version = "2.4.0-1.11.1-1"
30+
version = "2.4.0-1.11.1-2"
3131
group = "io.github.guilhe.kmp"
3232

3333
gradlePlugin {

kmp-composeuiviewcontroller-gradle-plugin/src/main/kotlin/com/github/guilhe/kmp/composeuiviewcontroller/gradle/Constants.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ let package = Package(
2222
)
2323
""".trimIndent()
2424

25-
internal const val VERSION_LIBRARY = "2.4.0-1.11.1-1"
25+
internal const val VERSION_LIBRARY = "2.4.0-1.11.1-2"
2626
internal const val LOG_TAG = "KmpComposeUIViewControllerPlugin"
2727
internal const val PLUGIN_KMP = "org.jetbrains.kotlin.multiplatform"
2828
internal const val PLUGIN_KSP = "com.google.devtools.ksp"

kmp-composeuiviewcontroller-gradle-plugin/src/main/resources/createRepresentablesPackage.sh

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -140,27 +140,60 @@ add_to_xcodeproj() {
140140
exit 1
141141
end
142142
143+
# Remove any orphaned product dependencies (no package pointer) left behind by
144+
# previous manual additions or interrupted runs, to avoid duplicate/unresolvable entries.
145+
orphaned = project.objects.select do |obj|
146+
obj.isa == "XCSwiftPackageProductDependency" &&
147+
obj.respond_to?(:product_name) &&
148+
obj.product_name == product_name &&
149+
(!obj.respond_to?(:package) || obj.package.nil?)
150+
end
151+
unless orphaned.empty?
152+
puts " > Removing #{orphaned.size} orphaned XCSwiftPackageProductDependency entry/entries for \"#{product_name}\""
153+
project.targets.each do |t|
154+
frameworks_phase = t.frameworks_build_phase
155+
next unless frameworks_phase
156+
to_remove = frameworks_phase.files.select { |bf| bf.respond_to?(:product_ref) && orphaned.include?(bf.product_ref) }
157+
to_remove.each { |bf| frameworks_phase.remove_build_file(bf) }
158+
end
159+
project.targets.each do |t|
160+
next unless t.respond_to?(:package_product_dependencies)
161+
t.package_product_dependencies.delete_if { |dep| orphaned.include?(dep) }
162+
end
163+
orphaned.each(&:remove_from_project)
164+
end
165+
143166
existing_refs = project.root_object.package_references.to_a rescue []
144-
already_added = existing_refs.any? do |ref|
167+
pkg_ref = existing_refs.find do |ref|
145168
ref.isa == "XCLocalSwiftPackageReference" &&
146169
ref.respond_to?(:relative_path) &&
147170
ref.relative_path == relative_path
148171
end
149172
150-
if already_added
151-
puts " > Package \"#{product_name}\" already added to project. Skipping"
152-
exit 0
173+
if pkg_ref
174+
# Package reference exists; ensure the target has a correctly linked product dependency.
175+
already_linked = target.package_product_dependencies.any? { |dep| dep.package == pkg_ref && dep.product_name == product_name }
176+
if already_linked
177+
puts " > Package \"#{product_name}\" already correctly linked. Skipping"
178+
project.save if !orphaned.empty?
179+
exit 0
180+
end
181+
puts " > Package reference exists but target is not linked — re-linking \"#{product_name}\""
182+
else
183+
pkg_ref = project.new(Xcodeproj::Project::Object::XCLocalSwiftPackageReference)
184+
pkg_ref.relative_path = relative_path
185+
project.root_object.package_references << pkg_ref
153186
end
154187
155-
pkg_ref = project.new(Xcodeproj::Project::Object::XCLocalSwiftPackageReference)
156-
pkg_ref.relative_path = relative_path
157-
project.root_object.package_references << pkg_ref
158-
159188
pkg_dep = project.new(Xcodeproj::Project::Object::XCSwiftPackageProductDependency)
160189
pkg_dep.package = pkg_ref
161190
pkg_dep.product_name = product_name
162191
target.package_product_dependencies << pkg_dep
163192
193+
build_file = project.new(Xcodeproj::Project::Object::PBXBuildFile)
194+
build_file.product_ref = pkg_dep
195+
target.frameworks_build_phase.add_build_file(build_file)
196+
164197
begin
165198
project.save
166199
puts " > Added \"#{product_name}\" local package to target \"#{target_name}\""

kmp-composeuiviewcontroller-gradle-plugin/src/main/resources/deleteRepresentablesPackage.sh

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,7 @@ remove_from_xcodeproj() {
7171
return 0
7272
fi
7373

74-
if ! grep -q "XCLocalSwiftPackageReference" "$project_pbxproj" 2>/dev/null || \
75-
! grep -q "\"$group_name\"" "$project_pbxproj" 2>/dev/null; then
74+
if ! grep -q "$group_name" "$project_pbxproj" 2>/dev/null; then
7675
echo " > Package \"$group_name\" not found in project. Skipping"
7776
return 0
7877
fi
@@ -94,30 +93,65 @@ remove_from_xcodeproj() {
9493
exit 1
9594
end
9695
97-
target = project.targets.find { |t| t.name == target_name }
98-
unless target
99-
puts " > ERROR: Target \"#{target_name}\" not found in project"
100-
exit 1
96+
# Collect ALL product dependencies for this product name — including orphaned ones
97+
# (entries without a package pointer left behind by previous manual/tool additions).
98+
all_deps = project.objects.select do |obj|
99+
obj.isa == "XCSwiftPackageProductDependency" &&
100+
obj.respond_to?(:product_name) &&
101+
obj.product_name == product_name
102+
end
103+
104+
if all_deps.empty?
105+
puts " > No XCSwiftPackageProductDependency entries found for \"#{product_name}\". Skipping"
106+
else
107+
puts " > Removing #{all_deps.size} XCSwiftPackageProductDependency entry/entries for \"#{product_name}\""
108+
109+
# Remove build-file entries from every target framework phase
110+
project.targets.each do |target|
111+
frameworks_phase = target.frameworks_build_phase
112+
next unless frameworks_phase
113+
to_remove = frameworks_phase.files.select do |bf|
114+
bf.respond_to?(:product_ref) && all_deps.include?(bf.product_ref)
115+
end
116+
to_remove.each do |bf|
117+
frameworks_phase.remove_build_file(bf)
118+
puts " > Removed \"#{product_name} in Frameworks\" from target \"#{target.name}\""
119+
end
120+
end
121+
122+
# Remove from each target package_product_dependencies list
123+
project.targets.each do |target|
124+
next unless target.respond_to?(:package_product_dependencies)
125+
before = target.package_product_dependencies.size
126+
target.package_product_dependencies.delete_if { |dep| all_deps.include?(dep) }
127+
removed = before - target.package_product_dependencies.size
128+
puts " > Removed #{removed} product dependency reference(s) from target \"#{target.name}\"" if removed > 0
129+
end
130+
131+
# Remove the dependency objects themselves
132+
all_deps.each(&:remove_from_project)
101133
end
102134
103-
pkg_ref = (project.root_object.package_references.to_a rescue []).find do |ref|
135+
# Remove the XCLocalSwiftPackageReference (there may be more than one if setup ran multiple times)
136+
pkg_refs = (project.root_object.package_references.to_a rescue []).select do |ref|
104137
ref.isa == "XCLocalSwiftPackageReference" &&
105138
ref.respond_to?(:relative_path) &&
106139
ref.relative_path == relative_path
107140
end
108141
109-
unless pkg_ref
110-
puts " > Package \"#{product_name}\" not found in project. Skipping"
111-
exit 0
142+
if pkg_refs.empty?
143+
puts " > No XCLocalSwiftPackageReference found for \"#{product_name}\". Skipping"
144+
else
145+
pkg_refs.each do |ref|
146+
project.root_object.package_references.delete(ref)
147+
ref.remove_from_project
148+
end
149+
puts " > Removed #{pkg_refs.size} XCLocalSwiftPackageReference entry/entries for \"#{product_name}\""
112150
end
113151
114-
target.package_product_dependencies.delete_if { |dep| dep.package == pkg_ref }
115-
project.root_object.package_references.delete(pkg_ref)
116-
pkg_ref.remove_from_project
117-
118152
begin
119153
project.save
120-
puts " > Removed \"#{product_name}\" local package from target \"#{target_name}\""
154+
puts " > Xcode project saved"
121155
rescue => e
122156
puts " > ERROR: Failed to save Xcode project: #{e.message}"
123157
exit 1
@@ -144,4 +178,4 @@ echo " > Removing package reference from Xcode project..."
144178
remove_from_xcodeproj
145179

146180
echo ""
147-
echo " > Done. Run setupRepresentablesSpmPackage to set up again."
181+
echo " > Done. Run createRepresentablesPackage to set up again."

kmp-composeuiviewcontroller-gradle-plugin/src/main/resources/exportToSpm.sh

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,24 @@ add_to_xcodeproj_if_needed() {
110110

111111
if grep -q "XCLocalSwiftPackageReference" "$project_pbxproj" 2>/dev/null && \
112112
grep -q "\"$group_name\"" "$project_pbxproj" 2>/dev/null; then
113-
return 0
113+
# Fast path: package reference exists. Still check for orphaned product dependencies
114+
# (entries without a package pointer) that cause unresolvable build errors.
115+
local orphaned_count
116+
orphaned_count=$(ruby -e '
117+
require "xcodeproj"
118+
project = Xcodeproj::Project.open(ARGV[0]) rescue exit(0)
119+
count = project.objects.count { |o|
120+
o.isa == "XCSwiftPackageProductDependency" &&
121+
o.respond_to?(:product_name) && o.product_name == ARGV[1] &&
122+
(!o.respond_to?(:package) || o.package.nil?)
123+
}
124+
puts count
125+
' "$IOS_APP_DIR/$xcodeproj_path" "$group_name" 2>/dev/null || echo "0")
126+
127+
if [ "${orphaned_count:-0}" -eq 0 ] 2>/dev/null; then
128+
return 0
129+
fi
130+
echo " > Found $orphaned_count orphaned \"$group_name\" dependency entry/entries — cleaning up..."
114131
fi
115132

116133
echo " > Adding \"$group_name\" local package to Xcode project..."
@@ -137,27 +154,59 @@ add_to_xcodeproj_if_needed() {
137154
exit 1
138155
end
139156
157+
# Remove any orphaned product dependencies (no package pointer) left behind by
158+
# previous manual additions or interrupted runs, to avoid duplicate/unresolvable entries.
159+
orphaned = project.objects.select do |obj|
160+
obj.isa == "XCSwiftPackageProductDependency" &&
161+
obj.respond_to?(:product_name) &&
162+
obj.product_name == product_name &&
163+
(!obj.respond_to?(:package) || obj.package.nil?)
164+
end
165+
unless orphaned.empty?
166+
puts " > Removing #{orphaned.size} orphaned XCSwiftPackageProductDependency entry/entries for \"#{product_name}\""
167+
project.targets.each do |t|
168+
frameworks_phase = t.frameworks_build_phase
169+
next unless frameworks_phase
170+
to_remove = frameworks_phase.files.select { |bf| bf.respond_to?(:product_ref) && orphaned.include?(bf.product_ref) }
171+
to_remove.each { |bf| frameworks_phase.remove_build_file(bf) }
172+
end
173+
project.targets.each do |t|
174+
next unless t.respond_to?(:package_product_dependencies)
175+
t.package_product_dependencies.delete_if { |dep| orphaned.include?(dep) }
176+
end
177+
orphaned.each(&:remove_from_project)
178+
end
179+
140180
existing_refs = project.root_object.package_references.to_a rescue []
141-
already_added = existing_refs.any? do |ref|
181+
pkg_ref = existing_refs.find do |ref|
142182
ref.isa == "XCLocalSwiftPackageReference" &&
143183
ref.respond_to?(:relative_path) &&
144184
ref.relative_path == relative_path
145185
end
146186
147-
if already_added
148-
puts " > Package \"#{product_name}\" already added to project. Skipping"
149-
exit 0
187+
if pkg_ref
188+
already_linked = target.package_product_dependencies.any? { |dep| dep.package == pkg_ref && dep.product_name == product_name }
189+
if already_linked
190+
puts " > Package \"#{product_name}\" already correctly linked. Skipping"
191+
project.save if !orphaned.empty?
192+
exit 0
193+
end
194+
puts " > Package reference exists but target is not linked — re-linking \"#{product_name}\""
195+
else
196+
pkg_ref = project.new(Xcodeproj::Project::Object::XCLocalSwiftPackageReference)
197+
pkg_ref.relative_path = relative_path
198+
project.root_object.package_references << pkg_ref
150199
end
151200
152-
pkg_ref = project.new(Xcodeproj::Project::Object::XCLocalSwiftPackageReference)
153-
pkg_ref.relative_path = relative_path
154-
project.root_object.package_references << pkg_ref
155-
156201
pkg_dep = project.new(Xcodeproj::Project::Object::XCSwiftPackageProductDependency)
157202
pkg_dep.package = pkg_ref
158203
pkg_dep.product_name = product_name
159204
target.package_product_dependencies << pkg_dep
160205
206+
build_file = project.new(Xcodeproj::Project::Object::PBXBuildFile)
207+
build_file.product_ref = pkg_dep
208+
target.frameworks_build_phase.add_build_file(build_file)
209+
161210
begin
162211
project.save
163212
puts " > Added \"#{product_name}\" local package to target \"#{target_name}\""

0 commit comments

Comments
 (0)